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(
c.jid,
strconv.FormatInt(chatId, 10),
gateway.CHATNODE(chatId),
c.xmpp,
typ,
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
if c.Session.AsciiArrows {
deleteChar = "X "
@ -397,7 +403,13 @@ func (c *Client) updateDeleteMessages(update *client.UpdateDeleteMessages) {
deleteChar = "✗ "
}
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
}
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)
if user == nil {

View file

@ -459,14 +459,13 @@ func (c *Client) ProcessStatusUpdate(chatID int64, status string, show string, o
}
c.locks.mucCacheLock.Lock()
chatJid := strconv.FormatInt(chatID, 10) + "@" + gateway.Jid.Full()
chatJid := gateway.CHATJID(chatID, true)
for mucId, state := range c.mucCache {
member, ok := state.Members[chatID]
if ok {
sMucId := strconv.FormatInt(mucId, 10)
newMucArgs := append(
newArgs,
gateway.SPFrom(sMucId),
gateway.SPFrom(gateway.MUCNODE(mucId)),
gateway.SPResource(member.Nickname),
gateway.SPMUCAffiliation(member.Affiliation),
gateway.SPMUCJid(chatJid),
@ -529,7 +528,7 @@ func (c *Client) sendMUCStatuses(chatID int64) {
c.mucCache[chatID] = mucState
}
sChatId := strconv.FormatInt(chatID, 10)
sChatId := gateway.MUCNODE(chatID)
myNickname := "me"
if c.me != nil {
myNickname = c.getFullName(c.me)
@ -542,8 +541,6 @@ func (c *Client) sendMUCStatuses(chatID int64) {
Filter: &client.ChatMembersFilterMembers{},
})
if err == nil {
gatewayJidSuffix := "@" + gateway.Jid.Full()
for _, member := range members.Members {
var senderId int64
switch member.MemberId.MessageSenderType() {
@ -573,7 +570,7 @@ func (c *Client) sendMUCStatuses(chatID int64) {
gateway.SPResource(nickname),
gateway.SPImmed(true),
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{
ChatId: chatID,
})
mucJid := strconv.FormatInt(chatID, 10) + "@" + gateway.Jid.Bare()
mucJid := gateway.MUCJID(chatID)
toJid := c.jid + "/" + resource
if err == nil {
gateway.SendSubjectMessage(
@ -617,7 +614,7 @@ func (c *Client) updateMUCsNickname(memberID int64, newNickname string) {
c.locks.mucCacheLock.Lock()
defer c.locks.mucCacheLock.Unlock()
realJid := strconv.FormatInt(memberID, 10) + "@" + gateway.Jid.Full()
realJid := gateway.CHATJID(memberID, true)
for mucId, state := range c.mucCache {
oldMember, ok := state.Members[memberID]
if ok {
@ -626,7 +623,7 @@ func (c *Client) updateMUCsNickname(memberID int64, newNickname string) {
Affiliation: oldMember.Affiliation,
}
sMucId := strconv.FormatInt(mucId, 10)
sMucId := gateway.MUCNODE(mucId)
unavailableStatusCodes := []uint16{303, 210}
availableStatusCodes := []uint16{100, 210}
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)
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
if groupChatFrom == "" {
from = strconv.FormatInt(chatId, 10)
from = gateway.CHATNODE(chatId)
} else {
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) {
sChatId := strconv.FormatInt(chatID, 10)
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 {
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))
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...)
@ -2080,7 +2076,7 @@ func (c *Client) UpdateChatNicknames() {
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) {
sChatId := strconv.FormatInt(chatID, 10)
sChatId := gateway.CHATNODE(chatID)
var mucJid string
if toJid != "" {
mucJid = sChatId + "@" + gateway.Jid.Bare()
mucJid = gateway.MUCJID(chatID)
}
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
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
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, "")
@ -76,12 +102,12 @@ func SendServiceMessage(to, body string, component *xmpp.Component) {
}
// 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
if uuid, err := uuid.NewRandom(); err == nil {
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
@ -536,7 +562,7 @@ func SendPresence(component *xmpp.Component, to string, args ...args.V) error {
// SPAppendFrom appends numeric from and resource to varargs
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))
return newArgs
}
@ -626,7 +652,7 @@ func SendPubSubAvatarNotification(component *xmpp.Component, jid string, chatId
message := stanza.Message{
Attrs: stanza.Attrs{
From: strconv.FormatInt(chatId, 10) + "@" + Jid.Bare(),
From: CHATJID(chatId, false),
To: jid,
Type: stanza.MessageTypeHeadline,
},
@ -637,6 +663,5 @@ func SendPubSubAvatarNotification(component *xmpp.Component, jid string, chatId
}
func InviteToMUC(chatID int64, jid string, component *xmpp.Component) {
sChatID := strconv.FormatInt(chatID, 10)
SendMUCInvite(jid, sChatID, component, Jid.Full())
SendMUCInvite(jid, MUCNODE(chatID), component, Jid.Full())
}

View file

@ -128,7 +128,7 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) {
return
}
toID, ok := toToID(msg.To)
toID, ok, toIsGroup := toToID(msg.To)
if ok {
toJid, err := stanza.NewJid(msg.To)
if err != nil {
@ -138,15 +138,22 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) {
isGroupchat := msg.Type == "groupchat"
if session.Session.MUC && toJid.Resource != "" {
if session.Session.MUC {
chat, _, err := session.GetContactByID(toID, nil)
if err == nil && session.IsGroup(chat) {
if isGroupchat {
gateway.SendErrorMessageWithBody(msg.From, msg.To, msg.Body, "", msg.Id, 400, true, component)
} else {
gateway.SendErrorMessage(msg.From, msg.To, "PMing room members is not supported, use the real JID", 406, true, component)
if !toIsGroup {
gateway.SendErrorMessage(msg.From, toJid.Node, "KHVATIT SYUDA ZVONITb", 403, false, component)
return
}
if toJid.Resource != "" {
if isGroupchat {
gateway.SendErrorMessageWithBody(msg.From, msg.To, msg.Body, "", msg.Id, 400, true, component)
} else {
gateway.SendErrorMessage(msg.From, msg.To, "PMing room members is not supported, use the real JID", 406, true, component)
}
return
}
return
}
}
@ -210,13 +217,21 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) {
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), "<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
}
replaceId = msgId
log.Debugf("replace tg: %#v %#v", chatId, msgId)
} 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
}
}
@ -313,7 +328,7 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) {
if !ok {
return
}
toID, ok := toToID(msg.To)
toID, ok, _ := toToID(msg.To)
if !ok {
return
}
@ -385,7 +400,7 @@ func handleSubscription(s xmpp.Sender, p stanza.Presence) {
_ = gateway.ResumableSend(component, reply)
toID, ok := toToID(p.To)
toID, ok, _ := toToID(p.To)
if !ok {
return
}
@ -502,8 +517,8 @@ func handleMUCPresence(s xmpp.Sender, p stanza.Presence, mucExt stanza.MucPresen
return
}
chatId, ok := toToID(toBare)
if !ok {
chatId, ok, toIsGroup := toToID(toBare)
if !ok || !toIsGroup {
presenceReplySetError(reply, 404)
return
}
@ -562,8 +577,8 @@ func tryHandleMUCNicknameChange(s xmpp.Sender, p stanza.Presence) {
return
}
chatId, ok := toToID(toBare)
if !ok {
chatId, ok, toIsGroup := toToID(toBare)
if !ok || !toIsGroup {
return
}
@ -663,7 +678,7 @@ func handleGetAvatarDataIq(s xmpp.Sender, iq *stanza.IQ, pubsub *stanza.PubSubGe
return
}
chatId, ok := toToID(iq.To)
chatId, ok, _ := toToID(iq.To)
if !ok {
log.Errorf("Invalid chat id in To JID %v", iq.To)
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) {
toId, ok := toToID(to)
toId, ok, _ := toToID(to)
if ok {
bare, _, ok := gateway.SplitJID(from)
if ok {
@ -809,14 +824,14 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoInfo) {
defer gateway.ResumableSend(component, answer)
disco := answer.DiscoInfo()
toID, toOk := toToID(iq.To)
toID, toOk, toIsGroup := toToID(iq.To)
if di.Node == "" {
var isMuc bool
bare, _, fromOk := gateway.SplitJID(iq.From)
if fromOk {
session, sessionOk := sessions[bare]
if sessionOk && session.Session.MUC {
if sessionOk && session.Session.MUC && toIsGroup {
if toOk {
chat, _, err := session.GetContactByID(toID, nil)
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)
toID, toOk := toToID(iq.To)
toID, toOk, _ := toToID(iq.To)
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
session, sessionOk := sessions[bare]
if sessionOk && session.Session.MUC {
bareJid := gateway.Jid.Bare()
disco.AddItem(bareJid, "", "Telegram group chats")
disco.AddItem(gateway.Jid.Bare(), "", "Telegram group chats")
for _, chat := range session.GetGroupChats() {
jid := strconv.FormatInt(chat.Id, 10) + "@" + bareJid
jid := gateway.MUCJID(chat.Id)
disco.AddItem(jid, "", chat.Title)
}
}
@ -1085,7 +1099,7 @@ func handleSetQueryCommand(s xmpp.Sender, iq *stanza.IQ, command *stanza.Command
if !ok {
return
}
toId, toOk := toToID(iq.To)
toId, toOk, _ := toToID(iq.To)
var cmdString string
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, "@")
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 {
log.WithFields(log.Fields{
"to": to,
}).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 {