mirror of
https://dev.narayana.im/narayana/telegabber.git
synced 2026-10-07 08:41:47 +00:00
Implement occupant IDs (XEP-0421) and shorten visible occupant nicknames
This commit is contained in:
parent
4566297817
commit
22cb5b3d4c
7 changed files with 311 additions and 97 deletions
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -215,9 +216,17 @@ func (c *Client) updateUser(update *client.UpdateUser) {
|
||||||
// check if MUC nicknames should be updated
|
// check if MUC nicknames should be updated
|
||||||
oldCacheUser, ok := c.cache.GetUser(update.User.Id)
|
oldCacheUser, ok := c.cache.GetUser(update.User.Id)
|
||||||
c.cache.SetUser(update.User.Id, update.User)
|
c.cache.SetUser(update.User.Id, update.User)
|
||||||
if ok && (oldCacheUser.FirstName != update.User.FirstName || oldCacheUser.LastName != update.User.LastName) {
|
if ok {
|
||||||
newNickname := c.GetMUCNickname(update.User.Id)
|
var oldUsernames, newUsernames []string
|
||||||
c.updateMUCsNickname(update.User.Id, newNickname)
|
if oldCacheUser.Usernames != nil {
|
||||||
|
oldUsernames = oldCacheUser.Usernames.ActiveUsernames
|
||||||
|
}
|
||||||
|
if update.User.Usernames != nil {
|
||||||
|
newUsernames = update.User.Usernames.ActiveUsernames
|
||||||
|
}
|
||||||
|
if oldCacheUser.FirstName != update.User.FirstName || oldCacheUser.LastName != update.User.LastName || !slices.Equal(oldUsernames, newUsernames) {
|
||||||
|
c.updateMUCsNickname(update.User.Id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
show, status, presenceType := c.userStatusToText(update.User.Status, update.User.Id)
|
show, status, presenceType := c.userStatusToText(update.User.Status, update.User.Id)
|
||||||
|
|
@ -425,10 +434,11 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) {
|
||||||
var from string
|
var from string
|
||||||
var originalFrom string
|
var originalFrom string
|
||||||
var nickname string
|
var nickname string
|
||||||
|
var senderId int64
|
||||||
if isMUC {
|
if isMUC {
|
||||||
if messageErr == nil {
|
if messageErr == nil {
|
||||||
senderId := c.getMessageSenderId(message)
|
senderId = c.getMessageSenderId(message)
|
||||||
nickname = c.GetMUCNickname(senderId)
|
nickname = c.GetMUCNickname(update.ChatId, senderId)
|
||||||
originalFrom = gateway.CHATJID(senderId, true)
|
originalFrom = gateway.CHATJID(senderId, true)
|
||||||
|
|
||||||
safeToSend = c.assureMUCOccupant(update.ChatId, senderId, message.SenderId, chat)
|
safeToSend = c.assureMUCOccupant(update.ChatId, senderId, message.SenderId, chat)
|
||||||
|
|
@ -461,7 +471,7 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) {
|
||||||
textContent.Text.Text,
|
textContent.Text.Text,
|
||||||
textContent.Text.Entities,
|
textContent.Text.Entities,
|
||||||
markupFunction,
|
markupFunction,
|
||||||
c,
|
mucMentionRetriever{c, update.ChatId},
|
||||||
))
|
))
|
||||||
|
|
||||||
id := "e"+sId
|
id := "e"+sId
|
||||||
|
|
@ -510,10 +520,14 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) {
|
||||||
|
|
||||||
for _, jid := range jids {
|
for _, jid := range jids {
|
||||||
if safeToSend {
|
if safeToSend {
|
||||||
|
var occupantId string
|
||||||
|
if isMUC && senderId != 0 {
|
||||||
|
occupantId = gateway.CHATNODE(senderId)
|
||||||
|
}
|
||||||
gateway.SendMessage(jid, from, c.xmpp,
|
gateway.SendMessage(jid, from, c.xmpp,
|
||||||
gateway.SMBody(body), gateway.SMId(id), gateway.SMReplaceId(replaceId),
|
gateway.SMBody(body), gateway.SMId(id), gateway.SMReplaceId(replaceId),
|
||||||
gateway.SMIsCarbon(isCarbon), gateway.SMIsGroupchat(isMUC), gateway.SMOriginalFrom(originalFrom),
|
gateway.SMIsCarbon(isCarbon), gateway.SMIsGroupchat(isMUC), gateway.SMOriginalFrom(originalFrom),
|
||||||
gateway.SMOMEMOEnvelope(envelope),
|
gateway.SMOMEMOEnvelope(envelope), gateway.SMOccupantId(occupantId),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
gateway.SendMUCAnnouncement(jid, from, body, nickname, id, c.xmpp)
|
gateway.SendMUCAnnouncement(jid, from, body, nickname, id, c.xmpp)
|
||||||
|
|
@ -713,6 +727,7 @@ func (c *Client) updateChatPermissions(update *client.UpdateChatPermissions) {
|
||||||
gateway.SPResource(occupant.Nickname),
|
gateway.SPResource(occupant.Nickname),
|
||||||
gateway.SPImmed(true),
|
gateway.SPImmed(true),
|
||||||
gateway.SPMUCJid(gateway.CHATJID(occupant.id, true)),
|
gateway.SPMUCJid(gateway.CHATJID(occupant.id, true)),
|
||||||
|
gateway.SPOccupantId(gateway.CHATNODE(occupant.id)),
|
||||||
gateway.SPMUCAffiliation(affiliation),
|
gateway.SPMUCAffiliation(affiliation),
|
||||||
gateway.SPMUCRole(role),
|
gateway.SPMUCRole(role),
|
||||||
gateway.SPToJids(toJids),
|
gateway.SPToJids(toJids),
|
||||||
|
|
|
||||||
|
|
@ -114,8 +114,8 @@ func (lru *MUCOccupantsLRU) Set(id int64, occupant *MUCOccupant) (deleted *MUCOc
|
||||||
lru.cutOut(oldOccupant)
|
lru.cutOut(oldOccupant)
|
||||||
|
|
||||||
delete(lru.rev, oldOccupant.Nickname)
|
delete(lru.rev, oldOccupant.Nickname)
|
||||||
lru.rev[occupant.Nickname] = id
|
|
||||||
}
|
}
|
||||||
|
lru.rev[occupant.Nickname] = id
|
||||||
|
|
||||||
if (lru.oldest == nil) != (lru.newest == nil) {
|
if (lru.oldest == nil) != (lru.newest == nil) {
|
||||||
log.Fatal("MRD MUDAQ")
|
log.Fatal("MRD MUDAQ")
|
||||||
|
|
|
||||||
|
|
@ -210,7 +210,7 @@ func (c *Client) updateMessageInteractionInfo(update *client.UpdateMessageIntera
|
||||||
if jids := c.GetReactionCapableFullJids(); len(jids) > 0 && c.peerReactionsChanged(update.ChatId, update.MessageId, 0, peerEmojis) {
|
if jids := c.GetReactionCapableFullJids(); len(jids) > 0 && c.peerReactionsChanged(update.ChatId, update.MessageId, 0, peerEmojis) {
|
||||||
from := gateway.CHATNODE(update.ChatId)
|
from := gateway.CHATNODE(update.ChatId)
|
||||||
for _, jid := range jids {
|
for _, jid := range jids {
|
||||||
gateway.SendReactionMessage(jid, from, c.xmpp, xmppId, peerEmojis, false)
|
gateway.SendReactionMessage(jid, from, c.xmpp, xmppId, peerEmojis)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
@ -231,9 +231,9 @@ func (c *Client) updateMessageInteractionInfo(update *client.UpdateMessageIntera
|
||||||
if !c.assureMUCOccupant(update.ChatId, senderId, &client.MessageSenderUser{UserId: senderId}, chat) {
|
if !c.assureMUCOccupant(update.ChatId, senderId, &client.MessageSenderUser{UserId: senderId}, chat) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
from := mucJid + "/" + c.GetMUCNickname(senderId)
|
from := mucJid + "/" + c.GetMUCNickname(update.ChatId, senderId)
|
||||||
for _, jid := range jids {
|
for _, jid := range jids {
|
||||||
gateway.SendReactionMessage(jid, from, c.xmpp, xmppId, senderEmojis, true)
|
gateway.SendReactionMessage(jid, from, c.xmpp, xmppId, senderEmojis, gateway.SMIsGroupchat(true), gateway.SMOccupantId(gateway.CHATNODE(senderId)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -245,9 +245,9 @@ func (c *Client) updateMessageInteractionInfo(update *client.UpdateMessageIntera
|
||||||
if c.peerReactionsChanged(update.ChatId, update.MessageId, update.ChatId, anonymousEmojis) {
|
if c.peerReactionsChanged(update.ChatId, update.MessageId, update.ChatId, anonymousEmojis) {
|
||||||
anonymousSender := &client.MessageSenderChat{ChatId: update.ChatId}
|
anonymousSender := &client.MessageSenderChat{ChatId: update.ChatId}
|
||||||
if c.assureMUCOccupant(update.ChatId, update.ChatId, anonymousSender, chat) {
|
if c.assureMUCOccupant(update.ChatId, update.ChatId, anonymousSender, chat) {
|
||||||
from := mucJid + "/" + c.GetMUCNickname(update.ChatId)
|
from := mucJid + "/" + c.GetMUCNickname(update.ChatId, update.ChatId)
|
||||||
for _, jid := range jids {
|
for _, jid := range jids {
|
||||||
gateway.SendReactionMessage(jid, from, c.xmpp, xmppId, anonymousEmojis, true)
|
gateway.SendReactionMessage(jid, from, c.xmpp, xmppId, anonymousEmojis, gateway.SMIsGroupchat(true), gateway.SMOccupantId(gateway.CHATNODE(update.ChatId)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -306,13 +306,21 @@ func (c *Client) SendDelayedMUCReactions(chatId int64, message *client.Message,
|
||||||
timestamp := int64(message.Date)
|
timestamp := int64(message.Date)
|
||||||
|
|
||||||
for senderId, senderEmojis := range perOccupant {
|
for senderId, senderEmojis := range perOccupant {
|
||||||
from := mucJid + "/" + c.GetMUCNickname(senderId)
|
from := mucJid + "/" + c.GetMUCNickname(chatId, senderId)
|
||||||
entryId := "r" + strId + "-" + strconv.FormatInt(senderId, 10)
|
entryId := "r" + strId + "-" + strconv.FormatInt(senderId, 10)
|
||||||
gateway.SendMAMReactionMessage(toJid, from, c.xmpp, xmppId, senderEmojis, entryId, timestamp, mucJid, mamQueryId)
|
gateway.SendReactionMessage(toJid, from, c.xmpp, xmppId, senderEmojis,
|
||||||
|
gateway.SMId(entryId), gateway.SMStanzaId(entryId), gateway.SMTimestamp(timestamp),
|
||||||
|
gateway.SMMamQueryId(mamQueryId), gateway.SMMucJID(mucJid), gateway.SMIsGroupchat(true),
|
||||||
|
gateway.SMOccupantId(gateway.CHATNODE(senderId)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if len(anonymousEmojis) > 0 {
|
if len(anonymousEmojis) > 0 {
|
||||||
from := mucJid + "/" + c.GetMUCNickname(chatId)
|
from := mucJid + "/" + c.GetMUCNickname(chatId, chatId)
|
||||||
gateway.SendMAMReactionMessage(toJid, from, c.xmpp, xmppId, anonymousEmojis, "r"+strId+"-a", timestamp, mucJid, mamQueryId)
|
gateway.SendReactionMessage(toJid, from, c.xmpp, xmppId, anonymousEmojis,
|
||||||
|
gateway.SMId("r"+strId+"-a"), gateway.SMStanzaId("r"+strId+"-a"), gateway.SMTimestamp(timestamp),
|
||||||
|
gateway.SMMamQueryId(mamQueryId), gateway.SMMucJID(mucJid), gateway.SMIsGroupchat(true),
|
||||||
|
gateway.SMOccupantId(gateway.CHATNODE(chatId)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -431,7 +439,24 @@ func (c *Client) ownReactions(chatId, messageId int64) (map[string]bool, error)
|
||||||
MessageId: messageId,
|
MessageId: messageId,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
// messageId may be a since-superseded provisional id for a message
|
||||||
|
// we ourselves just sent - TDlib assigns a temporary id
|
||||||
|
// immediately and reassigns the real one shortly after via
|
||||||
|
// UpdateMessageSendSucceeded (see updateMessageSendSucceeded's
|
||||||
|
// MessageIdChanges bookkeeping). Retry with the current id if the
|
||||||
|
// reassignment has already landed.
|
||||||
|
c.MessageIdChangesLock.Lock()
|
||||||
|
newId, newIdOk := c.MessageIdChanges[chatId][messageId]
|
||||||
|
c.MessageIdChangesLock.Unlock()
|
||||||
|
if newIdOk && newId.Id != 0 {
|
||||||
|
message, err = c.client.GetMessage(&client.GetMessageRequest{
|
||||||
|
ChatId: chatId,
|
||||||
|
MessageId: newId.Id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
current := make(map[string]bool)
|
current := make(map[string]bool)
|
||||||
|
|
|
||||||
|
|
@ -596,6 +596,7 @@ func (c *Client) ProcessStatusUpdate(chatID int64, status string, show string, o
|
||||||
gateway.SPMUCAffiliation(occupant.Affiliation),
|
gateway.SPMUCAffiliation(occupant.Affiliation),
|
||||||
gateway.SPMUCRole(occupant.Role),
|
gateway.SPMUCRole(occupant.Role),
|
||||||
gateway.SPMUCJid(chatJid),
|
gateway.SPMUCJid(chatJid),
|
||||||
|
gateway.SPOccupantId(gateway.CHATNODE(chatID)),
|
||||||
gateway.SPToJids(toJids),
|
gateway.SPToJids(toJids),
|
||||||
)
|
)
|
||||||
err := c.sendPresence(newMucArgs...)
|
err := c.sendPresence(newMucArgs...)
|
||||||
|
|
@ -723,10 +724,11 @@ func (c *Client) sendMUCStatuses(chatID int64) {
|
||||||
func (c *Client) updateMUCOccupants(mucState *MUCState, chatID int64, members []*client.ChatMember) {
|
func (c *Client) updateMUCOccupants(mucState *MUCState, chatID int64, members []*client.ChatMember) {
|
||||||
sChatId := gateway.MUCNODE(chatID)
|
sChatId := gateway.MUCNODE(chatID)
|
||||||
myNickname := "me"
|
myNickname := "me"
|
||||||
var myJid string
|
var myJid, myOccupantId string
|
||||||
if c.me != nil {
|
if c.me != nil {
|
||||||
myNickname = c.GetMUCNickname(c.me.Id)
|
myNickname = c.getMUCNicknameLocked(c.me.Id, mucState)
|
||||||
myJid = gateway.CHATJID(c.me.Id, true)
|
myJid = gateway.CHATJID(c.me.Id, true)
|
||||||
|
myOccupantId = gateway.CHATNODE(c.me.Id)
|
||||||
}
|
}
|
||||||
myAffiliation := "member"
|
myAffiliation := "member"
|
||||||
myRole := "participant"
|
myRole := "participant"
|
||||||
|
|
@ -736,7 +738,7 @@ func (c *Client) updateMUCOccupants(mucState *MUCState, chatID int64, members []
|
||||||
_, toJids := c.getMUCJoinedJIDs(chatID, mucState, false)
|
_, toJids := c.getMUCJoinedJIDs(chatID, mucState, false)
|
||||||
|
|
||||||
for _, member := range members {
|
for _, member := range members {
|
||||||
senderId, nickname, affiliation, role := c.TgMemberToMUCOccupant(member, chat)
|
senderId, nickname, affiliation, role := c.tgMemberToMUCOccupantLocked(member, chat, mucState)
|
||||||
mucState.Occupants.Set(senderId, &MUCOccupant{
|
mucState.Occupants.Set(senderId, &MUCOccupant{
|
||||||
Nickname: nickname,
|
Nickname: nickname,
|
||||||
Affiliation: affiliation,
|
Affiliation: affiliation,
|
||||||
|
|
@ -758,6 +760,7 @@ func (c *Client) updateMUCOccupants(mucState *MUCState, chatID int64, members []
|
||||||
gateway.SPMUCAffiliation(affiliation),
|
gateway.SPMUCAffiliation(affiliation),
|
||||||
gateway.SPMUCRole(role),
|
gateway.SPMUCRole(role),
|
||||||
gateway.SPMUCJid(gateway.CHATJID(senderId, true)),
|
gateway.SPMUCJid(gateway.CHATJID(senderId, true)),
|
||||||
|
gateway.SPOccupantId(gateway.CHATNODE(senderId)),
|
||||||
gateway.SPToJids(toJids),
|
gateway.SPToJids(toJids),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -774,6 +777,7 @@ func (c *Client) updateMUCOccupants(mucState *MUCState, chatID int64, members []
|
||||||
gateway.SPMUCAffiliation(myAffiliation),
|
gateway.SPMUCAffiliation(myAffiliation),
|
||||||
gateway.SPMUCRole(myRole),
|
gateway.SPMUCRole(myRole),
|
||||||
gateway.SPMUCJid(myJid),
|
gateway.SPMUCJid(myJid),
|
||||||
|
gateway.SPOccupantId(myOccupantId),
|
||||||
gateway.SPMUCStatusCodes(selfStatusCodes),
|
gateway.SPMUCStatusCodes(selfStatusCodes),
|
||||||
gateway.SPToJids(toJids),
|
gateway.SPToJids(toJids),
|
||||||
)
|
)
|
||||||
|
|
@ -819,7 +823,7 @@ func (c *Client) addMUCOccupant(mucID int64, memberID int64, affiliation, role s
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
nickname := c.GetMUCNickname(memberID)
|
nickname := c.getMUCNicknameLocked(memberID, mucState)
|
||||||
|
|
||||||
_, toJids := c.getMUCJoinedJIDs(mucID, mucState, false)
|
_, toJids := c.getMUCJoinedJIDs(mucID, mucState, false)
|
||||||
|
|
||||||
|
|
@ -830,6 +834,7 @@ func (c *Client) addMUCOccupant(mucID int64, memberID int64, affiliation, role s
|
||||||
gateway.SPMUCAffiliation(affiliation),
|
gateway.SPMUCAffiliation(affiliation),
|
||||||
gateway.SPMUCRole(role),
|
gateway.SPMUCRole(role),
|
||||||
gateway.SPMUCJid(gateway.CHATJID(memberID, true)),
|
gateway.SPMUCJid(gateway.CHATJID(memberID, true)),
|
||||||
|
gateway.SPOccupantId(gateway.CHATNODE(memberID)),
|
||||||
gateway.SPToJids(toJids),
|
gateway.SPToJids(toJids),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -857,7 +862,7 @@ func (c *Client) sendMUCSubject(chatID int64, resource string) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
gateway.SendSubjectMessage(
|
gateway.SendSubjectMessage(
|
||||||
toJid,
|
toJid,
|
||||||
mucJid + "/" + c.GetMUCNickname(c.getMessageSenderId(pin)),
|
mucJid + "/" + c.GetMUCNickname(chatID, c.getMessageSenderId(pin)),
|
||||||
c.messageToText(pin, false),
|
c.messageToText(pin, false),
|
||||||
strconv.FormatInt(pin.Id, 10),
|
strconv.FormatInt(pin.Id, 10),
|
||||||
c.xmpp,
|
c.xmpp,
|
||||||
|
|
@ -868,8 +873,37 @@ func (c *Client) sendMUCSubject(chatID int64, resource string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMUCNickname generates a unique nickname for a MUC occupant
|
// MyId returns the bridged Telegram account's own user id, or 0 if the
|
||||||
func (c *Client) GetMUCNickname(chatID int64) string {
|
// account info isn't loaded yet.
|
||||||
|
func (c *Client) MyId() int64 {
|
||||||
|
if c.me == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return c.me.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
// resourcePrepBMP runs XMPP resourceprep and additionally rejects non-BMP
|
||||||
|
// characters, returning ok=false if either check fails.
|
||||||
|
func resourcePrepBMP(s string) (string, bool) {
|
||||||
|
rp, err := gateway.ResourcePrep(s)
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
for _, r := range rp {
|
||||||
|
if r > bmpCeil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rp, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildMUCNickname formats chatID (a Telegram user or chat id) for display
|
||||||
|
// as a MUC nickname. Normally a username-less user's nickname is always
|
||||||
|
// disambiguated with a trailing "(id)" so nicknames can never collide.
|
||||||
|
// When collides is non-nil (see GetMUCNickname), that disambiguator is
|
||||||
|
// dropped in favor of the bare name, unless collides reports that the
|
||||||
|
// bare name is already claimed by a different member.
|
||||||
|
func (c *Client) buildMUCNickname(chatID int64, collides func(name string) bool) string {
|
||||||
if chatID == 0 {
|
if chatID == 0 {
|
||||||
if c.me != nil {
|
if c.me != nil {
|
||||||
chatID = c.me.Id
|
chatID = c.me.Id
|
||||||
|
|
@ -877,22 +911,49 @@ func (c *Client) GetMUCNickname(chatID int64) string {
|
||||||
return "me"
|
return "me"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fc := c.FormatContact(chatID)
|
|
||||||
rp, err := gateway.ResourcePrep(fc)
|
chat, user, err := c.GetContactByID(chatID, nil, false)
|
||||||
if err == nil {
|
var fc string
|
||||||
// additionally check for non-BMP characters
|
switch {
|
||||||
for _, r := range rp {
|
case err != nil:
|
||||||
if r > bmpCeil {
|
fc = "unknown contact: " + err.Error()
|
||||||
err = errors.New("Non-BMP character")
|
// user must be checked before chat: GetContactByID resolves a non-nil
|
||||||
break
|
// chat (the private-chat wrapper) for any positive user id too, not
|
||||||
|
// just actual groups - checking chat first would always shadow user.
|
||||||
|
case user != nil:
|
||||||
|
var usernames string
|
||||||
|
if user.Usernames != nil {
|
||||||
|
usernames = c.usernamesToString(user.Usernames.ActiveUsernames)
|
||||||
|
}
|
||||||
|
name := spaceRegex.ReplaceAllString(fmt.Sprintf("%s %s", user.FirstName, user.LastName), " ")
|
||||||
|
if collides != nil && usernames == "" {
|
||||||
|
if rp, ok := resourcePrepBMP(name); ok && !collides(rp) {
|
||||||
|
return rp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
idOrUsername := usernames
|
||||||
|
if idOrUsername == "" {
|
||||||
|
idOrUsername = strconv.FormatInt(chatID, 10)
|
||||||
|
}
|
||||||
|
fc = fmt.Sprintf("%s (%v)", name, idOrUsername)
|
||||||
|
case chat != nil:
|
||||||
|
title := spaceRegex.ReplaceAllString(chat.Title, " ")
|
||||||
|
if collides != nil {
|
||||||
|
if rp, ok := resourcePrepBMP(title); ok && !collides(rp) {
|
||||||
|
return rp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fc = fmt.Sprintf("%s (%v)", chat.Title, chat.Id)
|
||||||
|
default:
|
||||||
|
fc = strconv.FormatInt(chatID, 10)
|
||||||
}
|
}
|
||||||
if err != nil {
|
fc = spaceRegex.ReplaceAllString(fc, " ")
|
||||||
|
|
||||||
|
rp, ok := resourcePrepBMP(fc)
|
||||||
|
if !ok {
|
||||||
log.Warnf("Resourceprep for %v failed, falling back to chat ID", fc)
|
log.Warnf("Resourceprep for %v failed, falling back to chat ID", fc)
|
||||||
|
|
||||||
var usernames string
|
var usernames string
|
||||||
_, user, _ := c.GetContactByID(chatID, nil, false)
|
|
||||||
if user != nil && user.Usernames != nil {
|
if user != nil && user.Usernames != nil {
|
||||||
usernames = c.usernamesToString(user.Usernames.ActiveUsernames)
|
usernames = c.usernamesToString(user.Usernames.ActiveUsernames)
|
||||||
}
|
}
|
||||||
|
|
@ -911,7 +972,45 @@ func (c *Client) GetMUCNickname(chatID int64) string {
|
||||||
return rp
|
return rp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) updateMUCsNickname(memberID int64, newNickname string) {
|
// getMUCNicknameLocked computes the nickname to register for memberId as
|
||||||
|
// an occupant of mucState's room, dropping the "(id)" disambiguator
|
||||||
|
// unless the bare name is already taken by someone else. mucState may be
|
||||||
|
// nil (no room context, e.g. a PM), in which case this degrades to the
|
||||||
|
// plain always-disambiguated buildMUCNickname formatting.
|
||||||
|
// achtung: assuming a locked mucState context
|
||||||
|
func (c *Client) getMUCNicknameLocked(memberId int64, mucState *MUCState) string {
|
||||||
|
// buildMUCNickname resolves a 0 memberId to c.me.Id internally (the
|
||||||
|
// "self" convention GetMUCNickname(chatId, 0) also relies on) - the
|
||||||
|
// collision check below needs that same resolved id, or self's own
|
||||||
|
// registered nickname would look like a collision with someone else's
|
||||||
|
// id 0.
|
||||||
|
resolvedId := memberId
|
||||||
|
if resolvedId == 0 && c.me != nil {
|
||||||
|
resolvedId = c.me.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
var collides func(string) bool
|
||||||
|
if mucState != nil {
|
||||||
|
collides = func(name string) bool {
|
||||||
|
id, ok := mucState.Occupants.GetIdByNickname(name)
|
||||||
|
return ok && id != resolvedId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.buildMUCNickname(memberId, collides)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMUCNickname is getMUCNicknameLocked for callers that don't already
|
||||||
|
// hold c.locks.mucCacheLock. chatId 0 (no room context) falls back to the
|
||||||
|
// plain always-disambiguated nickname.
|
||||||
|
func (c *Client) GetMUCNickname(chatId, memberId int64) string {
|
||||||
|
c.locks.mucCacheLock.Lock()
|
||||||
|
mucState := c.mucCache[chatId]
|
||||||
|
nickname := c.getMUCNicknameLocked(memberId, mucState)
|
||||||
|
c.locks.mucCacheLock.Unlock()
|
||||||
|
return nickname
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) updateMUCsNickname(memberID int64) {
|
||||||
c.locks.mucCacheLock.Lock()
|
c.locks.mucCacheLock.Lock()
|
||||||
defer c.locks.mucCacheLock.Unlock()
|
defer c.locks.mucCacheLock.Unlock()
|
||||||
|
|
||||||
|
|
@ -919,6 +1018,11 @@ func (c *Client) updateMUCsNickname(memberID int64, newNickname string) {
|
||||||
for mucId, state := range c.mucCache {
|
for mucId, state := range c.mucCache {
|
||||||
oldOccupant, ok := state.Occupants.Get(memberID)
|
oldOccupant, ok := state.Occupants.Get(memberID)
|
||||||
if ok {
|
if ok {
|
||||||
|
newNickname := c.getMUCNicknameLocked(memberID, state)
|
||||||
|
if newNickname == oldOccupant.Nickname {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
c.DelayedStatusesLock.Lock()
|
c.DelayedStatusesLock.Lock()
|
||||||
delete(c.DelayedStatuses, mucId)
|
delete(c.DelayedStatuses, mucId)
|
||||||
c.DelayedStatusesLock.Unlock()
|
c.DelayedStatusesLock.Unlock()
|
||||||
|
|
@ -949,6 +1053,7 @@ func (c *Client) updateMUCsNickname(memberID int64, newNickname string) {
|
||||||
gateway.SPMUCNick(newNickname),
|
gateway.SPMUCNick(newNickname),
|
||||||
gateway.SPMUCStatusCodes(unavailableStatusCodes),
|
gateway.SPMUCStatusCodes(unavailableStatusCodes),
|
||||||
gateway.SPMUCJid(realJid),
|
gateway.SPMUCJid(realJid),
|
||||||
|
gateway.SPOccupantId(gateway.CHATNODE(memberID)),
|
||||||
gateway.SPToJids(toJids),
|
gateway.SPToJids(toJids),
|
||||||
)
|
)
|
||||||
c.sendPresence(
|
c.sendPresence(
|
||||||
|
|
@ -959,6 +1064,7 @@ func (c *Client) updateMUCsNickname(memberID int64, newNickname string) {
|
||||||
gateway.SPMUCRole(oldOccupant.Role),
|
gateway.SPMUCRole(oldOccupant.Role),
|
||||||
gateway.SPMUCStatusCodes(availableStatusCodes),
|
gateway.SPMUCStatusCodes(availableStatusCodes),
|
||||||
gateway.SPMUCJid(realJid),
|
gateway.SPMUCJid(realJid),
|
||||||
|
gateway.SPOccupantId(gateway.CHATNODE(memberID)),
|
||||||
gateway.SPToJids(toJids),
|
gateway.SPToJids(toJids),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -1157,7 +1263,7 @@ func (c *Client) getMessageReply(message *client.Message, preview bool, noConten
|
||||||
replyTo.Quote.Text.Text,
|
replyTo.Quote.Text.Text,
|
||||||
replyTo.Quote.Text.Entities,
|
replyTo.Quote.Text.Entities,
|
||||||
c.getFormatter(),
|
c.getFormatter(),
|
||||||
c,
|
mucMentionRetriever{c, replyTo.ChatId},
|
||||||
)
|
)
|
||||||
// make the whole quote fit one line
|
// make the whole quote fit one line
|
||||||
text = strings.ReplaceAll(text, "\n", " ")
|
text = strings.ReplaceAll(text, "\n", " ")
|
||||||
|
|
@ -1187,10 +1293,11 @@ func (c *Client) getMessageReply(message *client.Message, preview bool, noConten
|
||||||
Id: replyId,
|
Id: replyId,
|
||||||
}
|
}
|
||||||
} else if !noContent {
|
} else if !noContent {
|
||||||
// it's safe to assume there's no need to pass ChatId here
|
// replyTo.ChatId, not message.ChatId: this quotes a message
|
||||||
// as it's needed only for pin messages which are not allowed in replies
|
// from another chat, and its mentions should resolve against
|
||||||
|
// that chat's own occupants, not the replying message's.
|
||||||
if text == "" && replyTo.Content != nil {
|
if text == "" && replyTo.Content != nil {
|
||||||
text = c.messageContentToText(replyTo.Content, 0, preview)
|
text = c.messageContentToText(replyTo.Content, replyTo.ChatId, preview)
|
||||||
}
|
}
|
||||||
|
|
||||||
if text == "" {
|
if text == "" {
|
||||||
|
|
@ -1544,7 +1651,7 @@ func (c *Client) messageContentToText(content client.MessageContent, chatId int6
|
||||||
photo.Caption.Text,
|
photo.Caption.Text,
|
||||||
photo.Caption.Entities,
|
photo.Caption.Entities,
|
||||||
markupMode,
|
markupMode,
|
||||||
c,
|
mucMentionRetriever{c, chatId},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case client.TypeMessageAudio:
|
case client.TypeMessageAudio:
|
||||||
|
|
@ -1556,7 +1663,7 @@ func (c *Client) messageContentToText(content client.MessageContent, chatId int6
|
||||||
audio.Caption.Text,
|
audio.Caption.Text,
|
||||||
audio.Caption.Entities,
|
audio.Caption.Entities,
|
||||||
markupMode,
|
markupMode,
|
||||||
c,
|
mucMentionRetriever{c, chatId},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case client.TypeMessageVideo:
|
case client.TypeMessageVideo:
|
||||||
|
|
@ -1568,7 +1675,7 @@ func (c *Client) messageContentToText(content client.MessageContent, chatId int6
|
||||||
video.Caption.Text,
|
video.Caption.Text,
|
||||||
video.Caption.Entities,
|
video.Caption.Entities,
|
||||||
markupMode,
|
markupMode,
|
||||||
c,
|
mucMentionRetriever{c, chatId},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case client.TypeMessageDocument:
|
case client.TypeMessageDocument:
|
||||||
|
|
@ -1580,7 +1687,7 @@ func (c *Client) messageContentToText(content client.MessageContent, chatId int6
|
||||||
document.Caption.Text,
|
document.Caption.Text,
|
||||||
document.Caption.Entities,
|
document.Caption.Entities,
|
||||||
markupMode,
|
markupMode,
|
||||||
c,
|
mucMentionRetriever{c, chatId},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case client.TypeMessageText:
|
case client.TypeMessageText:
|
||||||
|
|
@ -1592,7 +1699,7 @@ func (c *Client) messageContentToText(content client.MessageContent, chatId int6
|
||||||
text.Text.Text,
|
text.Text.Text,
|
||||||
text.Text.Entities,
|
text.Text.Entities,
|
||||||
markupMode,
|
markupMode,
|
||||||
c,
|
mucMentionRetriever{c, chatId},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case client.TypeMessageVoiceNote:
|
case client.TypeMessageVoiceNote:
|
||||||
|
|
@ -1604,7 +1711,7 @@ func (c *Client) messageContentToText(content client.MessageContent, chatId int6
|
||||||
voice.Caption.Text,
|
voice.Caption.Text,
|
||||||
voice.Caption.Entities,
|
voice.Caption.Entities,
|
||||||
markupMode,
|
markupMode,
|
||||||
c,
|
mucMentionRetriever{c, chatId},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case client.TypeMessageVideoNote:
|
case client.TypeMessageVideoNote:
|
||||||
|
|
@ -1618,7 +1725,7 @@ func (c *Client) messageContentToText(content client.MessageContent, chatId int6
|
||||||
animation.Caption.Text,
|
animation.Caption.Text,
|
||||||
animation.Caption.Entities,
|
animation.Caption.Entities,
|
||||||
markupMode,
|
markupMode,
|
||||||
c,
|
mucMentionRetriever{c, chatId},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case client.TypeMessageContact:
|
case client.TypeMessageContact:
|
||||||
|
|
@ -1909,7 +2016,7 @@ func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) {
|
||||||
|
|
||||||
switch message.Content.MessageContentType() {
|
switch message.Content.MessageContentType() {
|
||||||
case client.TypeMessageChatJoinByLink:
|
case client.TypeMessageChatJoinByLink:
|
||||||
c.mucOccupantRolePresence(chatId, senderId, ChatMemberStatusUnmuted, c.GetMUCNickname(senderId))
|
c.mucOccupantRolePresence(chatId, senderId, ChatMemberStatusUnmuted, c.GetMUCNickname(chatId, senderId))
|
||||||
case client.TypeMessageChatAddMembers:
|
case client.TypeMessageChatAddMembers:
|
||||||
addMembers, _ := message.Content.(*client.MessageChatAddMembers)
|
addMembers, _ := message.Content.(*client.MessageChatAddMembers)
|
||||||
for _, memberId := range addMembers.MemberUserIds {
|
for _, memberId := range addMembers.MemberUserIds {
|
||||||
|
|
@ -1918,11 +2025,11 @@ func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) {
|
||||||
gateway.InviteToMUC(chatId, jid, c.xmpp)
|
gateway.InviteToMUC(chatId, jid, c.xmpp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.mucOccupantRolePresence(chatId, memberId, ChatMemberStatusUnmuted, c.GetMUCNickname(memberId))
|
c.mucOccupantRolePresence(chatId, memberId, ChatMemberStatusUnmuted, c.GetMUCNickname(chatId, memberId))
|
||||||
}
|
}
|
||||||
case client.TypeMessageChatDeleteMember:
|
case client.TypeMessageChatDeleteMember:
|
||||||
deleteMember, _ := message.Content.(*client.MessageChatDeleteMember)
|
deleteMember, _ := message.Content.(*client.MessageChatDeleteMember)
|
||||||
c.mucOccupantRolePresence(chatId, deleteMember.UserId, ChatMemberStatusKicked, c.GetMUCNickname(deleteMember.UserId))
|
c.mucOccupantRolePresence(chatId, deleteMember.UserId, ChatMemberStatusKicked, c.GetMUCNickname(chatId, deleteMember.UserId))
|
||||||
case client.TypeMessageBasicGroupChatCreate, client.TypeMessageSupergroupChatCreate, client.TypeMessageChatUpgradeTo:
|
case client.TypeMessageBasicGroupChatCreate, client.TypeMessageSupergroupChatCreate, client.TypeMessageChatUpgradeTo:
|
||||||
inviteChatId := chatId
|
inviteChatId := chatId
|
||||||
if message.Content.MessageContentType() == client.TypeMessageChatUpgradeTo {
|
if message.Content.MessageContentType() == client.TypeMessageChatUpgradeTo {
|
||||||
|
|
@ -1938,7 +2045,7 @@ func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) {
|
||||||
|
|
||||||
safeToSend = c.assureMUCOccupant(chatId, senderId, message.SenderId, chat)
|
safeToSend = c.assureMUCOccupant(chatId, senderId, message.SenderId, chat)
|
||||||
|
|
||||||
groupChatFrom = gateway.MUCJID(chatId) + "/" + c.GetMUCNickname(senderId)
|
groupChatFrom = gateway.MUCJID(chatId) + "/" + c.GetMUCNickname(chatId, senderId)
|
||||||
var ok bool
|
var ok bool
|
||||||
ok, groupChatTos = c.getMUCJoinedJIDs(chatId, nil, true)
|
ok, groupChatTos = c.getMUCJoinedJIDs(chatId, nil, true)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
@ -1960,6 +2067,7 @@ func (c *Client) SendMessageToGateway(chatId int64, message *client.Message, id
|
||||||
var jids []string
|
var jids []string
|
||||||
var isGroupchat bool
|
var isGroupchat bool
|
||||||
var originalFrom string
|
var originalFrom string
|
||||||
|
senderId := c.getMessageSenderId(message)
|
||||||
if len(groupChatTos) == 0 {
|
if len(groupChatTos) == 0 {
|
||||||
isCarbon = c.isCarbonsEnabled() && message.IsOutgoing
|
isCarbon = c.isCarbonsEnabled() && message.IsOutgoing
|
||||||
jids = c.GetCarbonFullJids(isCarbon, "", true)
|
jids = c.GetCarbonFullJids(isCarbon, "", true)
|
||||||
|
|
@ -1967,7 +2075,6 @@ func (c *Client) SendMessageToGateway(chatId int64, message *client.Message, id
|
||||||
isGroupchat = true
|
isGroupchat = true
|
||||||
jids = groupChatTos
|
jids = groupChatTos
|
||||||
|
|
||||||
senderId := c.getMessageSenderId(message)
|
|
||||||
if senderId != 0 {
|
if senderId != 0 {
|
||||||
originalFrom = gateway.CHATJID(senderId, true)
|
originalFrom = gateway.CHATJID(senderId, true)
|
||||||
}
|
}
|
||||||
|
|
@ -2189,12 +2296,18 @@ func (c *Client) SendMessageToGateway(chatId int64, message *client.Message, id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var occupantId string
|
||||||
|
if isGroupchat && senderId != 0 {
|
||||||
|
occupantId = gateway.CHATNODE(senderId)
|
||||||
|
}
|
||||||
|
|
||||||
for _, jid := range jids {
|
for _, jid := range jids {
|
||||||
commonArgs := []args.V{
|
commonArgs := []args.V{
|
||||||
gateway.SMReply(reply), gateway.SMTimestamp(timestamp), gateway.SMIsCarbon(isCarbon),
|
gateway.SMReply(reply), gateway.SMTimestamp(timestamp), gateway.SMIsCarbon(isCarbon),
|
||||||
gateway.SMIsGroupchat(isGroupchat), gateway.SMRequestReceipt(c.Session.Receipts),
|
gateway.SMIsGroupchat(isGroupchat), gateway.SMRequestReceipt(c.Session.Receipts),
|
||||||
gateway.SMOriginalFrom(originalFrom), gateway.SMMamQueryId(mamQueryId),
|
gateway.SMOriginalFrom(originalFrom), gateway.SMMamQueryId(mamQueryId),
|
||||||
gateway.SMMucJID(mucJID), gateway.SMMucUserItem(mucUserItem),
|
gateway.SMMucJID(mucJID), gateway.SMMucUserItem(mucUserItem),
|
||||||
|
gateway.SMOccupantId(occupantId),
|
||||||
}
|
}
|
||||||
if omemoFailed {
|
if omemoFailed {
|
||||||
gateway.SendMessage(jid, from, c.xmpp, append(commonArgs,
|
gateway.SendMessage(jid, from, c.xmpp, append(commonArgs,
|
||||||
|
|
@ -2237,7 +2350,7 @@ func (c *Client) SendDelayedMUCMessage(chatId int64, message *client.Message, to
|
||||||
message,
|
message,
|
||||||
msgId,
|
msgId,
|
||||||
true,
|
true,
|
||||||
gateway.MUCJID(chatId) + "/" + c.GetMUCNickname(c.getMessageSenderId(message)),
|
gateway.MUCJID(chatId) + "/" + c.GetMUCNickname(chatId, c.getMessageSenderId(message)),
|
||||||
[]string{toJid},
|
[]string{toJid},
|
||||||
mamQueryId,
|
mamQueryId,
|
||||||
)
|
)
|
||||||
|
|
@ -2868,7 +2981,7 @@ func (c *Client) GetChatDescription(chat *client.Chat) string {
|
||||||
fullInfo.Bio.Text,
|
fullInfo.Bio.Text,
|
||||||
fullInfo.Bio.Entities,
|
fullInfo.Bio.Entities,
|
||||||
c.getFormatter(),
|
c.getFormatter(),
|
||||||
c,
|
mucMentionRetriever{c, 0},
|
||||||
)
|
)
|
||||||
} else if fullInfo.BotInfo != nil {
|
} else if fullInfo.BotInfo != nil {
|
||||||
if fullInfo.BotInfo.ShortDescription != "" {
|
if fullInfo.BotInfo.ShortDescription != "" {
|
||||||
|
|
@ -3266,8 +3379,21 @@ func (c *Client) memberStatusToAffiliationAndRole(memberStatus client.ChatMember
|
||||||
|
|
||||||
// TgMemberToMUCMember resolves useful data to generate a MUC occupant
|
// TgMemberToMUCMember resolves useful data to generate a MUC occupant
|
||||||
func (c *Client) TgMemberToMUCOccupant(member *client.ChatMember, chat *client.Chat) (senderId int64, nickname, affiliation, role string) {
|
func (c *Client) TgMemberToMUCOccupant(member *client.ChatMember, chat *client.Chat) (senderId int64, nickname, affiliation, role string) {
|
||||||
|
var mucState *MUCState
|
||||||
|
if chat != nil {
|
||||||
|
c.locks.mucCacheLock.Lock()
|
||||||
|
mucState = c.mucCache[chat.Id]
|
||||||
|
defer c.locks.mucCacheLock.Unlock()
|
||||||
|
}
|
||||||
|
return c.tgMemberToMUCOccupantLocked(member, chat, mucState)
|
||||||
|
}
|
||||||
|
|
||||||
|
// tgMemberToMUCOccupantLocked is TgMemberToMUCOccupant for callers that
|
||||||
|
// already hold c.locks.mucCacheLock (mirrors updateMUCOccupants' locking
|
||||||
|
// convention).
|
||||||
|
func (c *Client) tgMemberToMUCOccupantLocked(member *client.ChatMember, chat *client.Chat, mucState *MUCState) (senderId int64, nickname, affiliation, role string) {
|
||||||
senderId = c.GetSenderId(member.MemberId)
|
senderId = c.GetSenderId(member.MemberId)
|
||||||
nickname = c.GetMUCNickname(senderId)
|
nickname = c.getMUCNicknameLocked(senderId, mucState)
|
||||||
affiliation, role = c.memberStatusToAffiliationAndRole(member.Status, chat)
|
affiliation, role = c.memberStatusToAffiliationAndRole(member.Status, chat)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -3299,12 +3425,14 @@ func (c *Client) sendMessagesReverse(chatID int64, messages []*client.Message, p
|
||||||
|
|
||||||
var originalFrom string
|
var originalFrom string
|
||||||
var from string
|
var from string
|
||||||
|
var occupantId string
|
||||||
if isMUC {
|
if isMUC {
|
||||||
senderId := c.getMessageSenderId(message)
|
senderId := c.getMessageSenderId(message)
|
||||||
if senderId != 0 {
|
if senderId != 0 {
|
||||||
originalFrom = gateway.CHATJID(senderId, true)
|
originalFrom = gateway.CHATJID(senderId, true)
|
||||||
|
occupantId = gateway.CHATNODE(senderId)
|
||||||
}
|
}
|
||||||
from = mucJid + "/" + c.GetMUCNickname(senderId)
|
from = mucJid + "/" + c.GetMUCNickname(chatID, senderId)
|
||||||
} else {
|
} else {
|
||||||
from = sChatId
|
from = sChatId
|
||||||
}
|
}
|
||||||
|
|
@ -3349,7 +3477,7 @@ func (c *Client) sendMessagesReverse(chatID int64, messages []*client.Message, p
|
||||||
gateway.SendMessage(to, from, c.xmpp,
|
gateway.SendMessage(to, from, c.xmpp,
|
||||||
gateway.SMBody(body), gateway.SMId(sId),
|
gateway.SMBody(body), gateway.SMId(sId),
|
||||||
gateway.SMReply(reply), gateway.SMIsGroupchat(isMUC), gateway.SMOriginalFrom(originalFrom),
|
gateway.SMReply(reply), gateway.SMIsGroupchat(isMUC), gateway.SMOriginalFrom(originalFrom),
|
||||||
gateway.SMOMEMOEnvelope(envelope),
|
gateway.SMOMEMOEnvelope(envelope), gateway.SMOccupantId(occupantId),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -3518,17 +3646,19 @@ func (c *Client) leaveChat(chatID int64) error {
|
||||||
|
|
||||||
// achtung: assuming a locked mucState context
|
// achtung: assuming a locked mucState context
|
||||||
func (c *Client) kickMeFromMUC(chatID int64, statusCodes []uint16, destroy bool, mucState *MUCState) error {
|
func (c *Client) kickMeFromMUC(chatID int64, statusCodes []uint16, destroy bool, mucState *MUCState) error {
|
||||||
var myJid string
|
var myJid, myOccupantId string
|
||||||
if c.me != nil {
|
if c.me != nil {
|
||||||
myJid = gateway.CHATJID(c.me.Id, true)
|
myJid = gateway.CHATJID(c.me.Id, true)
|
||||||
|
myOccupantId = gateway.CHATNODE(c.me.Id)
|
||||||
}
|
}
|
||||||
_, toJids := c.getMUCJoinedJIDs(chatID, mucState, mucState == nil)
|
_, toJids := c.getMUCJoinedJIDs(chatID, mucState, mucState == nil)
|
||||||
args := []args.V{
|
args := []args.V{
|
||||||
gateway.SPFrom(gateway.MUCNODE(chatID)),
|
gateway.SPFrom(gateway.MUCNODE(chatID)),
|
||||||
gateway.SPResource(c.GetMUCNickname(0)),
|
gateway.SPResource(c.getMUCNicknameLocked(0, mucState)),
|
||||||
gateway.SPMUCAffiliation("none"),
|
gateway.SPMUCAffiliation("none"),
|
||||||
gateway.SPMUCRole("none"),
|
gateway.SPMUCRole("none"),
|
||||||
gateway.SPMUCJid(myJid),
|
gateway.SPMUCJid(myJid),
|
||||||
|
gateway.SPOccupantId(myOccupantId),
|
||||||
gateway.SPMUCStatusCodes(statusCodes),
|
gateway.SPMUCStatusCodes(statusCodes),
|
||||||
gateway.SPToJids(toJids),
|
gateway.SPToJids(toJids),
|
||||||
}
|
}
|
||||||
|
|
@ -3562,6 +3692,7 @@ func (c *Client) kickStaleOccupant(chatID int64, deleted *MUCOccupant, mucState
|
||||||
gateway.SPMUCAffiliation(deleted.Affiliation),
|
gateway.SPMUCAffiliation(deleted.Affiliation),
|
||||||
gateway.SPMUCRole(deleted.Role),
|
gateway.SPMUCRole(deleted.Role),
|
||||||
gateway.SPMUCStatusCodes([]uint16{307, 333}),
|
gateway.SPMUCStatusCodes([]uint16{307, 333}),
|
||||||
|
gateway.SPOccupantId(gateway.CHATNODE(deleted.id)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3719,6 +3850,7 @@ func (c *Client) mucOccupantRolePresence(chatID, userID int64, status ChatMember
|
||||||
gateway.SPResource(nickname),
|
gateway.SPResource(nickname),
|
||||||
gateway.SPImmed(true),
|
gateway.SPImmed(true),
|
||||||
gateway.SPMUCJid(gateway.CHATJID(userID, true)),
|
gateway.SPMUCJid(gateway.CHATJID(userID, true)),
|
||||||
|
gateway.SPOccupantId(gateway.CHATNODE(userID)),
|
||||||
gateway.SPToJids(toJids),
|
gateway.SPToJids(toJids),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3827,16 +3959,29 @@ func (c *Client) getChatMemberStatus(status client.ChatMemberStatus) ChatMemberS
|
||||||
return ChatMemberStatusIllegal
|
return ChatMemberStatusIllegal
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMUCNicknameByUsername implement the MentionRetriever interface for message formatters
|
// mucMentionRetriever implements formatter.MentionRetriever bound to a
|
||||||
func (c *Client) GetMUCNicknameByUsername(username string) (string, error) {
|
// single chat, so an @mention inside one of that chat's messages renders
|
||||||
chat, err := c.client.SearchPublicChat(&client.SearchPublicChatRequest{
|
// with the same nickname shown in its occupant list (see GetMUCNickname).
|
||||||
|
// chatId 0 (no particular room in scope) falls back to the plain
|
||||||
|
// always-disambiguated nickname.
|
||||||
|
type mucMentionRetriever struct {
|
||||||
|
c *Client
|
||||||
|
chatId int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mucMentionRetriever) GetMUCNickname(id int64) string {
|
||||||
|
return m.c.GetMUCNickname(m.chatId, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mucMentionRetriever) GetMUCNicknameByUsername(username string) (string, error) {
|
||||||
|
chat, err := m.c.client.SearchPublicChat(&client.SearchPublicChatRequest{
|
||||||
Username: username,
|
Username: username,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.GetMUCNickname(chat.Id), nil
|
return m.c.GetMUCNickname(m.chatId, chat.Id), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindMessageByTime retrieves the closest message before the given timestamp
|
// FindMessageByTime retrieves the closest message before the given timestamp
|
||||||
|
|
|
||||||
|
|
@ -333,6 +333,12 @@ type MessageOriginId struct {
|
||||||
Id string `xml:"id,attr"`
|
Id string `xml:"id,attr"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OccupantId is from XEP-0421, carried on both MUC messages and presence
|
||||||
|
type OccupantId struct {
|
||||||
|
XMLName xml.Name `xml:"urn:xmpp:occupant-id:0 occupant-id"`
|
||||||
|
Id string `xml:"id,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
// EmptySubject is a dummy for MUCs to circumvent omitempty. Not registered as it would conflict with Subject field
|
// EmptySubject is a dummy for MUCs to circumvent omitempty. Not registered as it would conflict with Subject field
|
||||||
type EmptySubject struct {
|
type EmptySubject struct {
|
||||||
XMLName xml.Name `xml:"subject"`
|
XMLName xml.Name `xml:"subject"`
|
||||||
|
|
@ -889,6 +895,18 @@ func init() {
|
||||||
"origin-id",
|
"origin-id",
|
||||||
}, MessageOriginId{})
|
}, MessageOriginId{})
|
||||||
|
|
||||||
|
// occupant id (message)
|
||||||
|
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{
|
||||||
|
"urn:xmpp:occupant-id:0",
|
||||||
|
"occupant-id",
|
||||||
|
}, OccupantId{})
|
||||||
|
|
||||||
|
// occupant id (presence)
|
||||||
|
stanza.TypeRegistry.MapExtension(stanza.PKTPresence, xml.Name{
|
||||||
|
"urn:xmpp:occupant-id:0",
|
||||||
|
"occupant-id",
|
||||||
|
}, OccupantId{})
|
||||||
|
|
||||||
// muc admin query
|
// muc admin query
|
||||||
stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{
|
stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{
|
||||||
"http://jabber.org/protocol/muc#admin",
|
"http://jabber.org/protocol/muc#admin",
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,9 @@ const NSCaps string = "http://jabber.org/protocol/caps"
|
||||||
// NSReactions is the XEP-0444 message reactions namespace
|
// NSReactions is the XEP-0444 message reactions namespace
|
||||||
const NSReactions string = "urn:xmpp:reactions:0"
|
const NSReactions string = "urn:xmpp:reactions:0"
|
||||||
|
|
||||||
|
// NSOccupantId is the XEP-0421 occupant id namespace
|
||||||
|
const NSOccupantId string = "urn:xmpp:occupant-id:0"
|
||||||
|
|
||||||
// CapsNode is this software's advertised XEP-0115 node URI
|
// CapsNode is this software's advertised XEP-0115 node URI
|
||||||
const CapsNode string = "https://dev.narayana.im/narayana/telegabber/"
|
const CapsNode string = "https://dev.narayana.im/narayana/telegabber/"
|
||||||
|
|
||||||
|
|
@ -281,28 +284,11 @@ func SendMessageMarker(to string, from string, component *xmpp.Component, marker
|
||||||
|
|
||||||
// SendReactionMessage creates and sends a message stanza with a XEP-0444
|
// SendReactionMessage creates and sends a message stanza with a XEP-0444
|
||||||
// reaction set (see Reactions); an empty reactionEmojis clears id's reactions.
|
// reaction set (see Reactions); an empty reactionEmojis clears id's reactions.
|
||||||
func SendReactionMessage(to string, from string, component *xmpp.Component, id string, reactionEmojis []string, isGroupchat bool) {
|
func SendReactionMessage(to string, from string, component *xmpp.Component, id string, reactionEmojis []string, args ...args.V) {
|
||||||
sendMessageWrapper(to, from, component, SMReactions(&Reactions{
|
sendMessageWrapper(to, from, component, append(args, SMReactions(&Reactions{
|
||||||
Id: id,
|
Id: id,
|
||||||
Reactions: reactionEmojis,
|
Reactions: reactionEmojis,
|
||||||
}), SMIsGroupchat(isGroupchat))
|
}))...)
|
||||||
}
|
|
||||||
|
|
||||||
// SendMAMReactionMessage is SendReactionMessage wrapped as a XEP-0313 MAM
|
|
||||||
// result: entryId is this result's own id (distinct from id, the
|
|
||||||
// reacted-to message's own id); timestamp stands in for a historical
|
|
||||||
// reaction timestamp, which doesn't exist (only a reaction's current
|
|
||||||
// state, not when it was added).
|
|
||||||
func SendMAMReactionMessage(to string, from string, component *xmpp.Component, id string, reactionEmojis []string, entryId string, timestamp int64, mucJID string, mamQueryId string) {
|
|
||||||
sendMessageWrapper(to, from, component,
|
|
||||||
SMReactions(&Reactions{Id: id, Reactions: reactionEmojis}),
|
|
||||||
SMId(entryId),
|
|
||||||
SMStanzaId(entryId),
|
|
||||||
SMTimestamp(timestamp),
|
|
||||||
SMMamQueryId(mamQueryId),
|
|
||||||
SMMucJID(mucJID),
|
|
||||||
SMIsGroupchat(true),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendMUCInvite creates and send a MUC invitation message
|
// SendMUCInvite creates and send a MUC invitation message
|
||||||
|
|
@ -366,6 +352,9 @@ var SMInviteFrom = args.NewString()
|
||||||
// SMStanzaId is a XEP-0359 stanza id
|
// SMStanzaId is a XEP-0359 stanza id
|
||||||
var SMStanzaId = args.NewString()
|
var SMStanzaId = args.NewString()
|
||||||
|
|
||||||
|
// SMOccupantId is a XEP-0421 occupant id (a stable identifier for a MUC occupant)
|
||||||
|
var SMOccupantId = args.NewString()
|
||||||
|
|
||||||
// SMStatusCode is a XEP-0045 muc#user status code
|
// SMStatusCode is a XEP-0045 muc#user status code
|
||||||
var SMStatusCode = args.NewInt64()
|
var SMStatusCode = args.NewInt64()
|
||||||
|
|
||||||
|
|
@ -404,6 +393,7 @@ func sendMessageWrapper(to, from string, component *xmpp.Component, args ...args
|
||||||
errorCode := SMErrorCode.Get(args)
|
errorCode := SMErrorCode.Get(args)
|
||||||
inviteFrom := SMInviteFrom.Get(args)
|
inviteFrom := SMInviteFrom.Get(args)
|
||||||
stanzaId := SMStanzaId.Get(args)
|
stanzaId := SMStanzaId.Get(args)
|
||||||
|
occupantId := SMOccupantId.Get(args)
|
||||||
statusCode := SMStatusCode.Get(args)
|
statusCode := SMStatusCode.Get(args)
|
||||||
mamQueryId := SMMamQueryId.Get(args)
|
mamQueryId := SMMamQueryId.Get(args)
|
||||||
mucJID := SMMucJID.Get(args)
|
mucJID := SMMucJID.Get(args)
|
||||||
|
|
@ -619,6 +609,11 @@ func sendMessageWrapper(to, from string, component *xmpp.Component, args ...args
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if occupantId != "" {
|
||||||
|
message.Extensions = append(message.Extensions, extensions.OccupantId{
|
||||||
|
Id: occupantId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if isCarbon {
|
if isCarbon {
|
||||||
carbonMessage := extensions.ClientMessage{
|
carbonMessage := extensions.ClientMessage{
|
||||||
|
|
@ -807,6 +802,9 @@ var SPMUCNick = args.NewString()
|
||||||
// SPMUCJid is a real jid of a MUC member
|
// SPMUCJid is a real jid of a MUC member
|
||||||
var SPMUCJid = args.NewString()
|
var SPMUCJid = args.NewString()
|
||||||
|
|
||||||
|
// SPOccupantId is a XEP-0421 occupant id (a stable identifier for a MUC occupant)
|
||||||
|
var SPOccupantId = args.NewString()
|
||||||
|
|
||||||
// SPMUCStatusCodes is a set of XEP-0045 MUC status codes
|
// SPMUCStatusCodes is a set of XEP-0045 MUC status codes
|
||||||
var SPMUCStatusCodes = args.New()
|
var SPMUCStatusCodes = args.New()
|
||||||
|
|
||||||
|
|
@ -883,6 +881,14 @@ func newPresence(bareJid string, to string, args ...args.V) stanza.Presence {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if SPOccupantId.IsSet(args) {
|
||||||
|
occupantId := SPOccupantId.Get(args)
|
||||||
|
if occupantId != "" {
|
||||||
|
presence.Extensions = append(presence.Extensions, extensions.OccupantId{
|
||||||
|
Id: occupantId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
if SPMUCAffiliation.IsSet(args) {
|
if SPMUCAffiliation.IsSet(args) {
|
||||||
affiliation := SPMUCAffiliation.Get(args)
|
affiliation := SPMUCAffiliation.Get(args)
|
||||||
if affiliation != "" {
|
if affiliation != "" {
|
||||||
|
|
|
||||||
|
|
@ -298,13 +298,17 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) {
|
||||||
var replyId int64
|
var replyId int64
|
||||||
text := msg.Body
|
text := msg.Body
|
||||||
|
|
||||||
// OMEMO decrypt hook - personal-chat pseudo-JIDs only (true
|
// OMEMO decrypt hook - personal-chat pseudo-JIDs only, XEP-0045
|
||||||
// MUC/XEP-0045 groupchat OMEMO is out of scope entirely, not
|
// groupchat OMEMO is out of scope entirely. A successful
|
||||||
// deferred - see the plan doc). A successful decrypt is itself
|
// decrypt marks the chat OMEMO-active. Groupchat OMEMO is
|
||||||
// the auto-upgrade signal for this chat (enablement trigger 2);
|
// bounced rather than silently dropped, so the sender gets a
|
||||||
// the account-wide config toggle (trigger 1) is a separate,
|
// clear signal instead of treating it as delivered.
|
||||||
// not-yet-wired-up piece (M4).
|
if isGroupchat {
|
||||||
if !isGroupchat {
|
if hasOMEMOPayload(msg) {
|
||||||
|
gateway.SendErrorMessageWithBody(msg.From, msg.To, msg.Body, "OMEMO encryption is not supported in this room", msg.Id, 406, true, component)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if backend, backendOk := gateway.E2EE.Backend(); backendOk {
|
if backend, backendOk := gateway.E2EE.Backend(); backendOk {
|
||||||
env, hasEnv, decErr := decodeOMEMOEnvelope(msg)
|
env, hasEnv, decErr := decodeOMEMOEnvelope(msg)
|
||||||
if decErr != nil {
|
if decErr != nil {
|
||||||
|
|
@ -444,14 +448,14 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) {
|
||||||
tgMessage,
|
tgMessage,
|
||||||
msg.Id,
|
msg.Id,
|
||||||
false,
|
false,
|
||||||
msg.To + "/" + session.GetMUCNickname(session.GetSenderId(tgMessage.SenderId)),
|
msg.To + "/" + session.GetMUCNickname(toID, session.GetSenderId(tgMessage.SenderId)),
|
||||||
[]string{msg.From},
|
[]string{msg.From},
|
||||||
"",
|
"",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if isCommand && isGroupchat && session.Session.MUC {
|
} else if isCommand && isGroupchat && session.Session.MUC {
|
||||||
// pong outgoing commands back to groupchats
|
// pong outgoing commands back to groupchats
|
||||||
gateway.SendMessage(msg.From, msg.To+"/"+session.GetMUCNickname(0), component, gateway.SMBody(text), gateway.SMIsGroupchat(isGroupchat))
|
gateway.SendMessage(msg.From, msg.To+"/"+session.GetMUCNickname(toID, 0), component, gateway.SMBody(text), gateway.SMIsGroupchat(isGroupchat), gateway.SMOccupantId(gateway.CHATNODE(session.MyId())))
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
// if a message failed to edit on Telegram side, match new XMPP ID with old Telegram ID anyway
|
// if a message failed to edit on Telegram side, match new XMPP ID with old Telegram ID anyway
|
||||||
|
|
@ -903,6 +907,7 @@ func handleMUCUnavailable(component *xmpp.Component, p stanza.Presence, session
|
||||||
gateway.SPMUCAffiliation("member"),
|
gateway.SPMUCAffiliation("member"),
|
||||||
gateway.SPMUCRole("none"),
|
gateway.SPMUCRole("none"),
|
||||||
gateway.SPMUCJid(p.From),
|
gateway.SPMUCJid(p.From),
|
||||||
|
gateway.SPOccupantId(gateway.CHATNODE(session.MyId())),
|
||||||
gateway.SPMUCStatusCodes([]uint16{110}),
|
gateway.SPMUCStatusCodes([]uint16{110}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -1129,7 +1134,6 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoInfo) {
|
||||||
"http://jabber.org/protocol/muc",
|
"http://jabber.org/protocol/muc",
|
||||||
"muc_persistent",
|
"muc_persistent",
|
||||||
"muc_hidden",
|
"muc_hidden",
|
||||||
"muc_membersonly",
|
|
||||||
"muc_moderated",
|
"muc_moderated",
|
||||||
"muc_nonanonymous",
|
"muc_nonanonymous",
|
||||||
"muc_unsecured",
|
"muc_unsecured",
|
||||||
|
|
@ -1142,6 +1146,7 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoInfo) {
|
||||||
gateway.NSReactions,
|
gateway.NSReactions,
|
||||||
"urn:xmpp:sid:0",
|
"urn:xmpp:sid:0",
|
||||||
"vcard-temp",
|
"vcard-temp",
|
||||||
|
gateway.NSOccupantId,
|
||||||
)
|
)
|
||||||
fields := []*stanza.Field{
|
fields := []*stanza.Field{
|
||||||
&stanza.Field{
|
&stanza.Field{
|
||||||
|
|
@ -1215,7 +1220,7 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoInfo) {
|
||||||
chat, _, err := session.GetContactByID(toID, nil, true)
|
chat, _, err := session.GetContactByID(toID, nil, true)
|
||||||
if err == nil && session.IsGroup(chat) {
|
if err == nil && session.IsGroup(chat) {
|
||||||
disco.SetNode(di.Node)
|
disco.SetNode(di.Node)
|
||||||
disco.AddIdentity(session.GetMUCNickname(0), "conference", "text")
|
disco.AddIdentity(session.GetMUCNickname(toID, 0), "conference", "text")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1340,7 +1345,7 @@ func handleGetQueryRegister(s xmpp.Sender, iq *stanza.IQ) {
|
||||||
}
|
}
|
||||||
defer gateway.ResumableSend(component, answer)
|
defer gateway.ResumableSend(component, answer)
|
||||||
|
|
||||||
_, toOk, toIsGroup := toToID(iq.To)
|
toID, toOk, toIsGroup := toToID(iq.To)
|
||||||
|
|
||||||
bare, _, ok := gateway.SplitJID(iq.From)
|
bare, _, ok := gateway.SplitJID(iq.From)
|
||||||
var session *telegram.Client
|
var session *telegram.Client
|
||||||
|
|
@ -1353,7 +1358,7 @@ func handleGetQueryRegister(s xmpp.Sender, iq *stanza.IQ) {
|
||||||
if toIsGroup {
|
if toIsGroup {
|
||||||
nickname := "me"
|
nickname := "me"
|
||||||
if sessionOk {
|
if sessionOk {
|
||||||
nickname = session.GetMUCNickname(0)
|
nickname = session.GetMUCNickname(toID, 0)
|
||||||
}
|
}
|
||||||
answer.Payload = extensions.QueryRegister{
|
answer.Payload = extensions.QueryRegister{
|
||||||
Instructions: "MUC username is static",
|
Instructions: "MUC username is static",
|
||||||
|
|
@ -2317,7 +2322,7 @@ func handleSetQueryMucAdmin(s xmpp.Sender, iq *stanza.IQ, query *extensions.Quer
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
nick := session.GetMUCNickname(userID)
|
nick := session.GetMUCNickname(toID, userID)
|
||||||
|
|
||||||
var status telegram.ChatMemberStatus
|
var status telegram.ChatMemberStatus
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue