Move MUCs to separate c-prefixed JIDs

This commit is contained in:
Bohdan Horbeshko 2025-05-12 06:34:00 -04:00
parent b0be2ad259
commit f123f60905
4 changed files with 110 additions and 57 deletions

View file

@ -73,7 +73,7 @@ func (c *Client) sendMarker(chatId, messageId int64, typ gateway.MarkerType) {
gateway.SendMessageMarker( gateway.SendMessageMarker(
c.jid, c.jid,
strconv.FormatInt(chatId, 10), gateway.CHATNODE(chatId),
c.xmpp, c.xmpp,
typ, typ,
xmppId, xmppId,
@ -390,6 +390,12 @@ func (c *Client) updateDeleteMessages(update *client.UpdateDeleteMessages) {
} }
} }
var isGroupchat bool
chat, _, _ := c.GetContactByID(update.ChatId, nil)
if c.Session.MUC && c.IsGroup(chat) {
isGroupchat = true
}
var deleteChar string var deleteChar string
if c.Session.AsciiArrows { if c.Session.AsciiArrows {
deleteChar = "X " deleteChar = "X "
@ -397,7 +403,13 @@ func (c *Client) updateDeleteMessages(update *client.UpdateDeleteMessages) {
deleteChar = "✗ " deleteChar = "✗ "
} }
text := deleteChar + strings.Join(int64SliceToStringSlice(update.MessageIds), ",") text := deleteChar + strings.Join(int64SliceToStringSlice(update.MessageIds), ",")
gateway.SendTextMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, c.xmpp) var fromJid string
if isGroupchat {
fromJid = gateway.MUCJID(update.ChatId)
} else {
fromJid = gateway.CHATNODE(update.ChatId)
}
gateway.SendTextMessage(c.jid, fromJid, text, c.xmpp, isGroupchat)
} }
} }
@ -443,7 +455,7 @@ func (c *Client) updateChatTitle(update *client.UpdateChatTitle) {
return return
} }
gateway.SetNickname(c.jid, strconv.FormatInt(update.ChatId, 10), update.Title, c.xmpp) gateway.SetNickname(c.jid, gateway.CHATNODE(update.ChatId), update.Title, c.xmpp)
// set also the status (for group chats only) // set also the status (for group chats only)
if user == nil { if user == nil {

View file

@ -459,14 +459,13 @@ func (c *Client) ProcessStatusUpdate(chatID int64, status string, show string, o
} }
c.locks.mucCacheLock.Lock() c.locks.mucCacheLock.Lock()
chatJid := strconv.FormatInt(chatID, 10) + "@" + gateway.Jid.Full() chatJid := gateway.CHATJID(chatID, true)
for mucId, state := range c.mucCache { for mucId, state := range c.mucCache {
member, ok := state.Members[chatID] member, ok := state.Members[chatID]
if ok { if ok {
sMucId := strconv.FormatInt(mucId, 10)
newMucArgs := append( newMucArgs := append(
newArgs, newArgs,
gateway.SPFrom(sMucId), gateway.SPFrom(gateway.MUCNODE(mucId)),
gateway.SPResource(member.Nickname), gateway.SPResource(member.Nickname),
gateway.SPMUCAffiliation(member.Affiliation), gateway.SPMUCAffiliation(member.Affiliation),
gateway.SPMUCJid(chatJid), gateway.SPMUCJid(chatJid),
@ -529,7 +528,7 @@ func (c *Client) sendMUCStatuses(chatID int64) {
c.mucCache[chatID] = mucState c.mucCache[chatID] = mucState
} }
sChatId := strconv.FormatInt(chatID, 10) sChatId := gateway.MUCNODE(chatID)
myNickname := "me" myNickname := "me"
if c.me != nil { if c.me != nil {
myNickname = c.getFullName(c.me) myNickname = c.getFullName(c.me)
@ -542,8 +541,6 @@ func (c *Client) sendMUCStatuses(chatID int64) {
Filter: &client.ChatMembersFilterMembers{}, Filter: &client.ChatMembersFilterMembers{},
}) })
if err == nil { if err == nil {
gatewayJidSuffix := "@" + gateway.Jid.Full()
for _, member := range members.Members { for _, member := range members.Members {
var senderId int64 var senderId int64
switch member.MemberId.MessageSenderType() { switch member.MemberId.MessageSenderType() {
@ -573,7 +570,7 @@ func (c *Client) sendMUCStatuses(chatID int64) {
gateway.SPResource(nickname), gateway.SPResource(nickname),
gateway.SPImmed(true), gateway.SPImmed(true),
gateway.SPMUCAffiliation(affiliation), gateway.SPMUCAffiliation(affiliation),
gateway.SPMUCJid(strconv.FormatInt(senderId, 10) + gatewayJidSuffix), gateway.SPMUCJid(gateway.CHATJID(senderId, true)),
) )
} }
} }
@ -592,7 +589,7 @@ func (c *Client) sendMUCSubject(chatID int64, resource string) {
pin, err := c.client.GetChatPinnedMessage(&client.GetChatPinnedMessageRequest{ pin, err := c.client.GetChatPinnedMessage(&client.GetChatPinnedMessageRequest{
ChatId: chatID, ChatId: chatID,
}) })
mucJid := strconv.FormatInt(chatID, 10) + "@" + gateway.Jid.Bare() mucJid := gateway.MUCJID(chatID)
toJid := c.jid + "/" + resource toJid := c.jid + "/" + resource
if err == nil { if err == nil {
gateway.SendSubjectMessage( gateway.SendSubjectMessage(
@ -617,7 +614,7 @@ func (c *Client) updateMUCsNickname(memberID int64, newNickname string) {
c.locks.mucCacheLock.Lock() c.locks.mucCacheLock.Lock()
defer c.locks.mucCacheLock.Unlock() defer c.locks.mucCacheLock.Unlock()
realJid := strconv.FormatInt(memberID, 10) + "@" + gateway.Jid.Full() realJid := gateway.CHATJID(memberID, true)
for mucId, state := range c.mucCache { for mucId, state := range c.mucCache {
oldMember, ok := state.Members[memberID] oldMember, ok := state.Members[memberID]
if ok { if ok {
@ -626,7 +623,7 @@ func (c *Client) updateMUCsNickname(memberID int64, newNickname string) {
Affiliation: oldMember.Affiliation, Affiliation: oldMember.Affiliation,
} }
sMucId := strconv.FormatInt(mucId, 10) sMucId := gateway.MUCNODE(mucId)
unavailableStatusCodes := []uint16{303, 210} unavailableStatusCodes := []uint16{303, 210}
availableStatusCodes := []uint16{100, 210} availableStatusCodes := []uint16{100, 210}
if c.me != nil && memberID == c.me.Id { if c.me != nil && memberID == c.me.Id {
@ -1445,7 +1442,7 @@ func (c *Client) SendMessageToGateway(chatId int64, message *client.Message, id
senderId := c.getMessageSenderId(message) senderId := c.getMessageSenderId(message)
if senderId != 0 { if senderId != 0 {
originalFrom = strconv.FormatInt(senderId, 10) + "@" + gateway.Jid.Full() originalFrom = gateway.CHATJID(senderId, true)
} }
} }
@ -1552,7 +1549,7 @@ func (c *Client) SendMessageToGateway(chatId int64, message *client.Message, id
var from string var from string
if groupChatFrom == "" { if groupChatFrom == "" {
from = strconv.FormatInt(chatId, 10) from = gateway.CHATNODE(chatId)
} else { } else {
from = groupChatFrom from = groupChatFrom
} }
@ -1693,11 +1690,10 @@ func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid str
} }
func (c *Client) returnMessage(returnJid string, chatID int64, text string, code int, isGroupchat bool) { func (c *Client) returnMessage(returnJid string, chatID int64, text string, code int, isGroupchat bool) {
sChatId := strconv.FormatInt(chatID, 10)
if isGroupchat { if isGroupchat {
gateway.SendErrorMessage(returnJid, sChatId + "@" + gateway.Jid.Bare(), text, code, isGroupchat, c.xmpp) gateway.SendErrorMessage(returnJid, gateway.MUCJID(chatID), text, code, isGroupchat, c.xmpp)
} else { } else {
gateway.SendTextMessage(returnJid, sChatId, text, c.xmpp) gateway.SendTextMessage(returnJid, gateway.CHATNODE(chatID), text, c.xmpp, isGroupchat)
} }
} }
@ -2005,7 +2001,7 @@ func (c *Client) subscribeToID(id int64, chat *client.Chat) {
args = append(args, gateway.SPNickname(chat.Title)) args = append(args, gateway.SPNickname(chat.Title))
gateway.SetNickname(c.jid, strconv.FormatInt(id, 10), chat.Title, c.xmpp) gateway.SetNickname(c.jid, gateway.CHATNODE(id), chat.Title, c.xmpp)
} }
c.sendPresence(args...) c.sendPresence(args...)
@ -2080,7 +2076,7 @@ func (c *Client) UpdateChatNicknames() {
c.sendPresence(newArgs...) c.sendPresence(newArgs...)
gateway.SetNickname(c.jid, strconv.FormatInt(id, 10), chat.Title, c.xmpp) gateway.SetNickname(c.jid, gateway.CHATNODE(id), chat.Title, c.xmpp)
} }
} }
} }
@ -2240,10 +2236,10 @@ func (c *Client) memberStatusToAffiliation(memberStatus client.ChatMemberStatus)
} }
func (c *Client) sendMessagesReverse(chatID int64, messages []*client.Message, plain bool, toJid string) { func (c *Client) sendMessagesReverse(chatID int64, messages []*client.Message, plain bool, toJid string) {
sChatId := strconv.FormatInt(chatID, 10) sChatId := gateway.CHATNODE(chatID)
var mucJid string var mucJid string
if toJid != "" { if toJid != "" {
mucJid = sChatId + "@" + gateway.Jid.Bare() mucJid = gateway.MUCJID(chatID)
} }
for i := len(messages) - 1; i >= 0; i-- { for i := len(messages) - 1; i >= 0; i-- {

View file

@ -61,6 +61,32 @@ var DirtySessions = false
// MessageOutgoingPermissionVersion contains a XEP-0356 version to fake outgoing messages by foreign JIDs // MessageOutgoingPermissionVersion contains a XEP-0356 version to fake outgoing messages by foreign JIDs
var MessageOutgoingPermissionVersion = 0 var MessageOutgoingPermissionVersion = 0
// CHATNODE converts numeric id to node part of 1-1 chat JID
func CHATNODE(chatId int64) string {
return strconv.FormatInt(chatId, 10)
}
// CHATJID converts numeric id to 1-1 chat JID
func CHATJID(chatId int64, full bool) string {
var suffix string
if full {
suffix = Jid.Full()
} else {
suffix = Jid.Bare()
}
return CHATNODE(chatId) + "@" + suffix
}
// MUCNODE converts numeric id to node part of MUC JID
func MUCNODE(chatId int64) string {
return "c" + CHATNODE(chatId)
}
// MUCJID converts numeric id to MUC JID
func MUCJID(chatId int64) string {
return "c" + CHATJID(chatId, false)
}
// SendMessage creates and sends a message stanza // SendMessage creates and sends a message stanza
func SendMessage(to, from, body, id string, component *xmpp.Component, reply *Reply, timestamp int64, replaceId string, isCarbon, isGroupchat, requestReceipt bool, originalFrom string) { func SendMessage(to, from, body, id string, component *xmpp.Component, reply *Reply, timestamp int64, replaceId string, isCarbon, isGroupchat, requestReceipt bool, originalFrom string) {
sendMessageWrapper(to, from, body, "", "", id, component, reply, nil, timestamp, "", replaceId, isCarbon, isGroupchat, false, requestReceipt, originalFrom, 0, "") sendMessageWrapper(to, from, body, "", "", id, component, reply, nil, timestamp, "", replaceId, isCarbon, isGroupchat, false, requestReceipt, originalFrom, 0, "")
@ -76,12 +102,12 @@ func SendServiceMessage(to, body string, component *xmpp.Component) {
} }
// SendTextMessage creates and sends a simple message stanza // SendTextMessage creates and sends a simple message stanza
func SendTextMessage(to, from, body string, component *xmpp.Component) { func SendTextMessage(to, from, body string, component *xmpp.Component, isGroupchat bool) {
var id string var id string
if uuid, err := uuid.NewRandom(); err == nil { if uuid, err := uuid.NewRandom(); err == nil {
id = uuid.String() id = uuid.String()
} }
sendMessageWrapper(to, from, body, "", "", id, component, nil, nil, 0, "", "", false, false, false, false, "", 0, "") sendMessageWrapper(to, from, body, "", "", id, component, nil, nil, 0, "", "", false, isGroupchat, false, false, "", 0, "")
} }
// SendErrorMessage creates and sends an error message stanza // SendErrorMessage creates and sends an error message stanza
@ -536,7 +562,7 @@ func SendPresence(component *xmpp.Component, to string, args ...args.V) error {
// SPAppendFrom appends numeric from and resource to varargs // SPAppendFrom appends numeric from and resource to varargs
func SPAppendFrom(oldArgs []args.V, id int64) []args.V { func SPAppendFrom(oldArgs []args.V, id int64) []args.V {
newArgs := append(oldArgs, SPFrom(strconv.FormatInt(id, 10))) newArgs := append(oldArgs, SPFrom(CHATNODE(id)))
newArgs = append(newArgs, SPResource(Jid.Resource)) newArgs = append(newArgs, SPResource(Jid.Resource))
return newArgs return newArgs
} }
@ -626,7 +652,7 @@ func SendPubSubAvatarNotification(component *xmpp.Component, jid string, chatId
message := stanza.Message{ message := stanza.Message{
Attrs: stanza.Attrs{ Attrs: stanza.Attrs{
From: strconv.FormatInt(chatId, 10) + "@" + Jid.Bare(), From: CHATJID(chatId, false),
To: jid, To: jid,
Type: stanza.MessageTypeHeadline, Type: stanza.MessageTypeHeadline,
}, },
@ -637,6 +663,5 @@ func SendPubSubAvatarNotification(component *xmpp.Component, jid string, chatId
} }
func InviteToMUC(chatID int64, jid string, component *xmpp.Component) { func InviteToMUC(chatID int64, jid string, component *xmpp.Component) {
sChatID := strconv.FormatInt(chatID, 10) SendMUCInvite(jid, MUCNODE(chatID), component, Jid.Full())
SendMUCInvite(jid, sChatID, component, Jid.Full())
} }

View file

@ -128,7 +128,7 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) {
return return
} }
toID, ok := toToID(msg.To) toID, ok, toIsGroup := toToID(msg.To)
if ok { if ok {
toJid, err := stanza.NewJid(msg.To) toJid, err := stanza.NewJid(msg.To)
if err != nil { if err != nil {
@ -138,9 +138,15 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) {
isGroupchat := msg.Type == "groupchat" isGroupchat := msg.Type == "groupchat"
if session.Session.MUC && toJid.Resource != "" { if session.Session.MUC {
chat, _, err := session.GetContactByID(toID, nil) chat, _, err := session.GetContactByID(toID, nil)
if err == nil && session.IsGroup(chat) { if err == nil && session.IsGroup(chat) {
if !toIsGroup {
gateway.SendErrorMessage(msg.From, toJid.Node, "KHVATIT SYUDA ZVONITb", 403, false, component)
return
}
if toJid.Resource != "" {
if isGroupchat { if isGroupchat {
gateway.SendErrorMessageWithBody(msg.From, msg.To, msg.Body, "", msg.Id, 400, true, component) gateway.SendErrorMessageWithBody(msg.From, msg.To, msg.Body, "", msg.Id, 400, true, component)
} else { } else {
@ -149,6 +155,7 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) {
return return
} }
} }
}
var reply extensions.Reply var reply extensions.Reply
var fallback extensions.Fallback var fallback extensions.Fallback
@ -210,13 +217,21 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) {
chatId, msgId, err := gateway.IdsDB.GetByXmppId(session.Session.Login, bare, replace.Id) chatId, msgId, err := gateway.IdsDB.GetByXmppId(session.Session.Login, bare, replace.Id)
if err == nil { if err == nil {
if chatId != toID { if chatId != toID {
gateway.SendTextMessage(msg.From, strconv.FormatInt(toID, 10), "<ERROR: Chat mismatch>", component) if isGroupchat {
gateway.SendErrorMessage(msg.From, gateway.MUCJID(toID), text, 400, isGroupchat, component)
} else {
gateway.SendTextMessage(msg.From, gateway.CHATNODE(toID), "<ERROR: Chat mismatch>", component, isGroupchat)
}
return return
} }
replaceId = msgId replaceId = msgId
log.Debugf("replace tg: %#v %#v", chatId, msgId) log.Debugf("replace tg: %#v %#v", chatId, msgId)
} else { } else {
gateway.SendTextMessage(msg.From, strconv.FormatInt(toID, 10), "<ERROR: Could not find matching message to edit>", component) if isGroupchat {
gateway.SendErrorMessage(msg.From, gateway.MUCJID(toID), text, 400, isGroupchat, component)
} else {
gateway.SendTextMessage(msg.From, gateway.CHATNODE(toID), "<ERROR: Could not find matching message to edit>", component, isGroupchat)
}
return return
} }
} }
@ -313,7 +328,7 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) {
if !ok { if !ok {
return return
} }
toID, ok := toToID(msg.To) toID, ok, _ := toToID(msg.To)
if !ok { if !ok {
return return
} }
@ -385,7 +400,7 @@ func handleSubscription(s xmpp.Sender, p stanza.Presence) {
_ = gateway.ResumableSend(component, reply) _ = gateway.ResumableSend(component, reply)
toID, ok := toToID(p.To) toID, ok, _ := toToID(p.To)
if !ok { if !ok {
return return
} }
@ -502,8 +517,8 @@ func handleMUCPresence(s xmpp.Sender, p stanza.Presence, mucExt stanza.MucPresen
return return
} }
chatId, ok := toToID(toBare) chatId, ok, toIsGroup := toToID(toBare)
if !ok { if !ok || !toIsGroup {
presenceReplySetError(reply, 404) presenceReplySetError(reply, 404)
return return
} }
@ -562,8 +577,8 @@ func tryHandleMUCNicknameChange(s xmpp.Sender, p stanza.Presence) {
return return
} }
chatId, ok := toToID(toBare) chatId, ok, toIsGroup := toToID(toBare)
if !ok { if !ok || !toIsGroup {
return return
} }
@ -663,7 +678,7 @@ func handleGetAvatarDataIq(s xmpp.Sender, iq *stanza.IQ, pubsub *stanza.PubSubGe
return return
} }
chatId, ok := toToID(iq.To) chatId, ok, _ := toToID(iq.To)
if !ok { if !ok {
log.Errorf("Invalid chat id in To JID %v", iq.To) log.Errorf("Invalid chat id in To JID %v", iq.To)
return return
@ -764,7 +779,7 @@ func handleGetAvatarDataIq(s xmpp.Sender, iq *stanza.IQ, pubsub *stanza.PubSubGe
} }
func getTelegramChatType(from string, to string) (telegram.ChatType, error) { func getTelegramChatType(from string, to string) (telegram.ChatType, error) {
toId, ok := toToID(to) toId, ok, _ := toToID(to)
if ok { if ok {
bare, _, ok := gateway.SplitJID(from) bare, _, ok := gateway.SplitJID(from)
if ok { if ok {
@ -809,14 +824,14 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoInfo) {
defer gateway.ResumableSend(component, answer) defer gateway.ResumableSend(component, answer)
disco := answer.DiscoInfo() disco := answer.DiscoInfo()
toID, toOk := toToID(iq.To) toID, toOk, toIsGroup := toToID(iq.To)
if di.Node == "" { if di.Node == "" {
var isMuc bool var isMuc bool
bare, _, fromOk := gateway.SplitJID(iq.From) bare, _, fromOk := gateway.SplitJID(iq.From)
if fromOk { if fromOk {
session, sessionOk := sessions[bare] session, sessionOk := sessions[bare]
if sessionOk && session.Session.MUC { if sessionOk && session.Session.MUC && toIsGroup {
if toOk { if toOk {
chat, _, err := session.GetContactByID(toID, nil) chat, _, err := session.GetContactByID(toID, nil)
if err == nil && session.IsGroup(chat) { if err == nil && session.IsGroup(chat) {
@ -907,7 +922,7 @@ func handleGetDiscoItems(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoItems) {
log.Debugf("discoItems: %#v", di) log.Debugf("discoItems: %#v", di)
toID, toOk := toToID(iq.To) toID, toOk, _ := toToID(iq.To)
disco := answer.DiscoItems() disco := answer.DiscoItems()
@ -959,10 +974,9 @@ func handleGetDiscoItems(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoItems) {
// raw access, no need to create a new instance if not connected // raw access, no need to create a new instance if not connected
session, sessionOk := sessions[bare] session, sessionOk := sessions[bare]
if sessionOk && session.Session.MUC { if sessionOk && session.Session.MUC {
bareJid := gateway.Jid.Bare() disco.AddItem(gateway.Jid.Bare(), "", "Telegram group chats")
disco.AddItem(bareJid, "", "Telegram group chats")
for _, chat := range session.GetGroupChats() { for _, chat := range session.GetGroupChats() {
jid := strconv.FormatInt(chat.Id, 10) + "@" + bareJid jid := gateway.MUCJID(chat.Id)
disco.AddItem(jid, "", chat.Title) disco.AddItem(jid, "", chat.Title)
} }
} }
@ -1085,7 +1099,7 @@ func handleSetQueryCommand(s xmpp.Sender, iq *stanza.IQ, command *stanza.Command
if !ok { if !ok {
return return
} }
toId, toOk := toToID(iq.To) toId, toOk, _ := toToID(iq.To)
var cmdString string var cmdString string
var cmdType telegram.CommandType var cmdType telegram.CommandType
@ -1603,19 +1617,25 @@ func sendPubSubAvatarNotifications(s xmpp.Sender, jid string, session *telegram.
} }
} }
func toToID(to string) (int64, bool) { func toToID(to string) (int64, bool, bool) {
var isGroup bool
toParts := strings.Split(to, "@") toParts := strings.Split(to, "@")
if len(toParts) < 2 { if len(toParts) < 2 {
return 0, false return 0, false, isGroup
} }
toID, err := strconv.ParseInt(toParts[0], 10, 64) node := toParts[0]
if strings.HasPrefix(node, "c") {
isGroup = true
node = node[1:]
}
toID, err := strconv.ParseInt(node, 10, 64)
if err != nil { if err != nil {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
"to": to, "to": to,
}).Error(errors.Wrap(err, "Invalid to JID!")) }).Error(errors.Wrap(err, "Invalid to JID!"))
return 0, false return 0, false, isGroup
} }
return toID, true return toID, true, isGroup
} }
func makeVCardPayload(typ byte, id string, info telegram.VCardInfo, session *telegram.Client) stanza.IQPayload { func makeVCardPayload(typ byte, id string, info telegram.VCardInfo, session *telegram.Client) stanza.IQPayload {