From 6e32c62f8dac5ccc97dd0a6067965ba2689f3c86 Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Fri, 3 Mar 2023 21:41:45 -0500 Subject: Assign IDs from Telegram to XMPP messages --- telegram/handlers.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'telegram/handlers.go') diff --git a/telegram/handlers.go b/telegram/handlers.go index 126d858..517fb54 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -242,7 +242,7 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { textContent.Text.Entities, markupFunction, )) - gateway.SendMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, c.xmpp) + gateway.SendMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, "e" + strconv.FormatInt(update.MessageId, 10), c.xmpp) } } @@ -256,7 +256,7 @@ func (c *Client) updateDeleteMessages(update *client.UpdateDeleteMessages) { deleteChar = "✗ " } text := deleteChar + strings.Join(int64SliceToStringSlice(update.MessageIds), ",") - gateway.SendMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, c.xmpp) + gateway.SendMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, "", c.xmpp) } } -- cgit v1.2.3 From 4a5b83dff5c568871d5624202e2ee27e5d0de242 Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Sun, 5 Mar 2023 03:00:53 -0500 Subject: Show XEP-0461 replies from Telegram --- telegram/commands.go | 2 + telegram/connect.go | 8 ++-- telegram/handlers.go | 4 +- telegram/utils.go | 99 +++++++++++++++++++++++++------------------ telegram/utils_test.go | 62 ++++++++++++++++++++++++--- xmpp/extensions/extensions.go | 18 ++++++++ xmpp/gateway/gateway.go | 31 +++++++++++--- xmpp/handlers.go | 2 +- 8 files changed, 168 insertions(+), 58 deletions(-) (limited to 'telegram/handlers.go') diff --git a/telegram/commands.go b/telegram/commands.go index 957c335..943d071 100644 --- a/telegram/commands.go +++ b/telegram/commands.go @@ -184,6 +184,7 @@ func (c *Client) unsubscribe(chatID int64) error { func (c *Client) sendMessagesReverse(chatID int64, messages []*client.Message) { for i := len(messages) - 1; i >= 0; i-- { message := messages[i] + reply, _ := c.getMessageReply(message) gateway.SendMessage( c.jid, @@ -191,6 +192,7 @@ func (c *Client) sendMessagesReverse(chatID int64, messages []*client.Message) { c.formatMessage(0, 0, false, message), strconv.FormatInt(message.Id, 10), c.xmpp, + reply, ) } } diff --git a/telegram/connect.go b/telegram/connect.go index b4957b1..2633980 100644 --- a/telegram/connect.go +++ b/telegram/connect.go @@ -219,20 +219,20 @@ func (c *Client) interactor() { if c.Session.Login != "" { c.authorizer.PhoneNumber <- c.Session.Login } else { - gateway.SendMessage(c.jid, "", "Please, enter your Telegram login via /login 12345", "", c.xmpp) + gateway.SendServiceMessage(c.jid, "Please, enter your Telegram login via /login 12345", c.xmpp) } // stage 1: wait for auth code case client.TypeAuthorizationStateWaitCode: log.Warn("Waiting for authorization code...") - gateway.SendMessage(c.jid, "", "Please, enter authorization code via /code 12345", "", c.xmpp) + gateway.SendServiceMessage(c.jid, "Please, enter authorization code via /code 12345", c.xmpp) // stage 1b: wait for registration case client.TypeAuthorizationStateWaitRegistration: log.Warn("Waiting for full name...") - gateway.SendMessage(c.jid, "", "This number is not registered yet! Please, enter your name via /setname John Doe", "", c.xmpp) + gateway.SendServiceMessage(c.jid, "This number is not registered yet! Please, enter your name via /setname John Doe", c.xmpp) // stage 2: wait for 2fa case client.TypeAuthorizationStateWaitPassword: log.Warn("Waiting for 2FA password...") - gateway.SendMessage(c.jid, "", "Please, enter 2FA passphrase via /password 12345", "", c.xmpp) + gateway.SendServiceMessage(c.jid, "Please, enter 2FA passphrase via /password 12345", c.xmpp) } } } diff --git a/telegram/handlers.go b/telegram/handlers.go index 517fb54..307562a 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -242,7 +242,7 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { textContent.Text.Entities, markupFunction, )) - gateway.SendMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, "e" + strconv.FormatInt(update.MessageId, 10), c.xmpp) + gateway.SendMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, "e" + strconv.FormatInt(update.MessageId, 10), c.xmpp, nil) } } @@ -256,7 +256,7 @@ func (c *Client) updateDeleteMessages(update *client.UpdateDeleteMessages) { deleteChar = "✗ " } text := deleteChar + strings.Join(int64SliceToStringSlice(update.MessageIds), ",") - gateway.SendMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, "", c.xmpp) + gateway.SendTextMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, c.xmpp) } } diff --git a/telegram/utils.go b/telegram/utils.go index e58f6bf..f06abd7 100644 --- a/telegram/utils.go +++ b/telegram/utils.go @@ -261,6 +261,46 @@ func (c *Client) formatContact(chatID int64) string { return str } +func (c *Client) getSenderId(message *client.Message) (senderId int64) { + if message.SenderId != nil { + switch message.SenderId.MessageSenderType() { + case client.TypeMessageSenderUser: + senderUser, _ := message.SenderId.(*client.MessageSenderUser) + senderId = senderUser.UserId + case client.TypeMessageSenderChat: + senderChat, _ := message.SenderId.(*client.MessageSenderChat) + senderId = senderChat.ChatId + } + } + + return +} + +func (c *Client) formatSender(message *client.Message) string { + return c.formatContact(c.getSenderId(message)) +} + +func (c *Client) getMessageReply(message *client.Message) (reply *gateway.Reply, replyMsg *client.Message) { + if message.ReplyToMessageId != 0 { + var err error + replyMsg, err = c.client.GetMessage(&client.GetMessageRequest{ + ChatId: message.ChatId, + MessageId: message.ReplyToMessageId, + }) + if err != nil { + log.Errorf("", err.Error()) + return + } + + reply = &gateway.Reply { + Author: fmt.Sprintf("%v@%s", c.getSenderId(replyMsg), gateway.Jid.Full()), + Id: strconv.FormatInt(message.ReplyToMessageId, 10), + } + } + + return +} + func (c *Client) formatMessage(chatID int64, messageID int64, preview bool, message *client.Message) string { var err error if message == nil { @@ -279,18 +319,7 @@ func (c *Client) formatMessage(chatID int64, messageID int64, preview bool, mess var str strings.Builder // add messageid and sender - var senderId int64 - if message.SenderId != nil { - switch message.SenderId.MessageSenderType() { - case client.TypeMessageSenderUser: - senderUser, _ := message.SenderId.(*client.MessageSenderUser) - senderId = senderUser.UserId - case client.TypeMessageSenderChat: - senderChat, _ := message.SenderId.(*client.MessageSenderChat) - senderId = senderChat.ChatId - } - } - str.WriteString(fmt.Sprintf("%v | %s | ", message.Id, c.formatContact(senderId))) + str.WriteString(fmt.Sprintf("%v | %s | ", message.Id, c.formatSender(message))) // add date if !preview { str.WriteString( @@ -681,7 +710,7 @@ func (c *Client) contentToFile(content client.MessageContent) (*client.File, *cl return nil, nil } -func (c *Client) messageToPrefix(message *client.Message, previewString string, fileString string) string { +func (c *Client) messageToPrefix(message *client.Message, previewString string, fileString string, replyMsg *client.Message) string { prefix := []string{} // message direction var directionChar string @@ -700,21 +729,12 @@ func (c *Client) messageToPrefix(message *client.Message, previewString string, } prefix = append(prefix, directionChar+strconv.FormatInt(message.Id, 10)) // show sender in group chats - if message.ChatId < 0 && message.SenderId != nil { - var senderId int64 - switch message.SenderId.MessageSenderType() { - case client.TypeMessageSenderUser: - senderUser, _ := message.SenderId.(*client.MessageSenderUser) - senderId = senderUser.UserId - case client.TypeMessageSenderChat: - senderChat, _ := message.SenderId.(*client.MessageSenderChat) - senderId = senderChat.ChatId - } - prefix = append(prefix, c.formatContact(senderId)) + if message.ChatId < 0 { + prefix = append(prefix, c.formatSender(message)) } // reply to if message.ReplyToMessageId != 0 { - prefix = append(prefix, "reply: "+c.formatMessage(message.ChatId, message.ReplyToMessageId, true, nil)) + prefix = append(prefix, "reply: "+c.formatMessage(message.ChatId, message.ReplyToMessageId, true, replyMsg)) } if message.ForwardInfo != nil { prefix = append(prefix, "fwd: "+c.formatForward(message.ForwardInfo)) @@ -750,6 +770,9 @@ func (c *Client) ensureDownloadFile(file *client.File) *client.File { // ProcessIncomingMessage transfers a message to XMPP side and marks it as read on Telegram side func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) { var text, oob, auxText string + + reply, replyMsg := c.getMessageReply(message) + content := message.Content if content != nil && content.MessageContentType() == client.TypeMessageChatChangePhoto { chat, err := c.client.GetChat(&client.GetChatRequest{ @@ -783,7 +806,7 @@ func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) { text = oob } else if !c.Session.RawMessages { var prefix strings.Builder - prefix.WriteString(c.messageToPrefix(message, previewName, fileName)) + prefix.WriteString(c.messageToPrefix(message, previewName, fileName, replyMsg)) if text != "" { // \n if it is groupchat and message is not empty if chatId < 0 { @@ -808,9 +831,9 @@ func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) { // forward message to XMPP sId := strconv.FormatInt(message.Id, 10) sChatId := strconv.FormatInt(chatId, 10) - gateway.SendMessageWithOOB(c.jid, sChatId, text, sId, c.xmpp, oob) + gateway.SendMessageWithOOB(c.jid, sChatId, text, sId, c.xmpp, reply, oob) if auxText != "" { - gateway.SendMessage(c.jid, sChatId, auxText, sId, c.xmpp) + gateway.SendMessage(c.jid, sChatId, auxText, sId, c.xmpp, reply) } } @@ -825,7 +848,7 @@ func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid str // try to execute commands response, isCommand := c.ProcessChatCommand(chatID, text) if response != "" { - gateway.SendMessage(returnJid, strconv.FormatInt(chatID, 10), response, "", c.xmpp) + gateway.SendTextMessage(returnJid, strconv.FormatInt(chatID, 10), response, c.xmpp) } // do not send on success if isCommand { @@ -847,11 +870,10 @@ func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid str if chatID != 0 && c.content.Upload != "" && strings.HasPrefix(text, c.content.Upload) { response, err := http.Get(text) if err != nil { - gateway.SendMessage( + gateway.SendTextMessage( returnJid, strconv.FormatInt(chatID, 10), fmt.Sprintf("Failed to fetch the uploaded file: %s", err.Error()), - "", c.xmpp, ) return nil @@ -860,11 +882,10 @@ func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid str defer response.Body.Close() if response.StatusCode != 200 { - gateway.SendMessage( + gateway.SendTextMessage( returnJid, strconv.FormatInt(chatID, 10), fmt.Sprintf("Received status code %v", response.StatusCode), - "", c.xmpp, ) return nil @@ -872,22 +893,20 @@ func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid str tempDir, err := ioutil.TempDir("", "telegabber-*") if err != nil { - gateway.SendMessage( + gateway.SendTextMessage( returnJid, strconv.FormatInt(chatID, 10), fmt.Sprintf("Failed to create a temporary directory: %s", err.Error()), - "", c.xmpp, ) return nil } tempFile, err := os.Create(filepath.Join(tempDir, filepath.Base(text))) if err != nil { - gateway.SendMessage( + gateway.SendTextMessage( returnJid, strconv.FormatInt(chatID, 10), fmt.Sprintf("Failed to create a temporary file: %s", err.Error()), - "", c.xmpp, ) return nil @@ -895,11 +914,10 @@ func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid str _, err = io.Copy(tempFile, response.Body) if err != nil { - gateway.SendMessage( + gateway.SendTextMessage( returnJid, strconv.FormatInt(chatID, 10), fmt.Sprintf("Failed to write a temporary file: %s", err.Error()), - "", c.xmpp, ) return nil @@ -946,11 +964,10 @@ func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid str InputMessageContent: message, }) if err != nil { - gateway.SendMessage( + gateway.SendTextMessage( returnJid, strconv.FormatInt(chatID, 10), fmt.Sprintf("Not sent: %s", err.Error()), - "", c.xmpp, ) } diff --git a/telegram/utils_test.go b/telegram/utils_test.go index bfd6c49..03ab8bd 100644 --- a/telegram/utils_test.go +++ b/telegram/utils_test.go @@ -389,7 +389,7 @@ func TestMessageToPrefix1(t *testing.T) { }, }, } - prefix := (&Client{Session: &persistence.Session{}}).messageToPrefix(&message, "", "") + prefix := (&Client{Session: &persistence.Session{}}).messageToPrefix(&message, "", "", nil) if prefix != "➡ 42 | fwd: ziz" { t.Errorf("Wrong prefix: %v", prefix) } @@ -404,7 +404,7 @@ func TestMessageToPrefix2(t *testing.T) { }, }, } - prefix := (&Client{Session: &persistence.Session{}}).messageToPrefix(&message, "y.jpg", "") + prefix := (&Client{Session: &persistence.Session{}}).messageToPrefix(&message, "y.jpg", "", nil) if prefix != "⬅ 56 | fwd: (zaz) | preview: y.jpg" { t.Errorf("Wrong prefix: %v", prefix) } @@ -419,7 +419,7 @@ func TestMessageToPrefix3(t *testing.T) { }, }, } - prefix := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "a.jpg") + prefix := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "a.jpg", nil) if prefix != "< 56 | fwd: (zuz) | file: a.jpg" { t.Errorf("Wrong prefix: %v", prefix) } @@ -430,7 +430,7 @@ func TestMessageToPrefix4(t *testing.T) { Id: 23, IsOutgoing: true, } - prefix := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "") + prefix := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "", nil) if prefix != "> 23" { t.Errorf("Wrong prefix: %v", prefix) } @@ -445,8 +445,60 @@ func TestMessageToPrefix5(t *testing.T) { }, }, } - prefix := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "h.jpg", "a.jpg") + prefix := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "h.jpg", "a.jpg", nil) if prefix != "< 560 | fwd: (zyz) | preview: h.jpg | file: a.jpg" { t.Errorf("Wrong prefix: %v", prefix) } } + +func TestMessageToPrefix6(t *testing.T) { + message := client.Message{ + Id: 23, + IsOutgoing: true, + ReplyToMessageId: 42, + } + reply := client.Message{ + Id: 42, + Content: &client.MessageText{ + Text: &client.FormattedText{ + Text: "tist", + }, + }, + } + prefix := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "", &reply) + if prefix != "> 23 | reply: 42 | | tist" { + t.Errorf("Wrong prefix: %v", prefix) + } +} + +func GetSenderIdEmpty(t *testing.T) { + message := client.Message{} + senderId := (&Client{}).getSenderId(&message) + if senderId != 0 { + t.Errorf("Wrong sender id: %v", senderId) + } +} + +func GetSenderIdUser(t *testing.T) { + message := client.Message{ + SenderId: &client.MessageSenderUser{ + UserId: 42, + }, + } + senderId := (&Client{}).getSenderId(&message) + if senderId != 42 { + t.Errorf("Wrong sender id: %v", senderId) + } +} + +func GetSenderIdChat(t *testing.T) { + message := client.Message{ + SenderId: &client.MessageSenderChat{ + ChatId: -42, + }, + } + senderId := (&Client{}).getSenderId(&message) + if senderId != -42 { + t.Errorf("Wrong sender id: %v", senderId) + } +} diff --git a/xmpp/extensions/extensions.go b/xmpp/extensions/extensions.go index fac5e7b..ce27656 100644 --- a/xmpp/extensions/extensions.go +++ b/xmpp/extensions/extensions.go @@ -111,6 +111,13 @@ type IqVcardDesc struct { Text string `xml:",chardata"` } +// Reply is from XEP-0461 +type Reply struct { + XMLName xml.Name `xml:"urn:xmpp:reply:0 reply"` + To string `xml:"to,attr"` + Id string `xml:"id,attr"` +} + // Namespace is a namespace! func (c PresenceNickExtension) Namespace() string { return c.XMLName.Space @@ -131,6 +138,11 @@ func (c IqVcardTemp) GetSet() *stanza.ResultSet { return c.ResultSet } +// Namespace is a namespace! +func (c Reply) Namespace() string { + return c.XMLName.Space +} + func init() { // presence nick stanza.TypeRegistry.MapExtension(stanza.PKTPresence, xml.Name{ @@ -149,4 +161,10 @@ func init() { "vcard-temp", "vCard", }, IqVcardTemp{}) + + // reply + stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{ + "urn:xmpp:reply:0", + "reply", + }, Reply{}) } diff --git a/xmpp/gateway/gateway.go b/xmpp/gateway/gateway.go index daa8f8a..d309ade 100644 --- a/xmpp/gateway/gateway.go +++ b/xmpp/gateway/gateway.go @@ -13,6 +13,11 @@ import ( "gosrc.io/xmpp/stanza" ) +type Reply struct { + Author string + Id string +} + const NSNick string = "http://jabber.org/protocol/nick" // Queue stores presences to send later @@ -27,16 +32,26 @@ var Jid *stanza.Jid var DirtySessions = false // SendMessage creates and sends a message stanza -func SendMessage(to string, from string, body string, id string, component *xmpp.Component) { - sendMessageWrapper(to, from, body, id, component, "") +func SendMessage(to string, from string, body string, id string, component *xmpp.Component, reply *Reply) { + sendMessageWrapper(to, from, body, id, component, reply, "") +} + +// SendServiceMessage creates and sends a simple message stanza from transport +func SendServiceMessage(to string, body string, component *xmpp.Component) { + sendMessageWrapper(to, "", body, "", component, nil, "") +} + +// SendTextMessage creates and sends a simple message stanza +func SendTextMessage(to string, from string, body string, component *xmpp.Component) { + sendMessageWrapper(to, from, body, "", component, nil, "") } // SendMessageWithOOB creates and sends a message stanza with OOB URL -func SendMessageWithOOB(to string, from string, body string, id string, component *xmpp.Component, oob string) { - sendMessageWrapper(to, from, body, id, component, oob) +func SendMessageWithOOB(to string, from string, body string, id string, component *xmpp.Component, reply *Reply, oob string) { + sendMessageWrapper(to, from, body, id, component, reply, oob) } -func sendMessageWrapper(to string, from string, body string, id string, component *xmpp.Component, oob string) { +func sendMessageWrapper(to string, from string, body string, id string, component *xmpp.Component, reply *Reply, oob string) { componentJid := Jid.Full() var logFrom string @@ -69,6 +84,12 @@ func sendMessageWrapper(to string, from string, body string, id string, componen URL: oob, }) } + if reply != nil { + message.Extensions = append(message.Extensions, extensions.Reply{ + To: reply.Author, + Id: reply.Id, + }) + } sendMessage(&message, component) } diff --git a/xmpp/handlers.go b/xmpp/handlers.go index d83fef4..b023bc5 100644 --- a/xmpp/handlers.go +++ b/xmpp/handlers.go @@ -106,7 +106,7 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) { if err == nil && toJid.Bare() == gatewayJid && (strings.HasPrefix(msg.Body, "/") || strings.HasPrefix(msg.Body, "!")) { response := session.ProcessTransportCommand(msg.Body, resource) if response != "" { - gateway.SendMessage(msg.From, "", response, "", component) + gateway.SendServiceMessage(msg.From, response, component) } return } -- cgit v1.2.3 From 42ed16bf9e9d72bf226045f1f291b9d58e2a6200 Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Sat, 18 Mar 2023 17:43:11 -0400 Subject: Simulate carbons --- README.md | 31 +++++++++++++++++ persistence/sessions.go | 11 +++++++ telegram/commands.go | 5 +++ telegram/handlers.go | 2 +- telegram/utils.go | 74 +++++++++++++++++++++++++++++++++++++++-- xmpp/extensions/extensions.go | 77 +++++++++++++++++++++++++++++++++++++++++++ xmpp/gateway/gateway.go | 77 ++++++++++++++++++++++++++++++++++++++----- xmpp/handlers.go | 34 +++++++++++-------- 8 files changed, 284 insertions(+), 27 deletions(-) (limited to 'telegram/handlers.go') diff --git a/README.md b/README.md index 0ea9f73..36aa7ca 100644 --- a/README.md +++ b/README.md @@ -142,3 +142,34 @@ server { ``` Finally, update `:upload:` in your config.yml to match `server_name` in nginx config. + +### Carbons ### + +Telegabber needs special privileges according to XEP-0356 to simulate message carbons from the users (to display messages they have sent earlier or via other clients). Example configuration for Prosody: + +``` +modules_enabled = { + [...] + + "privilege"; +} + +[...] + +Component "telegabber.yourdomain.tld" + component_secret = "yourpassword" + modules_enabled = {"privilege"} + +[...] + +VirtualHost "yourdomain.tld" + [...] + + privileged_entities = { + [...] + + ["telegabber.yourdomain.tld"] = { + message = "outgoing"; + }, + } +``` diff --git a/persistence/sessions.go b/persistence/sessions.go index ed95f60..c0750a3 100644 --- a/persistence/sessions.go +++ b/persistence/sessions.go @@ -40,6 +40,7 @@ type Session struct { RawMessages bool `yaml:":rawmessages"` AsciiArrows bool `yaml:":asciiarrows"` OOBMode bool `yaml:":oobmode"` + Carbons bool `yaml:":carbons"` } var configKeys = []string{ @@ -48,6 +49,7 @@ var configKeys = []string{ "rawmessages", "asciiarrows", "oobmode", + "carbons", } var sessionDB *SessionsYamlDB @@ -122,6 +124,8 @@ func (s *Session) Get(key string) (string, error) { return fromBool(s.AsciiArrows), nil case "oobmode": return fromBool(s.OOBMode), nil + case "carbons": + return fromBool(s.Carbons), nil } return "", errors.New("Unknown session property") @@ -172,6 +176,13 @@ func (s *Session) Set(key string, value string) (string, error) { } s.OOBMode = b return value, nil + case "carbons": + b, err := toBool(value) + if err != nil { + return "", err + } + s.Carbons = b + return value, nil } return "", errors.New("Unknown session property") diff --git a/telegram/commands.go b/telegram/commands.go index 160e486..368a461 100644 --- a/telegram/commands.go +++ b/telegram/commands.go @@ -193,6 +193,7 @@ func (c *Client) sendMessagesReverse(chatID int64, messages []*client.Message) { strconv.FormatInt(message.Id, 10), c.xmpp, reply, + false, ) } } @@ -361,6 +362,10 @@ func (c *Client) ProcessTransportCommand(cmdline string, resource string) string } case "config": if len(args) > 1 { + if !gateway.MessageOutgoingPermission && args[0] == "carbons" && args[1] == "true" { + return "The server did not allow to enable carbons" + } + value, err := c.Session.Set(args[0], args[1]) if err != nil { return err.Error() diff --git a/telegram/handlers.go b/telegram/handlers.go index 307562a..84e3748 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -242,7 +242,7 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { textContent.Text.Entities, markupFunction, )) - gateway.SendMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, "e" + strconv.FormatInt(update.MessageId, 10), c.xmpp, nil) + gateway.SendMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, "e" + strconv.FormatInt(update.MessageId, 10), c.xmpp, nil, false) } } diff --git a/telegram/utils.go b/telegram/utils.go index 68dd524..99492e1 100644 --- a/telegram/utils.go +++ b/telegram/utils.go @@ -109,6 +109,33 @@ func (c *Client) GetContactByID(id int64, chat *client.Chat) (*client.Chat, *cli return chat, user, nil } +// IsPM checks if a chat is PM +func (c *Client) IsPM(id int64) (bool, error) { + if !c.Online() || id == 0 { + return false, errOffline + } + + var err error + + chat, ok := c.cache.GetChat(id) + if !ok { + chat, err = c.client.GetChat(&client.GetChatRequest{ + ChatId: id, + }) + if err != nil { + return false, err + } + + c.cache.SetChat(id, chat) + } + + chatType := chat.Type.ChatTypeType() + if chatType == client.TypeChatTypePrivate || chatType == client.TypeChatTypeSecret { + return true, nil + } + return false, nil +} + func (c *Client) userStatusToText(status client.UserStatus, chatID int64) (string, string, string) { var show, textStatus, presenceType string @@ -782,6 +809,7 @@ func (c *Client) ensureDownloadFile(file *client.File) *client.File { // ProcessIncomingMessage transfers a message to XMPP side and marks it as read on Telegram side func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) { var text, oob, auxText string + var err error reply, replyMsg := c.getMessageReply(message) @@ -847,12 +875,33 @@ func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) { MessageIds: []int64{message.Id}, ForceRead: true, }) + // forward message to XMPP sId := strconv.FormatInt(message.Id, 10) sChatId := strconv.FormatInt(chatId, 10) - gateway.SendMessageWithOOB(c.jid, sChatId, text, sId, c.xmpp, reply, oob) - if auxText != "" { - gateway.SendMessage(c.jid, sChatId, auxText, sId, c.xmpp, reply) + + var jids []string + var isPM bool + if gateway.MessageOutgoingPermission && c.Session.Carbons { + isPM, err = c.IsPM(chatId) + if err != nil { + log.Errorf("Could not determine if chat is PM: %v", err) + } + } + isOutgoing := isPM && message.IsOutgoing + if isOutgoing { + for resource := range c.resourcesRange() { + jids = append(jids, c.jid + "/" + resource) + } + } else { + jids = []string{c.jid} + } + + for _, jid := range jids { + gateway.SendMessageWithOOB(jid, sChatId, text, sId, c.xmpp, reply, oob, isOutgoing) + if auxText != "" { + gateway.SendMessage(jid, sChatId, auxText, sId, c.xmpp, reply, isOutgoing) + } } } @@ -1024,6 +1073,25 @@ func (c *Client) deleteResource(resource string) { } } +func (c *Client) resourcesRange() chan string { + c.locks.resourcesLock.Lock() + + resourceChan := make(chan string, 1) + + go func() { + defer func() { + c.locks.resourcesLock.Unlock() + close(resourceChan) + }() + + for resource := range c.resources { + resourceChan <- resource + } + }() + + return resourceChan +} + // resend statuses to (to another resource, for example) func (c *Client) roster(resource string) { if _, ok := c.resources[resource]; ok { diff --git a/xmpp/extensions/extensions.go b/xmpp/extensions/extensions.go index c982581..2cf7d45 100644 --- a/xmpp/extensions/extensions.go +++ b/xmpp/extensions/extensions.go @@ -141,6 +141,45 @@ type FallbackSubject struct { End string `xml:"end,attr"` } +// CarbonReceived is from XEP-0280 +type CarbonReceived struct { + XMLName xml.Name `xml:"urn:xmpp:carbons:2 received"` + Forwarded stanza.Forwarded `xml:"urn:xmpp:forward:0 forwarded"` +} + +// CarbonSent is from XEP-0280 +type CarbonSent struct { + XMLName xml.Name `xml:"urn:xmpp:carbons:2 sent"` + Forwarded stanza.Forwarded `xml:"urn:xmpp:forward:0 forwarded"` +} + +// ComponentPrivilege is from XEP-0356 +type ComponentPrivilege struct { + XMLName xml.Name `xml:"urn:xmpp:privilege:1 privilege"` + Perms []ComponentPerm `xml:"perm"` + Forwarded stanza.Forwarded `xml:"urn:xmpp:forward:0 forwarded"` +} + +// ComponentPerm is from XEP-0356 +type ComponentPerm struct { + XMLName xml.Name `xml:"perm"` + Access string `xml:"access,attr"` + Type string `xml:"type,attr"` + Push bool `xml:"push,attr"` +} + +// ClientMessage is a jabber:client NS message compatible with Prosody's XEP-0356 implementation +type ClientMessage struct { + XMLName xml.Name `xml:"jabber:client message"` + stanza.Attrs + + Subject string `xml:"subject,omitempty"` + Body string `xml:"body,omitempty"` + Thread string `xml:"thread,omitempty"` + Error stanza.Err `xml:"error,omitempty"` + Extensions []stanza.MsgExtension `xml:",omitempty"` +} + // Namespace is a namespace! func (c PresenceNickExtension) Namespace() string { return c.XMLName.Space @@ -171,6 +210,26 @@ func (c Fallback) Namespace() string { return c.XMLName.Space } +// Namespace is a namespace! +func (c CarbonReceived) Namespace() string { + return c.XMLName.Space +} + +// Namespace is a namespace! +func (c CarbonSent) Namespace() string { + return c.XMLName.Space +} + +// Namespace is a namespace! +func (c ComponentPrivilege) Namespace() string { + return c.XMLName.Space +} + +// Name is a packet name +func (ClientMessage) Name() string { + return "message" +} + // NewReplyFallback initializes a fallback range func NewReplyFallback(start uint64, end uint64) Fallback { return Fallback{ @@ -214,4 +273,22 @@ func init() { "urn:xmpp:fallback:0", "fallback", }, Fallback{}) + + // carbon received + stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{ + "urn:xmpp:carbons:2", + "received", + }, CarbonReceived{}) + + // carbon sent + stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{ + "urn:xmpp:carbons:2", + "sent", + }, CarbonSent{}) + + // component privilege + stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{ + "urn:xmpp:privilege:1", + "privilege", + }, ComponentPrivilege{}) } diff --git a/xmpp/gateway/gateway.go b/xmpp/gateway/gateway.go index de8b495..534ee7e 100644 --- a/xmpp/gateway/gateway.go +++ b/xmpp/gateway/gateway.go @@ -2,6 +2,7 @@ package gateway import ( "encoding/xml" + "github.com/pkg/errors" "strings" "sync" @@ -33,31 +34,44 @@ var Jid *stanza.Jid // were changed and need to be re-flushed to the YamlDB var DirtySessions = false +// MessageOutgoingPermission allows to fake outgoing messages by foreign JIDs +var MessageOutgoingPermission = false + // SendMessage creates and sends a message stanza -func SendMessage(to string, from string, body string, id string, component *xmpp.Component, reply *Reply) { - sendMessageWrapper(to, from, body, id, component, reply, "") +func SendMessage(to string, from string, body string, id string, component *xmpp.Component, reply *Reply, isOutgoing bool) { + sendMessageWrapper(to, from, body, id, component, reply, "", isOutgoing) } // SendServiceMessage creates and sends a simple message stanza from transport func SendServiceMessage(to string, body string, component *xmpp.Component) { - sendMessageWrapper(to, "", body, "", component, nil, "") + sendMessageWrapper(to, "", body, "", component, nil, "", false) } // SendTextMessage creates and sends a simple message stanza func SendTextMessage(to string, from string, body string, component *xmpp.Component) { - sendMessageWrapper(to, from, body, "", component, nil, "") + sendMessageWrapper(to, from, body, "", component, nil, "", false) } // SendMessageWithOOB creates and sends a message stanza with OOB URL -func SendMessageWithOOB(to string, from string, body string, id string, component *xmpp.Component, reply *Reply, oob string) { - sendMessageWrapper(to, from, body, id, component, reply, oob) +func SendMessageWithOOB(to string, from string, body string, id string, component *xmpp.Component, reply *Reply, oob string, isOutgoing bool) { + sendMessageWrapper(to, from, body, id, component, reply, oob, isOutgoing) } -func sendMessageWrapper(to string, from string, body string, id string, component *xmpp.Component, reply *Reply, oob string) { +func sendMessageWrapper(to string, from string, body string, id string, component *xmpp.Component, reply *Reply, oob string, isOutgoing bool) { + toJid, err := stanza.NewJid(to) + if err != nil { + log.WithFields(log.Fields{ + "to": to, + }).Error(errors.Wrap(err, "Invalid to JID!")) + return + } + bareTo := toJid.Bare() + componentJid := Jid.Full() var logFrom string var messageFrom string + var messageTo string if from == "" { logFrom = componentJid messageFrom = componentJid @@ -65,6 +79,12 @@ func sendMessageWrapper(to string, from string, body string, id string, componen logFrom = from messageFrom = from + "@" + componentJid } + if isOutgoing { + messageTo = messageFrom + messageFrom = bareTo + "/" + Jid.Resource + } else { + messageTo = to + } log.WithFields(log.Fields{ "from": logFrom, @@ -74,7 +94,7 @@ func sendMessageWrapper(to string, from string, body string, id string, componen message := stanza.Message{ Attrs: stanza.Attrs{ From: messageFrom, - To: to, + To: messageTo, Type: "chat", Id: id, }, @@ -96,7 +116,34 @@ func sendMessageWrapper(to string, from string, body string, id string, componen } } - sendMessage(&message, component) + if isOutgoing { + carbonMessage := extensions.ClientMessage{ + Attrs: stanza.Attrs{ + From: bareTo, + To: to, + Type: "chat", + }, + } + carbonMessage.Extensions = append(carbonMessage.Extensions, extensions.CarbonSent{ + Forwarded: stanza.Forwarded{ + Stanza: extensions.ClientMessage(message), + }, + }) + privilegeMessage := stanza.Message{ + Attrs: stanza.Attrs{ + From: Jid.Bare(), + To: toJid.Domain, + }, + } + privilegeMessage.Extensions = append(privilegeMessage.Extensions, extensions.ComponentPrivilege{ + Forwarded: stanza.Forwarded{ + Stanza: carbonMessage, + }, + }) + sendMessage(&privilegeMessage, component) + } else { + sendMessage(&message, component) + } } // SetNickname sets a new nickname for a contact @@ -297,3 +344,15 @@ func ResumableSend(component *xmpp.Component, packet stanza.Packet) error { } return err } + +// SplitJID tokenizes a JID string to bare JID and resource +func SplitJID(from string) (string, string, bool) { + fromJid, err := stanza.NewJid(from) + if err != nil { + log.WithFields(log.Fields{ + "from": from, + }).Error(errors.Wrap(err, "Invalid from JID!")) + return "", "", false + } + return fromJid.Bare(), fromJid.Resource, true +} diff --git a/xmpp/handlers.go b/xmpp/handlers.go index acf62a2..2bdbaef 100644 --- a/xmpp/handlers.go +++ b/xmpp/handlers.go @@ -79,7 +79,7 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) { }).Warn("Message") log.Debugf("%#v", msg) - bare, resource, ok := splitFrom(msg.From) + bare, resource, ok := gateway.SplitJID(msg.From) if !ok { return } @@ -152,6 +152,23 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) { } log.Warn("Unknown purpose of the message, skipping") } + + if msg.Body == "" { + var privilege extensions.ComponentPrivilege + if ok := msg.Get(&privilege); ok { + log.Debugf("privilege: %#v", privilege) + } + + for _, perm := range privilege.Perms { + if perm.Access == "message" && perm.Type == "outgoing" { + gateway.MessageOutgoingPermission = true + } + } + } + + if msg.Type == "error" { + log.Errorf("MESSAGE ERROR: %#v", p) + } } // HandlePresence processes an incoming XMPP presence @@ -196,7 +213,7 @@ func handleSubscription(s xmpp.Sender, p stanza.Presence) { if !ok { return } - bare, _, ok := splitFrom(p.From) + bare, _, ok := gateway.SplitJID(p.From) if !ok { return } @@ -227,7 +244,7 @@ func handlePresence(s xmpp.Sender, p stanza.Presence) { log.Debugf("%#v", p) // create session - bare, resource, ok := splitFrom(p.From) + bare, resource, ok := gateway.SplitJID(p.From) if !ok { return } @@ -385,17 +402,6 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ) { _ = gateway.ResumableSend(component, answer) } -func splitFrom(from string) (string, string, bool) { - fromJid, err := stanza.NewJid(from) - if err != nil { - log.WithFields(log.Fields{ - "from": from, - }).Error(errors.Wrap(err, "Invalid from JID!")) - return "", "", false - } - return fromJid.Bare(), fromJid.Resource, true -} - func toToID(to string) (int64, bool) { toParts := strings.Split(to, "@") if len(toParts) < 2 { -- cgit v1.2.3 From 22b46c71ce932cc97fa6d437a7473a653bb59a41 Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Sat, 18 Mar 2023 17:47:08 -0400 Subject: gofmt --- telegram/commands.go | 8 ++++---- telegram/handlers.go | 2 +- telegram/utils.go | 8 ++++---- xmpp/extensions/extensions.go | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'telegram/handlers.go') diff --git a/telegram/commands.go b/telegram/commands.go index 368a461..2a72219 100644 --- a/telegram/commands.go +++ b/telegram/commands.go @@ -52,10 +52,10 @@ var transportCommands = map[string]command{ "setpassword": command{"[old] [new]", "set or remove password"}, "config": command{"[param] [value]", "view or update configuration options"}, "report": command{"[chat] [comment]", "report a chat by id or @username"}, - "add": command{"@username", "add @username to your chat list"}, - "join": command{"https://t.me/invite_link", "join to chat via invite link or @publicname"}, - "supergroup": command{"title description", "create new supergroup «title» with «description»"}, - "channel": command{"title description", "create new channel «title» with «description»"}, + "add": command{"@username", "add @username to your chat list"}, + "join": command{"https://t.me/invite_link", "join to chat via invite link or @publicname"}, + "supergroup": command{"title description", "create new supergroup «title» with «description»"}, + "channel": command{"title description", "create new channel «title» with «description»"}, } var chatCommands = map[string]command{ diff --git a/telegram/handlers.go b/telegram/handlers.go index 84e3748..bd768ae 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -242,7 +242,7 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { textContent.Text.Entities, markupFunction, )) - gateway.SendMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, "e" + strconv.FormatInt(update.MessageId, 10), c.xmpp, nil, false) + gateway.SendMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, "e"+strconv.FormatInt(update.MessageId, 10), c.xmpp, nil, false) } } diff --git a/telegram/utils.go b/telegram/utils.go index 99492e1..905a97b 100644 --- a/telegram/utils.go +++ b/telegram/utils.go @@ -320,7 +320,7 @@ func (c *Client) getMessageReply(message *client.Message) (reply *gateway.Reply, return } - reply = &gateway.Reply { + reply = &gateway.Reply{ Author: fmt.Sprintf("%v@%s", c.getSenderId(replyMsg), gateway.Jid.Full()), Id: strconv.FormatInt(message.ReplyToMessageId, 10), } @@ -770,8 +770,8 @@ func (c *Client) messageToPrefix(message *client.Message, previewString string, } // reply to if message.ReplyToMessageId != 0 { - replyStart = c.countCharsInLines(&prefix) + (len(prefix) - 1) * len(messageHeaderSeparator) - replyLine := "reply: "+c.formatMessage(message.ChatId, message.ReplyToMessageId, true, replyMsg) + replyStart = c.countCharsInLines(&prefix) + (len(prefix)-1)*len(messageHeaderSeparator) + replyLine := "reply: " + c.formatMessage(message.ChatId, message.ReplyToMessageId, true, replyMsg) prefix = append(prefix, replyLine) replyEnd = replyStart + len(replyLine) + len(messageHeaderSeparator) } @@ -891,7 +891,7 @@ func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) { isOutgoing := isPM && message.IsOutgoing if isOutgoing { for resource := range c.resourcesRange() { - jids = append(jids, c.jid + "/" + resource) + jids = append(jids, c.jid+"/"+resource) } } else { jids = []string{c.jid} diff --git a/xmpp/extensions/extensions.go b/xmpp/extensions/extensions.go index 2cf7d45..78de47d 100644 --- a/xmpp/extensions/extensions.go +++ b/xmpp/extensions/extensions.go @@ -237,7 +237,7 @@ func NewReplyFallback(start uint64, end uint64) Fallback { Body: []FallbackBody{ FallbackBody{ Start: strconv.FormatUint(start, 10), - End: strconv.FormatUint(end, 10), + End: strconv.FormatUint(end, 10), }, }, } -- cgit v1.2.3 From 7215d11d7973b9896c6223938649c75165fa3ae7 Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Mon, 5 Jun 2023 04:22:13 -0400 Subject: XEP-0308 message editing --- badger/ids.go | 98 +++++++++++++++++++++++++++++++++++++++++++ telegram/handlers.go | 5 +++ telegram/utils.go | 39 +++++++++-------- xmpp/extensions/extensions.go | 17 ++++++++ xmpp/handlers.go | 43 ++++++++++++++++++- 5 files changed, 184 insertions(+), 18 deletions(-) (limited to 'telegram/handlers.go') diff --git a/badger/ids.go b/badger/ids.go index 80fb9ad..079887a 100644 --- a/badger/ids.go +++ b/badger/ids.go @@ -121,6 +121,104 @@ func splitTgByteString(val []byte) (int64, int64, error) { return tgChatId, tgMsgId, err } +// ReplaceIdPair replaces an old entry by XMPP ID with both new XMPP and Tg ID +func (db *IdsDB) ReplaceIdPair(tgAccount, xmppAccount, oldXmppId, newXmppId string, newMsgId int64) error { + // read old pair + chatId, oldMsgId, err := db.GetByXmppId(tgAccount, xmppAccount, oldXmppId) + if err != nil { + return err + } + + bPrefix := toKeyPrefix(tgAccount, xmppAccount) + + bOldTgId := toTgByteString(chatId, oldMsgId) + bOldXmppId := toXmppByteString(oldXmppId) + bOldTgKey := toByteKey(bPrefix, bOldTgId, "tg") + bOldXmppKey := toByteKey(bPrefix, bOldXmppId, "xmpp") + + bTgId := toTgByteString(chatId, newMsgId) + bXmppId := toXmppByteString(newXmppId) + bTgKey := toByteKey(bPrefix, bTgId, "tg") + bXmppKey := toByteKey(bPrefix, bXmppId, "xmpp") + + return db.db.Update(func(txn *badger.Txn) error { + // save new pair + if err := txn.Set(bTgKey, bXmppId); err != nil { + return err + } + if err := txn.Set(bXmppKey, bTgId); err != nil { + return err + } + // delete old pair + if err := txn.Delete(bOldTgKey); err != nil { + return err + } + return txn.Delete(bOldXmppKey) + }) +} + +// ReplaceXmppId replaces an old XMPP ID with new XMPP ID and keeps Tg ID intact +func (db *IdsDB) ReplaceXmppId(tgAccount, xmppAccount, oldXmppId, newXmppId string) error { + // read old Tg IDs + chatId, msgId, err := db.GetByXmppId(tgAccount, xmppAccount, oldXmppId) + if err != nil { + return err + } + + bPrefix := toKeyPrefix(tgAccount, xmppAccount) + + bOldXmppId := toXmppByteString(oldXmppId) + bOldXmppKey := toByteKey(bPrefix, bOldXmppId, "xmpp") + + bTgId := toTgByteString(chatId, msgId) + bXmppId := toXmppByteString(newXmppId) + bTgKey := toByteKey(bPrefix, bTgId, "tg") + bXmppKey := toByteKey(bPrefix, bXmppId, "xmpp") + + return db.db.Update(func(txn *badger.Txn) error { + // save new pair + if err := txn.Set(bTgKey, bXmppId); err != nil { + return err + } + if err := txn.Set(bXmppKey, bTgId); err != nil { + return err + } + // delete old xmpp->tg entry + return txn.Delete(bOldXmppKey) + }) +} + +// ReplaceTgId replaces an old Tg ID with new Tg ID and keeps Tg chat ID and XMPP ID intact +func (db *IdsDB) ReplaceTgId(tgAccount, xmppAccount string, chatId, oldMsgId, newMsgId int64) error { + // read old XMPP ID + xmppId, err := db.GetByTgIds(tgAccount, xmppAccount, chatId, oldMsgId) + if err != nil { + return err + } + + bPrefix := toKeyPrefix(tgAccount, xmppAccount) + + bOldTgId := toTgByteString(chatId, oldMsgId) + bOldTgKey := toByteKey(bPrefix, bOldTgId, "tg") + + bTgId := toTgByteString(chatId, newMsgId) + bXmppId := toXmppByteString(xmppId) + bTgKey := toByteKey(bPrefix, bTgId, "tg") + bXmppKey := toByteKey(bPrefix, bXmppId, "xmpp") + + return db.db.Update(func(txn *badger.Txn) error { + // save new pair + if err := txn.Set(bTgKey, bXmppId); err != nil { + return err + } + if err := txn.Set(bXmppKey, bTgId); err != nil { + return err + } + // delete old tg->xmpp entry + return txn.Delete(bOldTgKey) + }) +} + // Gc compacts the value log func (db *IdsDB) Gc() { db.db.RunValueLogGC(0.7) diff --git a/telegram/handlers.go b/telegram/handlers.go index bd768ae..6de59e7 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -272,6 +272,11 @@ func (c *Client) updateAuthorizationState(update *client.UpdateAuthorizationStat // clean uploaded files func (c *Client) updateMessageSendSucceeded(update *client.UpdateMessageSendSucceeded) { + log.Debugf("replace message %v with %v", update.OldMessageId, update.Message.Id) + if err := gateway.IdsDB.ReplaceTgId(c.Session.Login, c.jid, update.Message.ChatId, update.OldMessageId, update.Message.Id); err != nil { + log.Error("failed to replace %v with %v: %v", update.OldMessageId, update.Message.Id, err.Error()) + } + file, _ := c.contentToFile(update.Message.Content) if file != nil && file.Local != nil { c.cleanTempFile(file.Local.Path) diff --git a/telegram/utils.go b/telegram/utils.go index dd63248..ecce6da 100644 --- a/telegram/utils.go +++ b/telegram/utils.go @@ -991,14 +991,14 @@ func (c *Client) PrepareOutgoingMessageContent(text string) client.InputMessageC return c.prepareOutgoingMessageContent(text, nil) } -// ProcessOutgoingMessage executes commands or sends messages to mapped chats -func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid string, id string, replyId int64) { +// ProcessOutgoingMessage executes commands or sends messages to mapped chats, returns message id +func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid string, replyId int64, replaceId int64) int64 { if !c.Online() { // we're offline - return + return 0 } - if strings.HasPrefix(text, "/") || strings.HasPrefix(text, "!") { + if replaceId == 0 && (strings.HasPrefix(text, "/") || strings.HasPrefix(text, "!")) { // try to execute commands response, isCommand := c.ProcessChatCommand(chatID, text) if response != "" { @@ -1006,7 +1006,7 @@ func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid str } // do not send on success if isCommand { - return + return 0 } } @@ -1014,7 +1014,7 @@ func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid str // quotations var reply int64 - if replyId == 0 { + if replaceId == 0 && replyId == 0 { replySlice := replyRegex.FindStringSubmatch(text) if len(replySlice) > 1 { reply, _ = strconv.ParseInt(replySlice[1], 10, 64) @@ -1069,24 +1069,29 @@ func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid str content := c.prepareOutgoingMessageContent(text, file) + if replaceId != 0 { + tgMessage, err := c.client.EditMessageText(&client.EditMessageTextRequest{ + ChatId: chatID, + MessageId: replaceId, + InputMessageContent: content, + }) + if err != nil { + c.returnError(returnJid, chatID, "Not edited", err) + return 0 + } + return tgMessage.Id + } + tgMessage, err := c.client.SendMessage(&client.SendMessageRequest{ ChatId: chatID, ReplyToMessageId: reply, InputMessageContent: content, }) if err != nil { - gateway.SendTextMessage( - returnJid, - strconv.FormatInt(chatID, 10), - fmt.Sprintf("Not sent: %s", err.Error()), - c.xmpp, - ) - } else { - err = gateway.IdsDB.Set(c.Session.Login, c.jid, tgMessage.ChatId, tgMessage.Id, id) - if err != nil { - log.Errorf("Failed to save ids %v/%v %v", tgMessage.ChatId, tgMessage.Id, id) - } + c.returnError(returnJid, chatID, "Not sent", err) + return 0 } + return tgMessage.Id } func (c *Client) returnMessage(returnJid string, chatID int64, text string) { diff --git a/xmpp/extensions/extensions.go b/xmpp/extensions/extensions.go index 78de47d..2d547af 100644 --- a/xmpp/extensions/extensions.go +++ b/xmpp/extensions/extensions.go @@ -180,6 +180,12 @@ type ClientMessage struct { Extensions []stanza.MsgExtension `xml:",omitempty"` } +// Replace is from XEP-0308 +type Replace struct { + XMLName xml.Name `xml:"urn:xmpp:message-correct:0 replace"` + Id string `xml:"id,attr"` +} + // Namespace is a namespace! func (c PresenceNickExtension) Namespace() string { return c.XMLName.Space @@ -225,6 +231,11 @@ func (c ComponentPrivilege) Namespace() string { return c.XMLName.Space } +// Namespace is a namespace! +func (c Replace) Namespace() string { + return c.XMLName.Space +} + // Name is a packet name func (ClientMessage) Name() string { return "message" @@ -291,4 +302,10 @@ func init() { "urn:xmpp:privilege:1", "privilege", }, ComponentPrivilege{}) + + // message edit + stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{ + "urn:xmpp:message-correct:0", + "replace", + }, Replace{}) } diff --git a/xmpp/handlers.go b/xmpp/handlers.go index 5178acd..51fd831 100644 --- a/xmpp/handlers.go +++ b/xmpp/handlers.go @@ -102,10 +102,13 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) { if ok { var reply extensions.Reply var fallback extensions.Fallback + var replace extensions.Replace msg.Get(&reply) msg.Get(&fallback) + msg.Get(&replace) log.Debugf("reply: %#v", reply) log.Debugf("fallback: %#v", fallback) + log.Debugf("replace: %#v", replace) var replyId int64 var err error @@ -138,8 +141,46 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) { text = text[:start] + text[end:] } } + var replaceId int64 + if replace.Id != "" { + chatId, msgId, err := gateway.IdsDB.GetByXmppId(session.Session.Login, bare, replace.Id) + if err == nil { + if chatId != toID { + gateway.SendTextMessage(msg.From, strconv.FormatInt(toID, 10), "", component) + return + } + replaceId = msgId + log.Debugf("replace tg: %#v %#v", chatId, msgId) + } else { + gateway.SendTextMessage(msg.From, strconv.FormatInt(toID, 10), "", component) + return + } + } - session.ProcessOutgoingMessage(toID, text, msg.From, msg.Id, replyId) + tgMessageId := session.ProcessOutgoingMessage(toID, text, msg.From, replyId, replaceId) + if tgMessageId != 0 { + if replaceId != 0 { + // not needed (is it persistent among clients though?) + /* err = gateway.IdsDB.ReplaceIdPair(session.Session.Login, bare, replace.Id, msg.Id, tgMessageId) + if err != nil { + log.Errorf("Failed to replace id %v with %v %v", replace.Id, msg.Id, tgMessageId) + } */ + } else { + err = gateway.IdsDB.Set(session.Session.Login, bare, toID, tgMessageId, msg.Id) + if err != nil { + log.Errorf("Failed to save ids %v/%v %v", toID, tgMessageId, msg.Id) + } + } + } else { + /* + // if a message failed to edit on Telegram side, match new XMPP ID with old Telegram ID anyway + if replaceId != 0 { + err = gateway.IdsDB.ReplaceXmppId(session.Session.Login, bare, replace.Id, msg.Id) + if err != nil { + log.Errorf("Failed to replace id %v with %v", replace.Id, msg.Id) + } + } */ + } return } else { toJid, err := stanza.NewJid(msg.To) -- cgit v1.2.3 From 00f1417cb2ed8199633a12989e9befb53298d407 Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Thu, 8 Jun 2023 13:33:22 -0400 Subject: Version 1.6.0 --- badger/ids.go | 2 +- telegabber.go | 2 +- telegram/commands.go | 2 +- telegram/handlers.go | 2 +- telegram/utils.go | 12 ++++++------ xmpp/handlers.go | 16 ++++++++-------- 6 files changed, 18 insertions(+), 18 deletions(-) (limited to 'telegram/handlers.go') diff --git a/badger/ids.go b/badger/ids.go index 079887a..1295e8a 100644 --- a/badger/ids.go +++ b/badger/ids.go @@ -92,7 +92,7 @@ func toKeyPrefix(tgAccount, xmppAccount string) []byte { } func toByteKey(prefix, suffix []byte, typ string) []byte { - key := make([]byte, 0, len(prefix) + len(suffix) + 6) + key := make([]byte, 0, len(prefix)+len(suffix)+6) key = append(key, prefix...) key = append(key, []byte(typ)...) key = append(key, []byte("/")...) diff --git a/telegabber.go b/telegabber.go index d133d80..48db544 100644 --- a/telegabber.go +++ b/telegabber.go @@ -15,7 +15,7 @@ import ( goxmpp "gosrc.io/xmpp" ) -var version string = "1.6.0-dev" +var version string = "1.6.0" var commit string var sm *goxmpp.StreamManager diff --git a/telegram/commands.go b/telegram/commands.go index f36108a..5e16a6a 100644 --- a/telegram/commands.go +++ b/telegram/commands.go @@ -652,7 +652,7 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) keyValueString("Chat title", info.Fn), keyValueString("Photo", link), keyValueString("Username", info.Nickname), - keyValueString("Full name", info.Given + " " + info.Family), + keyValueString("Full name", info.Given+" "+info.Family), keyValueString("Phone number", info.Tel), } return strings.Join(entries, "\n"), true diff --git a/telegram/handlers.go b/telegram/handlers.go index 6de59e7..57402ab 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -274,7 +274,7 @@ func (c *Client) updateAuthorizationState(update *client.UpdateAuthorizationStat func (c *Client) updateMessageSendSucceeded(update *client.UpdateMessageSendSucceeded) { log.Debugf("replace message %v with %v", update.OldMessageId, update.Message.Id) if err := gateway.IdsDB.ReplaceTgId(c.Session.Login, c.jid, update.Message.ChatId, update.OldMessageId, update.Message.Id); err != nil { - log.Error("failed to replace %v with %v: %v", update.OldMessageId, update.Message.Id, err.Error()) + log.Errorf("failed to replace %v with %v: %v", update.OldMessageId, update.Message.Id, err.Error()) } file, _ := c.contentToFile(update.Message.Content) diff --git a/telegram/utils.go b/telegram/utils.go index a4a2bd4..58f7712 100644 --- a/telegram/utils.go +++ b/telegram/utils.go @@ -25,13 +25,13 @@ import ( ) type VCardInfo struct { - Fn string - Photo *client.File + Fn string + Photo *client.File Nickname string - Given string - Family string - Tel string - Info string + Given string + Family string + Tel string + Info string } var errOffline = errors.New("TDlib instance is offline") diff --git a/xmpp/handlers.go b/xmpp/handlers.go index 5034551..d5666b1 100644 --- a/xmpp/handlers.go +++ b/xmpp/handlers.go @@ -183,13 +183,13 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) { } } else { /* - // if a message failed to edit on Telegram side, match new XMPP ID with old Telegram ID anyway - if replaceId != 0 { - err = gateway.IdsDB.ReplaceXmppId(session.Session.Login, bare, replace.Id, msg.Id) - if err != nil { - log.Errorf("Failed to replace id %v with %v", replace.Id, msg.Id) - } - } */ + // if a message failed to edit on Telegram side, match new XMPP ID with old Telegram ID anyway + if replaceId != 0 { + err = gateway.IdsDB.ReplaceXmppId(session.Session.Login, bare, replace.Id, msg.Id) + if err != nil { + log.Errorf("Failed to replace id %v with %v", replace.Id, msg.Id) + } + } */ } return } else { @@ -225,7 +225,7 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) { suffix := "@" + msg.From for bare := range sessions { if strings.HasSuffix(bare, suffix) { - gateway.SendServiceMessage(bare, "Your server \"" + msg.From + "\" does not allow to send carbons", component) + gateway.SendServiceMessage(bare, "Your server \""+msg.From+"\" does not allow to send carbons", component) } } } -- cgit v1.2.3 From f8ad8c0204e2effabce4545c91992146a560168d Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Fri, 30 Jun 2023 08:48:36 -0400 Subject: Update chat title in chats cache --- telegram/handlers.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'telegram/handlers.go') diff --git a/telegram/handlers.go b/telegram/handlers.go index 57402ab..65e5f2f 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -294,8 +294,13 @@ func (c *Client) updateChatTitle(update *client.UpdateChatTitle) { gateway.SetNickname(c.jid, strconv.FormatInt(update.ChatId, 10), update.Title, c.xmpp) // set also the status (for group chats only) - _, user, _ := c.GetContactByID(update.ChatId, nil) + chat, user, _ := c.GetContactByID(update.ChatId, nil) if user == nil { c.ProcessStatusUpdate(update.ChatId, update.Title, "chat", gateway.SPImmed(true)) } + + // update chat title in the cache + if chat != nil { + chat.Title = update.Title + } } -- cgit v1.2.3 From 959dc061ff30ba1cf5c699adc0f7d1d991d7afa5 Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Sat, 8 Jul 2023 23:52:30 -0400 Subject: Send carbons for outgoing messages to other resources --- telegram/client.go | 6 +++- telegram/commands.go | 7 +++-- telegram/handlers.go | 28 +++++++++++-------- telegram/utils.go | 74 +++++++++++++++++++++++++++++++++++-------------- xmpp/gateway/gateway.go | 14 +++++----- xmpp/handlers.go | 3 ++ 6 files changed, 89 insertions(+), 43 deletions(-) (limited to 'telegram/handlers.go') diff --git a/telegram/client.go b/telegram/client.go index 71d8125..61d46aa 100644 --- a/telegram/client.go +++ b/telegram/client.go @@ -52,6 +52,7 @@ type Client struct { jid string Session *persistence.Session resources map[string]bool + outbox map[string]string content *config.TelegramContentConfig cache *cache.Cache online bool @@ -59,13 +60,15 @@ type Client struct { DelayedStatuses map[int64]*DelayedStatus DelayedStatusesLock sync.Mutex - locks clientLocks + locks clientLocks + SendMessageLock sync.Mutex } type clientLocks struct { authorizationReady sync.Mutex chatMessageLocks map[int64]*sync.Mutex resourcesLock sync.Mutex + outboxLock sync.Mutex } // NewClient instantiates a Telegram App @@ -121,6 +124,7 @@ func NewClient(conf config.TelegramConfig, jid string, component *xmpp.Component jid: jid, Session: session, resources: make(map[string]bool), + outbox: make(map[string]string), content: &conf.Content, cache: cache.NewCache(), options: options, diff --git a/telegram/commands.go b/telegram/commands.go index 2f879b1..53b5d75 100644 --- a/telegram/commands.go +++ b/telegram/commands.go @@ -513,11 +513,14 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) content := c.PrepareOutgoingMessageContent(rawCmdArguments(cmdline, 0)) if content != nil { - c.client.EditMessageText(&client.EditMessageTextRequest{ + _, err = c.client.EditMessageText(&client.EditMessageTextRequest{ ChatId: chatID, MessageId: message.Id, InputMessageContent: content, }) + if err != nil { + return "Message editing error", true + } } else { return "Message processing error", true } @@ -650,7 +653,7 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) } if messages != nil && messages.Messages != nil { for _, message := range messages.Messages { - c.ProcessIncomingMessage(targetChatId, message) + c.ProcessIncomingMessage(targetChatId, message, "") } } // print vCard diff --git a/telegram/handlers.go b/telegram/handlers.go index 65e5f2f..8173ecd 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -203,26 +203,30 @@ func (c *Client) updateChatLastMessage(update *client.UpdateChatLastMessage) { // message received func (c *Client) updateNewMessage(update *client.UpdateNewMessage) { - go func() { - chatId := update.Message.ChatId + chatId := update.Message.ChatId + + c.SendMessageLock.Lock() + c.SendMessageLock.Unlock() + xmppId, err := gateway.IdsDB.GetByTgIds(c.Session.Login, c.jid, chatId, update.Message.Id) + var ignoredResource string + if err == nil { + ignoredResource = c.popFromOutbox(xmppId) + } else { + log.Infof("Couldn't retrieve XMPP message ids for %v, an echo may happen", update.Message.Id) + } + log.Warnf("xmppId: %v, ignoredResource: %v", xmppId, ignoredResource) - // guarantee sequential message delivering per chat - lock := c.getChatMessageLock(chatId) + // guarantee sequential message delivering per chat + lock := c.getChatMessageLock(chatId) + go func() { lock.Lock() defer lock.Unlock() - // ignore self outgoing messages - if update.Message.IsOutgoing && - update.Message.SendingState != nil && - update.Message.SendingState.MessageSendingStateType() == client.TypeMessageSendingStatePending { - return - } - log.WithFields(log.Fields{ "chat_id": chatId, }).Warn("New message from chat") - c.ProcessIncomingMessage(chatId, update.Message) + c.ProcessIncomingMessage(chatId, update.Message, ignoredResource) }() } diff --git a/telegram/utils.go b/telegram/utils.go index a9e0bc8..9664857 100644 --- a/telegram/utils.go +++ b/telegram/utils.go @@ -890,9 +890,34 @@ func (c *Client) ensureDownloadFile(file *client.File) *client.File { } // ProcessIncomingMessage transfers a message to XMPP side and marks it as read on Telegram side -func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) { - var text, oob, auxText string +func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message, ignoredResource string) { + var jids []string + var isPM bool var err error + if gateway.MessageOutgoingPermission && c.Session.Carbons { + isPM, err = c.IsPM(chatId) + if err != nil { + log.Errorf("Could not determine if chat is PM: %v", err) + } + } + isOutgoing := message.IsOutgoing + isCarbon := isPM && isOutgoing + log.Warnf("isOutgoing: %v", isOutgoing) + if isOutgoing { + for resource := range c.resourcesRange() { + if ignoredResource == "" || resource != ignoredResource { + jids = append(jids, c.jid+"/"+resource) + } + } + if len(jids) == 0 { + log.Info("The only resource is ignored, aborting") + return + } + } else { + jids = []string{c.jid} + } + + var text, oob, auxText string reply, replyMsg := c.getMessageReply(message) @@ -965,27 +990,10 @@ func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) { sId := strconv.FormatInt(message.Id, 10) sChatId := strconv.FormatInt(chatId, 10) - var jids []string - var isPM bool - if gateway.MessageOutgoingPermission && c.Session.Carbons { - isPM, err = c.IsPM(chatId) - if err != nil { - log.Errorf("Could not determine if chat is PM: %v", err) - } - } - isOutgoing := isPM && message.IsOutgoing - if isOutgoing { - for resource := range c.resourcesRange() { - jids = append(jids, c.jid+"/"+resource) - } - } else { - jids = []string{c.jid} - } - for _, jid := range jids { - gateway.SendMessageWithOOB(jid, sChatId, text, sId, c.xmpp, reply, oob, isOutgoing) + gateway.SendMessageWithOOB(jid, sChatId, text, sId, c.xmpp, reply, oob, isCarbon) if auxText != "" { - gateway.SendMessage(jid, sChatId, auxText, sId, c.xmpp, reply, isOutgoing) + gateway.SendMessage(jid, sChatId, auxText, sId, c.xmpp, reply, isCarbon) } } } @@ -1172,9 +1180,12 @@ func (c *Client) resourcesRange() chan string { // resend statuses to (to another resource, for example) func (c *Client) roster(resource string) { + c.locks.resourcesLock.Lock() if _, ok := c.resources[resource]; ok { + c.locks.resourcesLock.Unlock() return // we know it } + c.locks.resourcesLock.Unlock() log.Warnf("Sending roster for %v", resource) @@ -1347,3 +1358,24 @@ func (c *Client) UpdateChatNicknames() { } } } + +// AddToOutbox remembers the resource from which a message with given ID was sent +func (c *Client) AddToOutbox(xmppId, resource string) { + c.locks.outboxLock.Lock() + defer c.locks.outboxLock.Unlock() + + c.outbox[xmppId] = resource +} + +func (c *Client) popFromOutbox(xmppId string) string { + c.locks.outboxLock.Lock() + defer c.locks.outboxLock.Unlock() + + resource, ok := c.outbox[xmppId] + if ok { + delete(c.outbox, xmppId) + } else { + log.Warnf("No %v xmppId in outbox", xmppId) + } + return resource +} diff --git a/xmpp/gateway/gateway.go b/xmpp/gateway/gateway.go index 29c8a07..7e54ee5 100644 --- a/xmpp/gateway/gateway.go +++ b/xmpp/gateway/gateway.go @@ -42,8 +42,8 @@ var DirtySessions = false var MessageOutgoingPermission = false // SendMessage creates and sends a message stanza -func SendMessage(to string, from string, body string, id string, component *xmpp.Component, reply *Reply, isOutgoing bool) { - sendMessageWrapper(to, from, body, id, component, reply, "", isOutgoing) +func SendMessage(to string, from string, body string, id string, component *xmpp.Component, reply *Reply, isCarbon bool) { + sendMessageWrapper(to, from, body, id, component, reply, "", isCarbon) } // SendServiceMessage creates and sends a simple message stanza from transport @@ -57,11 +57,11 @@ func SendTextMessage(to string, from string, body string, component *xmpp.Compon } // SendMessageWithOOB creates and sends a message stanza with OOB URL -func SendMessageWithOOB(to string, from string, body string, id string, component *xmpp.Component, reply *Reply, oob string, isOutgoing bool) { - sendMessageWrapper(to, from, body, id, component, reply, oob, isOutgoing) +func SendMessageWithOOB(to string, from string, body string, id string, component *xmpp.Component, reply *Reply, oob string, isCarbon bool) { + sendMessageWrapper(to, from, body, id, component, reply, oob, isCarbon) } -func sendMessageWrapper(to string, from string, body string, id string, component *xmpp.Component, reply *Reply, oob string, isOutgoing bool) { +func sendMessageWrapper(to string, from string, body string, id string, component *xmpp.Component, reply *Reply, oob string, isCarbon bool) { toJid, err := stanza.NewJid(to) if err != nil { log.WithFields(log.Fields{ @@ -83,7 +83,7 @@ func sendMessageWrapper(to string, from string, body string, id string, componen logFrom = from messageFrom = from + "@" + componentJid } - if isOutgoing { + if isCarbon { messageTo = messageFrom messageFrom = bareTo + "/" + Jid.Resource } else { @@ -120,7 +120,7 @@ func sendMessageWrapper(to string, from string, body string, id string, componen } } - if isOutgoing { + if isCarbon { carbonMessage := extensions.ClientMessage{ Attrs: stanza.Attrs{ From: bareTo, diff --git a/xmpp/handlers.go b/xmpp/handlers.go index 1286914..e6671bc 100644 --- a/xmpp/handlers.go +++ b/xmpp/handlers.go @@ -167,6 +167,8 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) { } } + session.SendMessageLock.Lock() + defer session.SendMessageLock.Unlock() tgMessageId := session.ProcessOutgoingMessage(toID, text, msg.From, replyId, replaceId) if tgMessageId != 0 { if replaceId != 0 { @@ -181,6 +183,7 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) { log.Errorf("Failed to save ids %v/%v %v", toID, tgMessageId, msg.Id) } } + session.AddToOutbox(msg.Id, resource) } else { /* // if a message failed to edit on Telegram side, match new XMPP ID with old Telegram ID anyway -- cgit v1.2.3 From e954c73bd2d881bc448b6bb2b3cb3a4ad37d0139 Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Sat, 15 Jul 2023 21:38:10 -0400 Subject: Do not ack with edited message to the XEP-0308 sender resource --- telegram/handlers.go | 23 +++++++++++++++++++++-- telegram/utils.go | 33 +++++++++++++++++++-------------- xmpp/handlers.go | 3 ++- 3 files changed, 42 insertions(+), 17 deletions(-) (limited to 'telegram/handlers.go') diff --git a/telegram/handlers.go b/telegram/handlers.go index 8173ecd..abc1f5d 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -214,7 +214,6 @@ func (c *Client) updateNewMessage(update *client.UpdateNewMessage) { } else { log.Infof("Couldn't retrieve XMPP message ids for %v, an echo may happen", update.Message.Id) } - log.Warnf("xmppId: %v, ignoredResource: %v", xmppId, ignoredResource) // guarantee sequential message delivering per chat lock := c.getChatMessageLock(chatId) @@ -233,6 +232,24 @@ func (c *Client) updateNewMessage(update *client.UpdateNewMessage) { // message content updated func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { markupFunction := formatter.EntityToXEP0393 + + c.SendMessageLock.Lock() + c.SendMessageLock.Unlock() + xmppId, err := gateway.IdsDB.GetByTgIds(c.Session.Login, c.jid, update.ChatId, update.MessageId) + var ignoredResource string + if err == nil { + ignoredResource = c.popFromOutbox(xmppId) + } else { + log.Infof("Couldn't retrieve XMPP message ids for %v, an echo may happen", update.MessageId) + } + log.Infof("ignoredResource: %v", ignoredResource) + + jids := c.getCarbonFullJids(true, ignoredResource) + if len(jids) == 0 { + log.Info("The only resource is ignored, aborting") + return + } + if update.NewContent.MessageContentType() == client.TypeMessageText { textContent := update.NewContent.(*client.MessageText) var editChar string @@ -246,7 +263,9 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { textContent.Text.Entities, markupFunction, )) - gateway.SendMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, "e"+strconv.FormatInt(update.MessageId, 10), c.xmpp, nil, false) + for _, jid := range jids { + gateway.SendMessage(jid, strconv.FormatInt(update.ChatId, 10), text, "e"+strconv.FormatInt(update.MessageId, 10), c.xmpp, nil, false) + } } } diff --git a/telegram/utils.go b/telegram/utils.go index 9664857..9486349 100644 --- a/telegram/utils.go +++ b/telegram/utils.go @@ -891,7 +891,6 @@ func (c *Client) ensureDownloadFile(file *client.File) *client.File { // ProcessIncomingMessage transfers a message to XMPP side and marks it as read on Telegram side func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message, ignoredResource string) { - var jids []string var isPM bool var err error if gateway.MessageOutgoingPermission && c.Session.Carbons { @@ -900,21 +899,13 @@ func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message, i log.Errorf("Could not determine if chat is PM: %v", err) } } + isOutgoing := message.IsOutgoing isCarbon := isPM && isOutgoing - log.Warnf("isOutgoing: %v", isOutgoing) - if isOutgoing { - for resource := range c.resourcesRange() { - if ignoredResource == "" || resource != ignoredResource { - jids = append(jids, c.jid+"/"+resource) - } - } - if len(jids) == 0 { - log.Info("The only resource is ignored, aborting") - return - } - } else { - jids = []string{c.jid} + jids := c.getCarbonFullJids(isOutgoing, ignoredResource) + if len(jids) == 0 { + log.Info("The only resource is ignored, aborting") + return } var text, oob, auxText string @@ -1379,3 +1370,17 @@ func (c *Client) popFromOutbox(xmppId string) string { } return resource } + +func (c *Client) getCarbonFullJids(isOutgoing bool, ignoredResource string) []string { + var jids []string + if isOutgoing { + for resource := range c.resourcesRange() { + if ignoredResource == "" || resource != ignoredResource { + jids = append(jids, c.jid+"/"+resource) + } + } + } else { + jids = []string{c.jid} + } + return jids +} diff --git a/xmpp/handlers.go b/xmpp/handlers.go index e6671bc..c5ec029 100644 --- a/xmpp/handlers.go +++ b/xmpp/handlers.go @@ -177,13 +177,14 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) { if err != nil { log.Errorf("Failed to replace id %v with %v %v", replace.Id, msg.Id, tgMessageId) } */ + session.AddToOutbox(replace.Id, resource) } else { err = gateway.IdsDB.Set(session.Session.Login, bare, toID, tgMessageId, msg.Id) if err != nil { log.Errorf("Failed to save ids %v/%v %v", toID, tgMessageId, msg.Id) } + session.AddToOutbox(msg.Id, resource) } - session.AddToOutbox(msg.Id, resource) } else { /* // if a message failed to edit on Telegram side, match new XMPP ID with old Telegram ID anyway -- cgit v1.2.3 From 563cb2d624598efdd3819daef00c64079d8a20e1 Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Sun, 16 Jul 2023 08:19:11 -0400 Subject: Avoid webpage preview updates being sent as message edits --- telegram/client.go | 3 +++ telegram/handlers.go | 20 ++++++++++++++++++++ telegram/utils.go | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) (limited to 'telegram/handlers.go') diff --git a/telegram/client.go b/telegram/client.go index 61d46aa..5cc15a4 100644 --- a/telegram/client.go +++ b/telegram/client.go @@ -53,6 +53,7 @@ type Client struct { Session *persistence.Session resources map[string]bool outbox map[string]string + editQueue map[ChatMessageId]bool content *config.TelegramContentConfig cache *cache.Cache online bool @@ -69,6 +70,7 @@ type clientLocks struct { chatMessageLocks map[int64]*sync.Mutex resourcesLock sync.Mutex outboxLock sync.Mutex + editQueueLock sync.Mutex } // NewClient instantiates a Telegram App @@ -125,6 +127,7 @@ func NewClient(conf config.TelegramConfig, jid string, component *xmpp.Component Session: session, resources: make(map[string]bool), outbox: make(map[string]string), + editQueue: make(map[ChatMessageId]bool), content: &conf.Content, cache: cache.NewCache(), options: options, diff --git a/telegram/handlers.go b/telegram/handlers.go index abc1f5d..93ac284 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -104,6 +104,13 @@ func (c *Client) updateHandler() { } c.updateNewMessage(typedUpdate) log.Debugf("%#v", typedUpdate.Message) + case client.TypeUpdateMessageEdited: + typedUpdate, ok := update.(*client.UpdateMessageEdited) + if !ok { + uhOh() + } + c.updateMessageEdited(typedUpdate) + log.Debugf("%#v", typedUpdate) case client.TypeUpdateMessageContent: typedUpdate, ok := update.(*client.UpdateMessageContent) if !ok { @@ -229,6 +236,11 @@ func (c *Client) updateNewMessage(update *client.UpdateNewMessage) { }() } +// message content edited +func (c *Client) updateMessageEdited(update *client.UpdateMessageEdited) { + c.addToEditQueue(update.ChatId, update.MessageId) +} + // message content updated func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { markupFunction := formatter.EntityToXEP0393 @@ -244,6 +256,14 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { } log.Infof("ignoredResource: %v", ignoredResource) + if !c.deleteFromEditQueue(update.ChatId, update.MessageId) { + log.WithFields(log.Fields{ + "chatId": update.ChatId, + "messageId": update.MessageId, + }).Infof("Content update with no preceding message edit, ignoring") + return + } + jids := c.getCarbonFullJids(true, ignoredResource) if len(jids) == 0 { log.Info("The only resource is ignored, aborting") diff --git a/telegram/utils.go b/telegram/utils.go index 9486349..da66189 100644 --- a/telegram/utils.go +++ b/telegram/utils.go @@ -24,6 +24,7 @@ import ( "github.com/zelenin/go-tdlib/client" ) +// VCardInfo contains intermediate data to produce a vCard type VCardInfo struct { Fn string Photo *client.File @@ -34,6 +35,12 @@ type VCardInfo struct { Info string } +// ChatMessageId uniquely identifies a Telegram message +type ChatMessageId struct { + ChatId int64 + MessageId int64 +} + var errOffline = errors.New("TDlib instance is offline") var spaceRegex = regexp.MustCompile(`\s+`) @@ -1384,3 +1391,29 @@ func (c *Client) getCarbonFullJids(isOutgoing bool, ignoredResource string) []st } return jids } + +func (c *Client) addToEditQueue(chatId, messageId int64) { + c.locks.editQueueLock.Lock() + defer c.locks.editQueueLock.Unlock() + + c.editQueue[ChatMessageId{ + ChatId: chatId, + MessageId: messageId, + }] = true +} + +func (c *Client) deleteFromEditQueue(chatId, messageId int64) bool { + c.locks.editQueueLock.Lock() + defer c.locks.editQueueLock.Unlock() + + key := ChatMessageId{ + ChatId: chatId, + MessageId: messageId, + } + _, ok := c.editQueue[key] + if ok { + delete(c.editQueue, key) + } + + return ok +} -- cgit v1.2.3 From eadef987be11dc22a89c2aad990814bd89add770 Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Fri, 21 Jul 2023 07:45:44 -0400 Subject: Revert "Avoid webpage preview updates being sent as message edits" This reverts commit 563cb2d624598efdd3819daef00c64079d8a20e1. --- telegram/client.go | 3 --- telegram/handlers.go | 20 -------------------- telegram/utils.go | 33 --------------------------------- 3 files changed, 56 deletions(-) (limited to 'telegram/handlers.go') diff --git a/telegram/client.go b/telegram/client.go index 5cc15a4..61d46aa 100644 --- a/telegram/client.go +++ b/telegram/client.go @@ -53,7 +53,6 @@ type Client struct { Session *persistence.Session resources map[string]bool outbox map[string]string - editQueue map[ChatMessageId]bool content *config.TelegramContentConfig cache *cache.Cache online bool @@ -70,7 +69,6 @@ type clientLocks struct { chatMessageLocks map[int64]*sync.Mutex resourcesLock sync.Mutex outboxLock sync.Mutex - editQueueLock sync.Mutex } // NewClient instantiates a Telegram App @@ -127,7 +125,6 @@ func NewClient(conf config.TelegramConfig, jid string, component *xmpp.Component Session: session, resources: make(map[string]bool), outbox: make(map[string]string), - editQueue: make(map[ChatMessageId]bool), content: &conf.Content, cache: cache.NewCache(), options: options, diff --git a/telegram/handlers.go b/telegram/handlers.go index 93ac284..abc1f5d 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -104,13 +104,6 @@ func (c *Client) updateHandler() { } c.updateNewMessage(typedUpdate) log.Debugf("%#v", typedUpdate.Message) - case client.TypeUpdateMessageEdited: - typedUpdate, ok := update.(*client.UpdateMessageEdited) - if !ok { - uhOh() - } - c.updateMessageEdited(typedUpdate) - log.Debugf("%#v", typedUpdate) case client.TypeUpdateMessageContent: typedUpdate, ok := update.(*client.UpdateMessageContent) if !ok { @@ -236,11 +229,6 @@ func (c *Client) updateNewMessage(update *client.UpdateNewMessage) { }() } -// message content edited -func (c *Client) updateMessageEdited(update *client.UpdateMessageEdited) { - c.addToEditQueue(update.ChatId, update.MessageId) -} - // message content updated func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { markupFunction := formatter.EntityToXEP0393 @@ -256,14 +244,6 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { } log.Infof("ignoredResource: %v", ignoredResource) - if !c.deleteFromEditQueue(update.ChatId, update.MessageId) { - log.WithFields(log.Fields{ - "chatId": update.ChatId, - "messageId": update.MessageId, - }).Infof("Content update with no preceding message edit, ignoring") - return - } - jids := c.getCarbonFullJids(true, ignoredResource) if len(jids) == 0 { log.Info("The only resource is ignored, aborting") diff --git a/telegram/utils.go b/telegram/utils.go index da66189..9486349 100644 --- a/telegram/utils.go +++ b/telegram/utils.go @@ -24,7 +24,6 @@ import ( "github.com/zelenin/go-tdlib/client" ) -// VCardInfo contains intermediate data to produce a vCard type VCardInfo struct { Fn string Photo *client.File @@ -35,12 +34,6 @@ type VCardInfo struct { Info string } -// ChatMessageId uniquely identifies a Telegram message -type ChatMessageId struct { - ChatId int64 - MessageId int64 -} - var errOffline = errors.New("TDlib instance is offline") var spaceRegex = regexp.MustCompile(`\s+`) @@ -1391,29 +1384,3 @@ func (c *Client) getCarbonFullJids(isOutgoing bool, ignoredResource string) []st } return jids } - -func (c *Client) addToEditQueue(chatId, messageId int64) { - c.locks.editQueueLock.Lock() - defer c.locks.editQueueLock.Unlock() - - c.editQueue[ChatMessageId{ - ChatId: chatId, - MessageId: messageId, - }] = true -} - -func (c *Client) deleteFromEditQueue(chatId, messageId int64) bool { - c.locks.editQueueLock.Lock() - defer c.locks.editQueueLock.Unlock() - - key := ChatMessageId{ - ChatId: chatId, - MessageId: messageId, - } - _, ok := c.editQueue[key] - if ok { - delete(c.editQueue, key) - } - - return ok -} -- cgit v1.2.3 From 748366ad6a9dc4b2a269d5499ae1d5d7e8526762 Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Sat, 22 Jul 2023 10:46:35 -0400 Subject: Avoid webpage preview updates being sent as message edits (by hash matching) --- telegram/client.go | 7 +++++++ telegram/handlers.go | 6 +++++- telegram/utils.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) (limited to 'telegram/handlers.go') diff --git a/telegram/client.go b/telegram/client.go index 61d46aa..5b0217a 100644 --- a/telegram/client.go +++ b/telegram/client.go @@ -2,6 +2,7 @@ package telegram import ( "github.com/pkg/errors" + "hash/maphash" "path/filepath" "strconv" "sync" @@ -60,6 +61,9 @@ type Client struct { DelayedStatuses map[int64]*DelayedStatus DelayedStatusesLock sync.Mutex + lastMsgHashes map[int64]uint64 + msgHashSeed maphash.Seed + locks clientLocks SendMessageLock sync.Mutex } @@ -69,6 +73,7 @@ type clientLocks struct { chatMessageLocks map[int64]*sync.Mutex resourcesLock sync.Mutex outboxLock sync.Mutex + lastMsgHashesLock sync.Mutex } // NewClient instantiates a Telegram App @@ -129,6 +134,8 @@ func NewClient(conf config.TelegramConfig, jid string, component *xmpp.Component cache: cache.NewCache(), options: options, DelayedStatuses: make(map[int64]*DelayedStatus), + lastMsgHashes: make(map[int64]uint64), + msgHashSeed: maphash.MakeSeed(), locks: clientLocks{ chatMessageLocks: make(map[int64]*sync.Mutex), }, diff --git a/telegram/handlers.go b/telegram/handlers.go index abc1f5d..cc6e635 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -226,6 +226,8 @@ func (c *Client) updateNewMessage(update *client.UpdateNewMessage) { }).Warn("New message from chat") c.ProcessIncomingMessage(chatId, update.Message, ignoredResource) + + c.updateLastMessageHash(update.Message.ChatId, update.Message.Id, update.Message.Content) }() } @@ -233,6 +235,8 @@ func (c *Client) updateNewMessage(update *client.UpdateNewMessage) { func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { markupFunction := formatter.EntityToXEP0393 + defer c.updateLastMessageHash(update.ChatId, update.MessageId, update.NewContent) + c.SendMessageLock.Lock() c.SendMessageLock.Unlock() xmppId, err := gateway.IdsDB.GetByTgIds(c.Session.Login, c.jid, update.ChatId, update.MessageId) @@ -250,7 +254,7 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { return } - if update.NewContent.MessageContentType() == client.TypeMessageText { + if update.NewContent.MessageContentType() == client.TypeMessageText && c.hasLastMessageHashChanged(update.ChatId, update.MessageId, update.NewContent) { textContent := update.NewContent.(*client.MessageText) var editChar string if c.Session.AsciiArrows { diff --git a/telegram/utils.go b/telegram/utils.go index 9486349..4835696 100644 --- a/telegram/utils.go +++ b/telegram/utils.go @@ -2,8 +2,10 @@ package telegram import ( "crypto/sha1" + "encoding/binary" "fmt" "github.com/pkg/errors" + "hash/maphash" "io" "io/ioutil" "net/http" @@ -1384,3 +1386,58 @@ func (c *Client) getCarbonFullJids(isOutgoing bool, ignoredResource string) []st } return jids } + +func (c *Client) calculateMessageHash(messageId int64, content client.MessageContent) uint64 { + var h maphash.Hash + h.SetSeed(c.msgHashSeed) + + buf8 := make([]byte, 8) + binary.BigEndian.PutUint64(buf8, uint64(messageId)) + h.Write(buf8) + + if content != nil && content.MessageContentType() == client.TypeMessageText { + textContent, ok := content.(*client.MessageText) + if !ok { + uhOh() + } + + if textContent.Text != nil { + h.WriteString(textContent.Text.Text) + for _, entity := range textContent.Text.Entities { + buf4 := make([]byte, 4) + binary.BigEndian.PutUint32(buf4, uint32(entity.Offset)) + h.Write(buf4) + binary.BigEndian.PutUint32(buf4, uint32(entity.Length)) + h.Write(buf4) + h.WriteString(entity.Type.TextEntityTypeType()) + } + } + } + + return h.Sum64() +} + +func (c *Client) updateLastMessageHash(chatId, messageId int64, content client.MessageContent) { + c.locks.lastMsgHashesLock.Lock() + defer c.locks.lastMsgHashesLock.Unlock() + + c.lastMsgHashes[chatId] = c.calculateMessageHash(messageId, content) +} + +func (c *Client) hasLastMessageHashChanged(chatId, messageId int64, content client.MessageContent) bool { + c.locks.lastMsgHashesLock.Lock() + defer c.locks.lastMsgHashesLock.Unlock() + + oldHash, ok := c.lastMsgHashes[chatId] + newHash := c.calculateMessageHash(messageId, content) + + if !ok { + log.Warnf("Last message hash for chat %v does not exist", chatId) + } + log.WithFields(log.Fields{ + "old hash": oldHash, + "new hash": newHash, + }).Info("Message hashes") + + return !ok || oldHash != newHash +} -- cgit v1.2.3 From ef831fc9725601a94149ce94c3fb686afc77e0a5 Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Mon, 31 Jul 2023 21:25:24 -0400 Subject: Migrate to TDLib 1.8.14 (multiple usernames support) --- go.mod | 1 + go.sum | 2 ++ telegram/client.go | 4 +-- telegram/commands.go | 26 ++++++++++------ telegram/connect.go | 12 ++------ telegram/handlers.go | 2 +- telegram/utils.go | 80 ++++++++++++++++++++++++++++++++++++++------------ telegram/utils_test.go | 47 +++++++++++++++++++++++++++++ xmpp/handlers.go | 8 ++--- 9 files changed, 138 insertions(+), 44 deletions(-) (limited to 'telegram/handlers.go') diff --git a/go.mod b/go.mod index a4e26d2..db7c380 100644 --- a/go.mod +++ b/go.mod @@ -34,3 +34,4 @@ require ( ) replace gosrc.io/xmpp => dev.narayana.im/narayana/go-xmpp v0.0.0-20220524203317-306b4ff58e8f +replace github.com/zelenin/go-tdlib => dev.narayana.im/narayana/go-tdlib v0.0.0-20230730021136-47da33180615 diff --git a/go.sum b/go.sum index 011bc93..2565582 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +dev.narayana.im/narayana/go-tdlib v0.0.0-20230730021136-47da33180615 h1:RRUZJSro+k8FkazNx7QEYLVoO4wZtchvsd0Y2RBWjeU= +dev.narayana.im/narayana/go-tdlib v0.0.0-20230730021136-47da33180615/go.mod h1:Xs8fXbk5n7VaPyrSs9DP7QYoBScWYsjX+lUcWmx1DIU= dev.narayana.im/narayana/go-xmpp v0.0.0-20220524203317-306b4ff58e8f h1:6249ajbMjgYz53Oq0IjTvjHXbxTfu29Mj1J/6swRHs4= dev.narayana.im/narayana/go-xmpp v0.0.0-20220524203317-306b4ff58e8f/go.mod h1:L3NFMqYOxyLz3JGmgFyWf7r9htE91zVGiK40oW4RwdY= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= diff --git a/telegram/client.go b/telegram/client.go index 5b0217a..e9acd20 100644 --- a/telegram/client.go +++ b/telegram/client.go @@ -45,7 +45,7 @@ type DelayedStatus struct { type Client struct { client *client.Client authorizer *clientAuthorizer - parameters *client.TdlibParameters + parameters *client.SetTdlibParametersRequest options []client.Option me *client.User @@ -100,7 +100,7 @@ func NewClient(conf config.TelegramConfig, jid string, component *xmpp.Component datadir = "./sessions/" // ye olde defaute } - parameters := client.TdlibParameters{ + parameters := client.SetTdlibParametersRequest{ UseTestDc: false, DatabaseDirectory: filepath.Join(datadir, jid), diff --git a/telegram/commands.go b/telegram/commands.go index 53b5d75..3828ec2 100644 --- a/telegram/commands.go +++ b/telegram/commands.go @@ -18,8 +18,7 @@ const notEnoughArguments string = "Not enough arguments" const telegramNotInitialized string = "Telegram connection is not initialized yet" const notOnline string = "Not online" -var permissionsAdmin = client.ChatMemberStatusAdministrator{ - CanBeEdited: true, +var permissionsAdmin = client.ChatAdministratorRights{ CanChangeInfo: true, CanPostMessages: true, CanEditMessages: true, @@ -30,14 +29,20 @@ var permissionsAdmin = client.ChatMemberStatusAdministrator{ CanPromoteMembers: false, } var permissionsMember = client.ChatPermissions{ - CanSendMessages: true, - CanSendMediaMessages: true, + CanSendBasicMessages: true, + CanSendAudios: true, + CanSendDocuments: true, + CanSendPhotos: true, + CanSendVideos: true, + CanSendVideoNotes: true, + CanSendVoiceNotes: true, CanSendPolls: true, CanSendOtherMessages: true, CanAddWebPagePreviews: true, CanChangeInfo: true, CanInviteUsers: true, CanPinMessages: true, + CanManageTopics: true, } var permissionsReadonly = client.ChatPermissions{} @@ -666,7 +671,7 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) entries := []string{ keyValueString("Chat title", info.Fn), keyValueString("Photo", link), - keyValueString("Username", info.Nickname), + keyValueString("Usernames", c.usernamesToString(info.Nicknames)), keyValueString("Full name", info.Given+" "+info.Family), keyValueString("Phone number", info.Tel), } @@ -883,7 +888,10 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) } // clone the permissions - status := permissionsAdmin + status := client.ChatMemberStatusAdministrator{ + CanBeEdited: true, + Rights: &permissionsAdmin, + } if len(args) > 1 { status.CustomTitle = args[1] @@ -933,9 +941,9 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) return "Invalid TTL", true } } - _, err = c.client.SetChatMessageTtl(&client.SetChatMessageTtlRequest{ - ChatId: chatID, - Ttl: int32(ttl), + _, err = c.client.SetChatMessageAutoDeleteTime(&client.SetChatMessageAutoDeleteTimeRequest{ + ChatId: chatID, + MessageAutoDeleteTime: int32(ttl), }) if err != nil { diff --git a/telegram/connect.go b/telegram/connect.go index 8324319..ef03428 100644 --- a/telegram/connect.go +++ b/telegram/connect.go @@ -13,7 +13,7 @@ import ( const chatsLimit int32 = 999 type clientAuthorizer struct { - TdlibParameters chan *client.TdlibParameters + TdlibParameters chan *client.SetTdlibParametersRequest PhoneNumber chan string Code chan string State chan client.AuthorizationState @@ -31,13 +31,7 @@ func (stateHandler *clientAuthorizer) Handle(c *client.Client, state client.Auth switch state.AuthorizationStateType() { case client.TypeAuthorizationStateWaitTdlibParameters: - _, err := c.SetTdlibParameters(&client.SetTdlibParametersRequest{ - Parameters: <-stateHandler.TdlibParameters, - }) - return err - - case client.TypeAuthorizationStateWaitEncryptionKey: - _, err := c.CheckDatabaseEncryptionKey(&client.CheckDatabaseEncryptionKeyRequest{}) + _, err := c.SetTdlibParameters(<-stateHandler.TdlibParameters) return err case client.TypeAuthorizationStateWaitPhoneNumber: @@ -116,7 +110,7 @@ func (c *Client) Connect(resource string) error { log.Warn("Connecting to Telegram network...") c.authorizer = &clientAuthorizer{ - TdlibParameters: make(chan *client.TdlibParameters, 1), + TdlibParameters: make(chan *client.SetTdlibParametersRequest, 1), PhoneNumber: make(chan string, 1), Code: make(chan string, 1), State: make(chan client.AuthorizationState, 10), diff --git a/telegram/handlers.go b/telegram/handlers.go index cc6e635..cedea63 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -233,7 +233,7 @@ func (c *Client) updateNewMessage(update *client.UpdateNewMessage) { // message content updated func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { - markupFunction := formatter.EntityToXEP0393 + markupFunction := c.getFormatter() defer c.updateLastMessageHash(update.ChatId, update.MessageId, update.NewContent) diff --git a/telegram/utils.go b/telegram/utils.go index 4835696..e1e9317 100644 --- a/telegram/utils.go +++ b/telegram/utils.go @@ -27,13 +27,13 @@ import ( ) type VCardInfo struct { - Fn string - Photo *client.File - Nickname string - Given string - Family string - Tel string - Info string + Fn string + Photo *client.File + Nicknames []string + Given string + Family string + Tel string + Info string } var errOffline = errors.New("TDlib instance is offline") @@ -286,12 +286,15 @@ func (c *Client) formatContact(chatID int64) string { if chat != nil { str = fmt.Sprintf("%s (%v)", chat.Title, chat.Id) } else if user != nil { - username := user.Username - if username == "" { - username = strconv.FormatInt(user.Id, 10) + var usernames string + if user.Usernames != nil { + usernames = c.usernamesToString(user.Usernames.ActiveUsernames) + } + if usernames == "" { + usernames = strconv.FormatInt(user.Id, 10) } - str = fmt.Sprintf("%s %s (%v)", user.FirstName, user.LastName, username) + str = fmt.Sprintf("%s %s (%v)", user.FirstName, user.LastName, usernames) } else { str = strconv.FormatInt(chatID, 10) } @@ -566,7 +569,7 @@ func (c *Client) messageToText(message *client.Message, preview bool) string { return "" } - markupFunction := formatter.EntityToXEP0393 + markupFunction := c.getFormatter() switch message.Content.MessageContentType() { case client.TypeMessageSticker: sticker, _ := message.Content.(*client.MessageSticker) @@ -737,6 +740,22 @@ func (c *Client) messageToText(message *client.Message, preview bool) string { return strings.Join(rows, "\n") } + case client.TypeMessageChatSetMessageAutoDeleteTime: + ttl, _ := message.Content.(*client.MessageChatSetMessageAutoDeleteTime) + name := c.formatContact(ttl.FromUserId) + if name == "" { + if ttl.MessageAutoDeleteTime == 0 { + return "The self-destruct timer was disabled" + } else { + return fmt.Sprintf("The self-destruct timer was set to %v seconds", ttl.MessageAutoDeleteTime) + } + } else { + if ttl.MessageAutoDeleteTime == 0 { + return fmt.Sprintf("%s disabled the self-destruct timer", name) + } else { + return fmt.Sprintf("%s set the self-destruct timer to %v seconds", name, ttl.MessageAutoDeleteTime) + } + } } return fmt.Sprintf("unknown message (%s)", message.Content.MessageContentType()) @@ -751,7 +770,7 @@ func (c *Client) contentToFile(content client.MessageContent) (*client.File, *cl case client.TypeMessageSticker: sticker, _ := content.(*client.MessageSticker) file := sticker.Sticker.Sticker - if sticker.Sticker.IsAnimated && sticker.Sticker.Thumbnail != nil && sticker.Sticker.Thumbnail.File != nil { + if sticker.Sticker.Format.StickerFormatType() != client.TypeStickerTypeRegular && sticker.Sticker.Thumbnail != nil && sticker.Sticker.Thumbnail.File != nil { file = sticker.Sticker.Thumbnail.File } return file, nil @@ -1192,7 +1211,7 @@ func (c *Client) roster(resource string) { } // get last messages from specified chat -func (c *Client) getLastMessages(id int64, query string, from int64, count int32) (*client.Messages, error) { +func (c *Client) getLastMessages(id int64, query string, from int64, count int32) (*client.FoundChatMessages, error) { return c.client.SearchChatMessages(&client.SearchChatMessagesRequest{ ChatId: id, Query: query, @@ -1245,10 +1264,18 @@ func (c *Client) GetChatDescription(chat *client.Chat) string { UserId: privateType.UserId, }) if err == nil { - if fullInfo.Bio != "" { - return fullInfo.Bio - } else if fullInfo.Description != "" { - return fullInfo.Description + if fullInfo.Bio != nil && fullInfo.Bio.Text != "" { + return formatter.Format( + fullInfo.Bio.Text, + fullInfo.Bio.Entities, + c.getFormatter(), + ) + } else if fullInfo.BotInfo != nil { + if fullInfo.BotInfo.ShortDescription != "" { + return fullInfo.BotInfo.ShortDescription + } else { + return fullInfo.BotInfo.Description + } } } else { log.Warnf("Coudln't retrieve private chat info: %v", err.Error()) @@ -1328,7 +1355,10 @@ func (c *Client) GetVcardInfo(toID int64) (VCardInfo, error) { info.Info = c.GetChatDescription(chat) } if user != nil { - info.Nickname = user.Username + if user.Usernames != nil { + info.Nicknames = make([]string, len(user.Usernames.ActiveUsernames)) + copy(info.Nicknames, user.Usernames.ActiveUsernames) + } info.Given = user.FirstName info.Family = user.LastName info.Tel = user.PhoneNumber @@ -1441,3 +1471,15 @@ func (c *Client) hasLastMessageHashChanged(chatId, messageId int64, content clie return !ok || oldHash != newHash } + +func (c *Client) getFormatter() func(*client.TextEntity) (*formatter.Insertion, *formatter.Insertion) { + return formatter.EntityToXEP0393 +} + +func (c *Client) usernamesToString(usernames []string) string { + var atUsernames []string + for _, username := range usernames { + atUsernames = append(atUsernames, "@"+username) + } + return strings.Join(atUsernames, ", ") +} diff --git a/telegram/utils_test.go b/telegram/utils_test.go index 91002ee..18be215 100644 --- a/telegram/utils_test.go +++ b/telegram/utils_test.go @@ -369,6 +369,53 @@ func TestMessageAnimation(t *testing.T) { } } +func TestMessageTtl1(t *testing.T) { + ttl := client.Message{ + Content: &client.MessageChatSetMessageAutoDeleteTime{}, + } + text := (&Client{}).messageToText(&ttl, false) + if text != "The self-destruct timer was disabled" { + t.Errorf("Wrong anonymous off ttl label: %v", text) + } +} + +func TestMessageTtl2(t *testing.T) { + ttl := client.Message{ + Content: &client.MessageChatSetMessageAutoDeleteTime{ + MessageAutoDeleteTime: 3, + }, + } + text := (&Client{}).messageToText(&ttl, false) + if text != "The self-destruct timer was set to 3 seconds" { + t.Errorf("Wrong anonymous ttl label: %v", text) + } +} + +func TestMessageTtl3(t *testing.T) { + ttl := client.Message{ + Content: &client.MessageChatSetMessageAutoDeleteTime{ + FromUserId: 3, + }, + } + text := (&Client{}).messageToText(&ttl, false) + if text != "unknown contact: TDlib instance is offline disabled the self-destruct timer" { + t.Errorf("Wrong off ttl label: %v", text) + } +} + +func TestMessageTtl4(t *testing.T) { + ttl := client.Message{ + Content: &client.MessageChatSetMessageAutoDeleteTime{ + FromUserId: 3, + MessageAutoDeleteTime: 3, + }, + } + text := (&Client{}).messageToText(&ttl, false) + if text != "unknown contact: TDlib instance is offline set the self-destruct timer to 3 seconds" { + t.Errorf("Wrong ttl label: %v", text) + } +} + func TestMessageUnknown(t *testing.T) { unknown := client.Message{ Content: &client.MessageExpiredPhoto{}, diff --git a/xmpp/handlers.go b/xmpp/handlers.go index c5ec029..fd1afad 100644 --- a/xmpp/handlers.go +++ b/xmpp/handlers.go @@ -481,7 +481,7 @@ func makeVCardPayload(typ byte, id string, info telegram.VCardInfo, session *tel vcard.Photo.Type.Text = "image/jpeg" vcard.Photo.Binval.Text = base64Photo } - vcard.Nickname.Text = info.Nickname + vcard.Nickname.Text = strings.Join(info.Nicknames, ",") vcard.N.Given.Text = info.Given vcard.N.Family.Text = info.Family vcard.Tel.Number.Text = info.Tel @@ -512,13 +512,13 @@ func makeVCardPayload(typ byte, id string, info telegram.VCardInfo, session *tel }, }) } - if info.Nickname != "" { + for _, nickname := range info.Nicknames { nodes = append(nodes, stanza.Node{ XMLName: xml.Name{Local: "nickname"}, Nodes: []stanza.Node{ stanza.Node{ XMLName: xml.Name{Local: "text"}, - Content: info.Nickname, + Content: nickname, }, }, }, stanza.Node{ @@ -526,7 +526,7 @@ func makeVCardPayload(typ byte, id string, info telegram.VCardInfo, session *tel Nodes: []stanza.Node{ stanza.Node{ XMLName: xml.Name{Local: "uri"}, - Content: "https://t.me/" + info.Nickname, + Content: "https://t.me/" + nickname, }, }, }) -- cgit v1.2.3 From 608f67551297a14e2e23603413bbce66f6ad5cd9 Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Wed, 2 Aug 2023 16:41:18 -0400 Subject: Revert sending carbons for outgoing messages to other resources (they duplicate what clients already send to each other) --- Makefile | 2 +- telegabber.go | 2 +- telegram/commands.go | 2 +- telegram/handlers.go | 19 ++++++++----------- telegram/utils.go | 11 +++-------- xmpp/handlers.go | 1 - 6 files changed, 14 insertions(+), 23 deletions(-) (limited to 'telegram/handlers.go') diff --git a/Makefile b/Makefile index 2eb17ac..6732740 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ COMMIT := $(shell git rev-parse --short HEAD) TD_COMMIT := "8517026415e75a8eec567774072cbbbbb52376c1" -VERSION := "v1.7.2" +VERSION := "v1.7.3" MAKEOPTS := "-j4" all: diff --git a/telegabber.go b/telegabber.go index 9ce070f..3d7d2ea 100644 --- a/telegabber.go +++ b/telegabber.go @@ -15,7 +15,7 @@ import ( goxmpp "gosrc.io/xmpp" ) -var version string = "1.7.2" +var version string = "1.7.3" var commit string var sm *goxmpp.StreamManager diff --git a/telegram/commands.go b/telegram/commands.go index e164ce1..206e049 100644 --- a/telegram/commands.go +++ b/telegram/commands.go @@ -658,7 +658,7 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) } if messages != nil && messages.Messages != nil { for _, message := range messages.Messages { - c.ProcessIncomingMessage(targetChatId, message, "") + c.ProcessIncomingMessage(targetChatId, message) } } // print vCard diff --git a/telegram/handlers.go b/telegram/handlers.go index cedea63..0d1cda9 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -205,27 +205,24 @@ func (c *Client) updateChatLastMessage(update *client.UpdateChatLastMessage) { func (c *Client) updateNewMessage(update *client.UpdateNewMessage) { chatId := update.Message.ChatId - c.SendMessageLock.Lock() - c.SendMessageLock.Unlock() - xmppId, err := gateway.IdsDB.GetByTgIds(c.Session.Login, c.jid, chatId, update.Message.Id) - var ignoredResource string - if err == nil { - ignoredResource = c.popFromOutbox(xmppId) - } else { - log.Infof("Couldn't retrieve XMPP message ids for %v, an echo may happen", update.Message.Id) - } - // guarantee sequential message delivering per chat lock := c.getChatMessageLock(chatId) go func() { lock.Lock() defer lock.Unlock() + // ignore self outgoing messages + if update.Message.IsOutgoing && + update.Message.SendingState != nil && + update.Message.SendingState.MessageSendingStateType() == client.TypeMessageSendingStatePending { + return + } + log.WithFields(log.Fields{ "chat_id": chatId, }).Warn("New message from chat") - c.ProcessIncomingMessage(chatId, update.Message, ignoredResource) + c.ProcessIncomingMessage(chatId, update.Message) c.updateLastMessageHash(update.Message.ChatId, update.Message.Id, update.Message.Content) }() diff --git a/telegram/utils.go b/telegram/utils.go index cd25c22..62ce945 100644 --- a/telegram/utils.go +++ b/telegram/utils.go @@ -911,7 +911,7 @@ func (c *Client) ensureDownloadFile(file *client.File) *client.File { } // ProcessIncomingMessage transfers a message to XMPP side and marks it as read on Telegram side -func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message, ignoredResource string) { +func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) { var isPM bool var err error if gateway.MessageOutgoingPermission && c.Session.Carbons { @@ -921,13 +921,8 @@ func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message, i } } - isOutgoing := message.IsOutgoing - isCarbon := isPM && isOutgoing - jids := c.getCarbonFullJids(isOutgoing, ignoredResource) - if len(jids) == 0 { - log.Info("The only resource is ignored, aborting") - return - } + isCarbon := isPM && message.IsOutgoing + jids := c.getCarbonFullJids(isCarbon, "") var text, oob, auxText string diff --git a/xmpp/handlers.go b/xmpp/handlers.go index fd1afad..4e3354e 100644 --- a/xmpp/handlers.go +++ b/xmpp/handlers.go @@ -183,7 +183,6 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) { if err != nil { log.Errorf("Failed to save ids %v/%v %v", toID, tgMessageId, msg.Id) } - session.AddToOutbox(msg.Id, resource) } } else { /* -- cgit v1.2.3