mirror of
https://dev.narayana.im/narayana/telegabber.git
synced 2026-08-05 04:07:07 +00:00
Advertise MUC avatars via XEP-0486
This commit is contained in:
parent
ab9d041cef
commit
a01e864d71
5 changed files with 93 additions and 51 deletions
|
|
@ -91,8 +91,8 @@ type Client struct {
|
||||||
XmppClientFeatures map[string]*[]string
|
XmppClientFeatures map[string]*[]string
|
||||||
XmppClientFeaturesLock sync.Mutex
|
XmppClientFeaturesLock sync.Mutex
|
||||||
|
|
||||||
AvatarHashes map[int64]*HashedAvatar
|
avatarHashes map[int64]*HashedAvatar
|
||||||
AvatarHashesLock sync.Mutex
|
avatarHashesLock sync.Mutex
|
||||||
|
|
||||||
locks clientLocks
|
locks clientLocks
|
||||||
SendMessageLock sync.Mutex
|
SendMessageLock sync.Mutex
|
||||||
|
|
@ -182,7 +182,7 @@ func NewClient(conf config.TelegramConfig, jid string, component *xmpp.Component
|
||||||
lastMsgHashes: make(map[int64]uint64),
|
lastMsgHashes: make(map[int64]uint64),
|
||||||
lastMsgIds: make(map[int64]string),
|
lastMsgIds: make(map[int64]string),
|
||||||
XmppClientFeatures: make(map[string]*[]string),
|
XmppClientFeatures: make(map[string]*[]string),
|
||||||
AvatarHashes: make(map[int64]*HashedAvatar),
|
avatarHashes: make(map[int64]*HashedAvatar),
|
||||||
locks: clientLocks{
|
locks: clientLocks{
|
||||||
chatMessageLocks: make(map[int64]*sync.Mutex),
|
chatMessageLocks: make(map[int64]*sync.Mutex),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -408,12 +408,12 @@ func (c *Client) getFileData(tgFile *client.File, typ byte) string {
|
||||||
|
|
||||||
// SetEmptyAvatarHash puts a dummy value into the cache to avoid attempting to fetch surely missing avatars
|
// SetEmptyAvatarHash puts a dummy value into the cache to avoid attempting to fetch surely missing avatars
|
||||||
func (c *Client) SetEmptyAvatarHash(chatId int64) {
|
func (c *Client) SetEmptyAvatarHash(chatId int64) {
|
||||||
c.AvatarHashesLock.Lock()
|
c.avatarHashesLock.Lock()
|
||||||
c.AvatarHashes[chatId] = &HashedAvatar{
|
c.avatarHashes[chatId] = &HashedAvatar{
|
||||||
Hash: "",
|
Hash: "",
|
||||||
File: 0,
|
File: 0,
|
||||||
}
|
}
|
||||||
c.AvatarHashesLock.Unlock()
|
c.avatarHashesLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPhotoSize return at least a rough size
|
// GetPhotoSize return at least a rough size
|
||||||
|
|
@ -431,12 +431,12 @@ func (c *Client) GetPhotoSize(photo *client.File) int64 {
|
||||||
// GetPhotoSha1 computes the photo hash
|
// GetPhotoSha1 computes the photo hash
|
||||||
func (c *Client) GetPhotoSha1(photo *client.File, chatId int64) string {
|
func (c *Client) GetPhotoSha1(photo *client.File, chatId int64) string {
|
||||||
sha1 := c.getFileData(photo, typeFileDataSha1)
|
sha1 := c.getFileData(photo, typeFileDataSha1)
|
||||||
c.AvatarHashesLock.Lock()
|
c.avatarHashesLock.Lock()
|
||||||
c.AvatarHashes[chatId] = &HashedAvatar{
|
c.avatarHashes[chatId] = &HashedAvatar{
|
||||||
Hash: sha1,
|
Hash: sha1,
|
||||||
File: photo.Id,
|
File: photo.Id,
|
||||||
}
|
}
|
||||||
c.AvatarHashesLock.Unlock()
|
c.avatarHashesLock.Unlock()
|
||||||
return sha1
|
return sha1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -445,6 +445,32 @@ func (c *Client) GetPhotoBase64(photo *client.File) string {
|
||||||
return c.getFileData(photo, typeFileDataBase64)
|
return c.getFileData(photo, typeFileDataBase64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetHashedAvatar obtain the avatar hash from cache or requests the avatar file immediately to calculate it
|
||||||
|
func (c *Client) GetHashedAvatar(chatId int64) *HashedAvatar {
|
||||||
|
c.avatarHashesLock.Lock()
|
||||||
|
hashedAvatar, ok := c.avatarHashes[chatId]
|
||||||
|
c.avatarHashesLock.Unlock()
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
log.Info("Could not find avatar in cache, fetching immediately")
|
||||||
|
|
||||||
|
chat, _, err := c.GetContactByID(chatId, nil)
|
||||||
|
if err != nil || chat == nil || chat.Photo == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
file := chat.Photo.Small
|
||||||
|
|
||||||
|
sha1 := c.GetPhotoSha1(file, chatId)
|
||||||
|
hashedAvatar = &HashedAvatar{
|
||||||
|
Hash: sha1,
|
||||||
|
File: file.Id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashedAvatar
|
||||||
|
}
|
||||||
|
|
||||||
// ProcessStatusUpdate sets contact status
|
// ProcessStatusUpdate sets contact status
|
||||||
func (c *Client) ProcessStatusUpdate(chatID int64, status string, show string, oldArgs ...args.V) error {
|
func (c *Client) ProcessStatusUpdate(chatID int64, status string, show string, oldArgs ...args.V) error {
|
||||||
if !c.Online() {
|
if !c.Online() {
|
||||||
|
|
@ -1776,6 +1802,12 @@ func (c *Client) SendMessageToGateway(chatId int64, message *client.Message, id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if isGroupchat {
|
||||||
|
for _, jid := range jids {
|
||||||
|
gateway.SendMUCStatusCode(jid, gateway.MUCJID(chatId), c.xmpp, 104)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -221,9 +221,10 @@ type MessageXLegacyInviteExtension struct {
|
||||||
|
|
||||||
// MessageXMucUserExtension is from XEP-0045
|
// MessageXMucUserExtension is from XEP-0045
|
||||||
type MessageXMucUserExtension struct {
|
type MessageXMucUserExtension struct {
|
||||||
XMLName xml.Name `xml:"http://jabber.org/protocol/muc#user x"`
|
XMLName xml.Name `xml:"http://jabber.org/protocol/muc#user x"`
|
||||||
Invite MessageXMucUserInvite
|
Invite *MessageXMucUserInvite `xml:"invite,omitempty"`
|
||||||
Password string `xml:"password,omitempty"`
|
Status *MessageXMucUserStatus `xml:"status,omitempty"`
|
||||||
|
Password string `xml:"password,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MessageXMucUserInvite is from XEP-0045
|
// MessageXMucUserInvite is from XEP-0045
|
||||||
|
|
@ -240,6 +241,12 @@ type MessageXMucUserInviteContinue struct {
|
||||||
Thread string `xml:"thread,attr,omitempty"`
|
Thread string `xml:"thread,attr,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MessageXMucUserStatus is from XEP-0486
|
||||||
|
type MessageXMucUserStatus struct {
|
||||||
|
XMLName xml.Name `xml:"status"`
|
||||||
|
Code string `xml:"code,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
// PresenceXMucUserExtension is from XEP-0045
|
// PresenceXMucUserExtension is from XEP-0045
|
||||||
type PresenceXMucUserExtension struct {
|
type PresenceXMucUserExtension struct {
|
||||||
XMLName xml.Name `xml:"http://jabber.org/protocol/muc#user x"`
|
XMLName xml.Name `xml:"http://jabber.org/protocol/muc#user x"`
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ func MUCJID(chatId int64) string {
|
||||||
|
|
||||||
// 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, stanzaId string) {
|
func SendMessage(to, from, body, id string, component *xmpp.Component, reply *Reply, timestamp int64, replaceId string, isCarbon, isGroupchat, requestReceipt bool, originalFrom, stanzaId string) {
|
||||||
sendMessageWrapper(to, from, body, "", "", id, component, reply, nil, timestamp, "", replaceId, isCarbon, isGroupchat, false, requestReceipt, originalFrom, 0, "", stanzaId)
|
sendMessageWrapper(to, from, body, "", "", id, component, reply, nil, timestamp, "", replaceId, isCarbon, isGroupchat, false, requestReceipt, originalFrom, 0, "", stanzaId, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendServiceMessage creates and sends a simple message stanza from transport
|
// SendServiceMessage creates and sends a simple message stanza from transport
|
||||||
|
|
@ -98,7 +98,7 @@ func SendServiceMessage(to, body string, component *xmpp.Component) {
|
||||||
if uuid, err := uuid.NewRandom(); err == nil {
|
if uuid, err := uuid.NewRandom(); err == nil {
|
||||||
id = uuid.String()
|
id = uuid.String()
|
||||||
}
|
}
|
||||||
sendMessageWrapper(to, "", body, "", "", id, component, nil, nil, 0, "", "", false, false, false, false, "", 0, "", "")
|
sendMessageWrapper(to, "", body, "", "", id, component, nil, nil, 0, "", "", false, false, false, false, "", 0, "", "", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendTextMessage creates and sends a simple message stanza
|
// SendTextMessage creates and sends a simple message stanza
|
||||||
|
|
@ -107,27 +107,27 @@ func SendTextMessage(to, from, body string, component *xmpp.Component, isGroupch
|
||||||
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, isGroupchat, false, false, "", 0, "", "")
|
sendMessageWrapper(to, from, body, "", "", id, component, nil, nil, 0, "", "", false, isGroupchat, false, false, "", 0, "", "", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendErrorMessage creates and sends an error message stanza
|
// SendErrorMessage creates and sends an error message stanza
|
||||||
func SendErrorMessage(to, from, text string, code int, isGroupchat bool, component *xmpp.Component) {
|
func SendErrorMessage(to, from, text string, code int, isGroupchat bool, component *xmpp.Component) {
|
||||||
sendMessageWrapper(to, from, "", "", text, "", component, nil, nil, 0, "", "", false, isGroupchat, false, false, "", code, "", "")
|
sendMessageWrapper(to, from, "", "", text, "", component, nil, nil, 0, "", "", false, isGroupchat, false, false, "", code, "", "", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendErrorMessageWithBody creates and sends an error message stanza with body payload
|
// SendErrorMessageWithBody creates and sends an error message stanza with body payload
|
||||||
func SendErrorMessageWithBody(to, from, body, errorText, id string, code int, isGroupchat bool, component *xmpp.Component) {
|
func SendErrorMessageWithBody(to, from, body, errorText, id string, code int, isGroupchat bool, component *xmpp.Component) {
|
||||||
sendMessageWrapper(to, from, body, "", errorText, id, component, nil, nil, 0, "", "", false, isGroupchat, false, false, "", code, "", "")
|
sendMessageWrapper(to, from, body, "", errorText, id, component, nil, nil, 0, "", "", false, isGroupchat, false, false, "", code, "", "", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendMessageWithOOB creates and sends a message stanza with OOB URL
|
// SendMessageWithOOB creates and sends a message stanza with OOB URL
|
||||||
func SendMessageWithOOB(to, from, body, id string, component *xmpp.Component, reply *Reply, timestamp int64, oob, replaceId string, isCarbon, isGroupchat, requestReceipt bool, originalFrom, stanzaId string) {
|
func SendMessageWithOOB(to, from, body, id string, component *xmpp.Component, reply *Reply, timestamp int64, oob, replaceId string, isCarbon, isGroupchat, requestReceipt bool, originalFrom, stanzaId string) {
|
||||||
sendMessageWrapper(to, from, body, "", "", id, component, reply, nil, timestamp, oob, replaceId, isCarbon, isGroupchat, false, requestReceipt, originalFrom, 0, "", stanzaId)
|
sendMessageWrapper(to, from, body, "", "", id, component, reply, nil, timestamp, oob, replaceId, isCarbon, isGroupchat, false, requestReceipt, originalFrom, 0, "", stanzaId, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendSubjectMessage creates and sends a MUC subject
|
// SendSubjectMessage creates and sends a MUC subject
|
||||||
func SendSubjectMessage(to, from, subject, id string, component *xmpp.Component, timestamp int64) {
|
func SendSubjectMessage(to, from, subject, id string, component *xmpp.Component, timestamp int64) {
|
||||||
sendMessageWrapper(to, from, "", subject, "", id, component, nil, nil, timestamp, "", "", false, true, true, false, "", 0, "", "")
|
sendMessageWrapper(to, from, "", subject, "", id, component, nil, nil, timestamp, "", "", false, true, true, false, "", 0, "", "", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendMessageMarker creates and sends a message stanza with a XEP-0333 marker
|
// SendMessageMarker creates and sends a message stanza with a XEP-0333 marker
|
||||||
|
|
@ -135,15 +135,20 @@ func SendMessageMarker(to string, from string, component *xmpp.Component, marker
|
||||||
sendMessageWrapper(to, from, "", "", "", "", component, nil, &marker{
|
sendMessageWrapper(to, from, "", "", "", "", component, nil, &marker{
|
||||||
Type: markerType,
|
Type: markerType,
|
||||||
Id: markerId,
|
Id: markerId,
|
||||||
}, 0, "", "", false, false, false, false, "", 0, "", "")
|
}, 0, "", "", false, false, false, false, "", 0, "", "", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendMUCInvite creates and send a MUC invitation message
|
// SendMUCInvite creates and send a MUC invitation message
|
||||||
func SendMUCInvite(to string, from string, component *xmpp.Component, inviteFrom string) {
|
func SendMUCInvite(to string, from string, component *xmpp.Component, inviteFrom string) {
|
||||||
sendMessageWrapper(to, from, "", "", "", "", component, nil, nil, 0, "", "", false, false, false, false, "", 0, inviteFrom, "")
|
sendMessageWrapper(to, from, "", "", "", "", component, nil, nil, 0, "", "", false, false, false, false, "", 0, inviteFrom, "", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendMessageWrapper(to, from, body, subject, errorText, id string, component *xmpp.Component, reply *Reply, marker *marker, timestamp int64, oob, replaceId string, isCarbon, isGroupchat, forceSubject, requestReceipt bool, originalFrom string, errorCode int, inviteFrom, stanzaId string) {
|
// SendMUCStatusCode creates a groupchat message with a muc#user status code
|
||||||
|
func SendMUCStatusCode(to string, from string, component *xmpp.Component, statusCode int64) {
|
||||||
|
sendMessageWrapper(to, from, "", "", "", "", component, nil, nil, 0, "", "", false, true, false, false, "", 0, "", "", statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendMessageWrapper(to, from, body, subject, errorText, id string, component *xmpp.Component, reply *Reply, marker *marker, timestamp int64, oob, replaceId string, isCarbon, isGroupchat, forceSubject, requestReceipt bool, originalFrom string, errorCode int, inviteFrom, stanzaId string, statusCode int64) {
|
||||||
toJid, err := stanza.NewJid(to)
|
toJid, err := stanza.NewJid(to)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithFields(log.Fields{
|
log.WithFields(log.Fields{
|
||||||
|
|
@ -297,15 +302,23 @@ func sendMessageWrapper(to, from, body, subject, errorText, id string, component
|
||||||
if replaceId != "" {
|
if replaceId != "" {
|
||||||
message.Extensions = append(message.Extensions, extensions.Replace{Id: replaceId})
|
message.Extensions = append(message.Extensions, extensions.Replace{Id: replaceId})
|
||||||
}
|
}
|
||||||
|
var userExt extensions.MessageXMucUserExtension
|
||||||
if inviteFrom != "" {
|
if inviteFrom != "" {
|
||||||
message.Extensions = append(message.Extensions, extensions.MessageXMucUserExtension{
|
userExt.Invite = &extensions.MessageXMucUserInvite{
|
||||||
Invite: extensions.MessageXMucUserInvite{
|
From: inviteFrom,
|
||||||
From: inviteFrom,
|
}
|
||||||
},
|
message.Extensions = append(message.Extensions, extensions.MessageXLegacyInviteExtension{
|
||||||
}, extensions.MessageXLegacyInviteExtension{
|
|
||||||
Jid: messageFrom,
|
Jid: messageFrom,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if statusCode != 0 {
|
||||||
|
userExt.Status = &extensions.MessageXMucUserStatus{
|
||||||
|
Code: strconv.FormatInt(statusCode, 10),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if inviteFrom != "" || statusCode != 0 {
|
||||||
|
message.Extensions = append(message.Extensions, userExt)
|
||||||
|
}
|
||||||
if stanzaId != "" {
|
if stanzaId != "" {
|
||||||
message.Extensions = append(message.Extensions, extensions.MessageStanzaId{
|
message.Extensions = append(message.Extensions, extensions.MessageStanzaId{
|
||||||
Id: stanzaId,
|
Id: stanzaId,
|
||||||
|
|
|
||||||
|
|
@ -745,12 +745,12 @@ func handleGetVcardIq(s xmpp.Sender, iq *stanza.IQ, typ byte) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
toParts := strings.Split(iq.To, "@")
|
toID, toOk, _ := toToID(iq.To)
|
||||||
toID, err := strconv.ParseInt(toParts[0], 10, 64)
|
if !toOk {
|
||||||
if err != nil {
|
|
||||||
log.Error("Invalid IQ to")
|
log.Error("Invalid IQ to")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
info, err := session.GetVcardInfo(toID)
|
info, err := session.GetVcardInfo(toID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
|
|
@ -826,27 +826,7 @@ func handleGetAvatarDataIq(s xmpp.Sender, iq *stanza.IQ, pubsub *stanza.PubSubGe
|
||||||
|
|
||||||
defer gateway.ResumableSend(component, &answer)
|
defer gateway.ResumableSend(component, &answer)
|
||||||
|
|
||||||
hashedAvatar, ok := session.AvatarHashes[chatId]
|
hashedAvatar := session.GetHashedAvatar(chatId)
|
||||||
if !ok {
|
|
||||||
log.Info("Could not find avatar in cache, fetching immediately")
|
|
||||||
|
|
||||||
chat, _, err := session.GetContactByID(chatId, nil)
|
|
||||||
if err != nil || chat == nil || chat.Photo == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
file := chat.Photo.Small
|
|
||||||
|
|
||||||
sha1 := session.GetPhotoSha1(file, chatId)
|
|
||||||
hashedAvatar = &telegram.HashedAvatar{
|
|
||||||
Hash: sha1,
|
|
||||||
File: file.Id,
|
|
||||||
}
|
|
||||||
|
|
||||||
session.AvatarHashesLock.Lock()
|
|
||||||
session.AvatarHashes[chatId] = hashedAvatar
|
|
||||||
session.AvatarHashesLock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
if id != "" && hashedAvatar.Hash != id {
|
if id != "" && hashedAvatar.Hash != id {
|
||||||
log.Infof("Cache contains %v hash for chat %v, but %v was requested; aborting", hashedAvatar.Hash, iq.To, id)
|
log.Infof("Cache contains %v hash for chat %v, but %v was requested; aborting", hashedAvatar.Hash, iq.To, id)
|
||||||
|
|
@ -963,6 +943,7 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoInfo) {
|
||||||
"http://jabber.org/protocol/muc#stable_id",
|
"http://jabber.org/protocol/muc#stable_id",
|
||||||
"jabber:iq:register",
|
"jabber:iq:register",
|
||||||
"urn:xmpp:sid:0",
|
"urn:xmpp:sid:0",
|
||||||
|
"vcard-temp",
|
||||||
)
|
)
|
||||||
fields := []*stanza.Field{
|
fields := []*stanza.Field{
|
||||||
&stanza.Field{
|
&stanza.Field{
|
||||||
|
|
@ -982,6 +963,15 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoInfo) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hashedAvatar := session.GetHashedAvatar(toID)
|
||||||
|
if hashedAvatar != nil {
|
||||||
|
fields = append(fields, &stanza.Field{
|
||||||
|
Var: "muc#roominfo_avatarhash",
|
||||||
|
Label: "Avatar hash",
|
||||||
|
ValuesList: []string{hashedAvatar.Hash},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
disco.Form = stanza.NewForm(fields, "result")
|
disco.Form = stanza.NewForm(fields, "result")
|
||||||
}
|
}
|
||||||
} else if !toOk {
|
} else if !toOk {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue