From f9f2bc8b83df433f0b3e32fe1faead6b7051f4fa Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Tue, 13 May 2025 23:52:55 -0400 Subject: [PATCH] Display incoming messages from Telegram in MUCs --- telegram/handlers.go | 30 +++++++++-- telegram/utils.go | 126 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 138 insertions(+), 18 deletions(-) diff --git a/telegram/handlers.go b/telegram/handlers.go index 021280d..e317ecb 100644 --- a/telegram/handlers.go +++ b/telegram/handlers.go @@ -309,7 +309,15 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { } log.Infof("ignoredResource: %v", ignoredResource) - jids := c.getCarbonFullJids(true, ignoredResource) + chat, _, _ := c.GetContactByID(update.ChatId, nil) + isMUC := c.Session.MUC && c.IsGroup(chat) + + var jids []string + if isMUC { + _, jids = c.getMUCJoinedJIDs(update.ChatId) + } else { + c.getCarbonFullJids(true, ignoredResource) + } if len(jids) == 0 { log.Info("The only resource is ignored, aborting") return @@ -342,7 +350,7 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { }) var prefix string if messageErr == nil { - isCarbon = c.isCarbonsEnabled() && message.IsOutgoing + isCarbon = c.isCarbonsEnabled() && message.IsOutgoing && !isMUC // reply correction support in clients is suboptimal yet, so cut them out for now prefix, _ = c.messageToPrefix(message, "", "", true) } else { @@ -370,9 +378,23 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) { markupFunction, )) - sChatId := strconv.FormatInt(update.ChatId, 10) + var from string + var originalFrom string + if isMUC { + var nickname string + if messageErr == nil { + senderId := c.getMessageSenderId(message) + nickname = c.GetMUCNickname(senderId) + originalFrom = gateway.CHATJID(senderId, true) + } else { + nickname = "#ERROR#" + } + from = gateway.MUCJID(update.ChatId) + "/" + nickname + } else { + from = gateway.CHATNODE(update.ChatId) + } for _, jid := range jids { - gateway.SendMessage(jid, sChatId, text.String(), "e"+sId, c.xmpp, nil, 0, replaceId, isCarbon, false, false, "") + gateway.SendMessage(jid, from, text.String(), "e"+sId, c.xmpp, nil, 0, replaceId, isCarbon, isMUC, false, originalFrom) } } } diff --git a/telegram/utils.go b/telegram/utils.go index c911001..935beb7 100644 --- a/telegram/utils.go +++ b/telegram/utils.go @@ -585,6 +585,47 @@ func (c *Client) sendMUCStatuses(chatID int64) { ) } +func (c *Client) mucCacheHasMember(mucID int64, memberID int64) bool { + c.locks.mucCacheLock.Lock() + defer c.locks.mucCacheLock.Unlock() + mucState, ok := c.mucCache[mucID] + if !ok || mucState == nil { + return false // no MUC to be added to + } + + _, ok = mucState.Members[memberID] + return ok +} + +func (c *Client) addMUCMember(mucID int64, memberID int64, affiliation string) bool { + c.locks.mucCacheLock.Lock() + defer c.locks.mucCacheLock.Unlock() + mucState, ok := c.mucCache[mucID] + if !ok || mucState == nil { + return false + } + + nickname := c.GetMUCNickname(memberID) + + err := c.sendPresence( + gateway.SPFrom(gateway.MUCNODE(mucID)), + gateway.SPResource(nickname), + gateway.SPImmed(true), + gateway.SPMUCAffiliation(affiliation), + gateway.SPMUCJid(gateway.CHATJID(memberID, true)), + ) + + if err == nil { + mucState.Members[memberID] = &MUCMember{ + Nickname: nickname, + Affiliation: affiliation, + } + return true + } + + return false +} + func (c *Client) sendMUCSubject(chatID int64, resource string) { pin, err := c.client.GetChatPinnedMessage(&client.GetChatPinnedMessageRequest{ ChatId: chatID, @@ -665,6 +706,24 @@ func (c *Client) MUCHasResource(chatID int64, resource string) bool { return ok } +func (c *Client) getMUCJoinedJIDs(chatId int64) (bool, []string) { + c.locks.mucCacheLock.Lock() + defer c.locks.mucCacheLock.Unlock() + + groupChatTos := []string{} + + mucState, ok := c.mucCache[chatId] + if !ok || mucState == nil { + return false, nil + } else { + for resource := range mucState.Resources { + groupChatTos = append(groupChatTos, c.jid + "/" + resource) + } + } + + return true, groupChatTos +} + // GetMyMUCNickname obtains this account's nickname in a given MUC func (c *Client) GetMyMUCNickname(chatID int64) (string, bool) { if c.me == nil { @@ -1424,7 +1483,44 @@ func (c *Client) getPrefixSeparator(chatId int64) string { // ProcessIncomingMessage is a legacy wrapper for SendMessageToGateway aiming only PM messages func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) { - c.SendMessageToGateway(chatId, message, "", false, "", []string{}) + chat, _, _ := c.GetContactByID(chatId, nil) + safeToSend := true + groupChatFrom := "" + groupChatTos := []string{} + if c.Session.MUC && c.IsGroup(chat) { + senderId := c.getMessageSenderId(message) + if senderId == 0 { + log.Errorf("Invalid sender id for message %#v", message) + return + } + + if !c.mucCacheHasMember(chatId, senderId) { + chatMember, err := c.client.GetChatMember(&client.GetChatMemberRequest{ + ChatId: chatId, + MemberId: message.SenderId, + }) + var status client.ChatMemberStatus + if err == nil { + status = chatMember.Status + } + safeToSend = c.addMUCMember(chatId, senderId, c.memberStatusToAffiliation(status)) + } + + groupChatFrom = gateway.MUCJID(chatId) + "/" + c.GetMUCNickname(senderId) + var ok bool + ok, groupChatTos = c.getMUCJoinedJIDs(chatId) + if !ok { + safeToSend = false + } + } + if safeToSend { + c.SendMessageToGateway(chatId, message, "", false, groupChatFrom, groupChatTos) + } else { + mucJID := gateway.MUCJID(chatId) + for _, to := range groupChatTos { + gateway.SendErrorMessage(to, mucJID, "Cannot show a message", 500, true, c.xmpp) + } + } } // SendMessageToGateway transfers a message to XMPP side and marks it as read on Telegram side @@ -2218,19 +2314,21 @@ func (c *Client) usernamesToString(usernames []string) string { } func (c *Client) memberStatusToAffiliation(memberStatus client.ChatMemberStatus) string { - switch memberStatus.ChatMemberStatusType() { - case client.TypeChatMemberStatusCreator: - return "owner" - case client.TypeChatMemberStatusAdministrator: - return "admin" - case client.TypeChatMemberStatusMember: - return "member" - case client.TypeChatMemberStatusRestricted: - return "outcast" - case client.TypeChatMemberStatusLeft: - return "none" - case client.TypeChatMemberStatusBanned: - return "outcast" + if memberStatus != nil { + switch memberStatus.ChatMemberStatusType() { + case client.TypeChatMemberStatusCreator: + return "owner" + case client.TypeChatMemberStatusAdministrator: + return "admin" + case client.TypeChatMemberStatusMember: + return "member" + case client.TypeChatMemberStatusRestricted: + return "outcast" + case client.TypeChatMemberStatusLeft: + return "none" + case client.TypeChatMemberStatusBanned: + return "outcast" + } } return "member" }