Show XEP-0461 replies from Telegram

This commit is contained in:
Bohdan Horbeshko 2023-03-05 03:00:53 -05:00
parent 6e32c62f8d
commit 4a5b83dff5
8 changed files with 168 additions and 58 deletions

View file

@ -184,6 +184,7 @@ func (c *Client) unsubscribe(chatID int64) error {
func (c *Client) sendMessagesReverse(chatID int64, messages []*client.Message) { func (c *Client) sendMessagesReverse(chatID int64, messages []*client.Message) {
for i := len(messages) - 1; i >= 0; i-- { for i := len(messages) - 1; i >= 0; i-- {
message := messages[i] message := messages[i]
reply, _ := c.getMessageReply(message)
gateway.SendMessage( gateway.SendMessage(
c.jid, c.jid,
@ -191,6 +192,7 @@ func (c *Client) sendMessagesReverse(chatID int64, messages []*client.Message) {
c.formatMessage(0, 0, false, message), c.formatMessage(0, 0, false, message),
strconv.FormatInt(message.Id, 10), strconv.FormatInt(message.Id, 10),
c.xmpp, c.xmpp,
reply,
) )
} }
} }

View file

@ -219,20 +219,20 @@ func (c *Client) interactor() {
if c.Session.Login != "" { if c.Session.Login != "" {
c.authorizer.PhoneNumber <- c.Session.Login c.authorizer.PhoneNumber <- c.Session.Login
} else { } else {
gateway.SendMessage(c.jid, "", "Please, enter your Telegram login via /login 12345", "", c.xmpp) gateway.SendServiceMessage(c.jid, "Please, enter your Telegram login via /login 12345", c.xmpp)
} }
// stage 1: wait for auth code // stage 1: wait for auth code
case client.TypeAuthorizationStateWaitCode: case client.TypeAuthorizationStateWaitCode:
log.Warn("Waiting for authorization code...") log.Warn("Waiting for authorization code...")
gateway.SendMessage(c.jid, "", "Please, enter authorization code via /code 12345", "", c.xmpp) gateway.SendServiceMessage(c.jid, "Please, enter authorization code via /code 12345", c.xmpp)
// stage 1b: wait for registration // stage 1b: wait for registration
case client.TypeAuthorizationStateWaitRegistration: case client.TypeAuthorizationStateWaitRegistration:
log.Warn("Waiting for full name...") log.Warn("Waiting for full name...")
gateway.SendMessage(c.jid, "", "This number is not registered yet! Please, enter your name via /setname John Doe", "", c.xmpp) gateway.SendServiceMessage(c.jid, "This number is not registered yet! Please, enter your name via /setname John Doe", c.xmpp)
// stage 2: wait for 2fa // stage 2: wait for 2fa
case client.TypeAuthorizationStateWaitPassword: case client.TypeAuthorizationStateWaitPassword:
log.Warn("Waiting for 2FA password...") log.Warn("Waiting for 2FA password...")
gateway.SendMessage(c.jid, "", "Please, enter 2FA passphrase via /password 12345", "", c.xmpp) gateway.SendServiceMessage(c.jid, "Please, enter 2FA passphrase via /password 12345", c.xmpp)
} }
} }
} }

View file

@ -242,7 +242,7 @@ func (c *Client) updateMessageContent(update *client.UpdateMessageContent) {
textContent.Text.Entities, textContent.Text.Entities,
markupFunction, markupFunction,
)) ))
gateway.SendMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, "e" + strconv.FormatInt(update.MessageId, 10), c.xmpp) gateway.SendMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, "e" + strconv.FormatInt(update.MessageId, 10), c.xmpp, nil)
} }
} }
@ -256,7 +256,7 @@ func (c *Client) updateDeleteMessages(update *client.UpdateDeleteMessages) {
deleteChar = "✗ " deleteChar = "✗ "
} }
text := deleteChar + strings.Join(int64SliceToStringSlice(update.MessageIds), ",") text := deleteChar + strings.Join(int64SliceToStringSlice(update.MessageIds), ",")
gateway.SendMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, "", c.xmpp) gateway.SendTextMessage(c.jid, strconv.FormatInt(update.ChatId, 10), text, c.xmpp)
} }
} }

View file

@ -261,6 +261,46 @@ func (c *Client) formatContact(chatID int64) string {
return str return str
} }
func (c *Client) getSenderId(message *client.Message) (senderId int64) {
if message.SenderId != nil {
switch message.SenderId.MessageSenderType() {
case client.TypeMessageSenderUser:
senderUser, _ := message.SenderId.(*client.MessageSenderUser)
senderId = senderUser.UserId
case client.TypeMessageSenderChat:
senderChat, _ := message.SenderId.(*client.MessageSenderChat)
senderId = senderChat.ChatId
}
}
return
}
func (c *Client) formatSender(message *client.Message) string {
return c.formatContact(c.getSenderId(message))
}
func (c *Client) getMessageReply(message *client.Message) (reply *gateway.Reply, replyMsg *client.Message) {
if message.ReplyToMessageId != 0 {
var err error
replyMsg, err = c.client.GetMessage(&client.GetMessageRequest{
ChatId: message.ChatId,
MessageId: message.ReplyToMessageId,
})
if err != nil {
log.Errorf("<error fetching message: %s>", err.Error())
return
}
reply = &gateway.Reply {
Author: fmt.Sprintf("%v@%s", c.getSenderId(replyMsg), gateway.Jid.Full()),
Id: strconv.FormatInt(message.ReplyToMessageId, 10),
}
}
return
}
func (c *Client) formatMessage(chatID int64, messageID int64, preview bool, message *client.Message) string { func (c *Client) formatMessage(chatID int64, messageID int64, preview bool, message *client.Message) string {
var err error var err error
if message == nil { if message == nil {
@ -279,18 +319,7 @@ func (c *Client) formatMessage(chatID int64, messageID int64, preview bool, mess
var str strings.Builder var str strings.Builder
// add messageid and sender // add messageid and sender
var senderId int64 str.WriteString(fmt.Sprintf("%v | %s | ", message.Id, c.formatSender(message)))
if message.SenderId != nil {
switch message.SenderId.MessageSenderType() {
case client.TypeMessageSenderUser:
senderUser, _ := message.SenderId.(*client.MessageSenderUser)
senderId = senderUser.UserId
case client.TypeMessageSenderChat:
senderChat, _ := message.SenderId.(*client.MessageSenderChat)
senderId = senderChat.ChatId
}
}
str.WriteString(fmt.Sprintf("%v | %s | ", message.Id, c.formatContact(senderId)))
// add date // add date
if !preview { if !preview {
str.WriteString( str.WriteString(
@ -681,7 +710,7 @@ func (c *Client) contentToFile(content client.MessageContent) (*client.File, *cl
return nil, nil return nil, nil
} }
func (c *Client) messageToPrefix(message *client.Message, previewString string, fileString string) string { func (c *Client) messageToPrefix(message *client.Message, previewString string, fileString string, replyMsg *client.Message) string {
prefix := []string{} prefix := []string{}
// message direction // message direction
var directionChar string var directionChar string
@ -700,21 +729,12 @@ func (c *Client) messageToPrefix(message *client.Message, previewString string,
} }
prefix = append(prefix, directionChar+strconv.FormatInt(message.Id, 10)) prefix = append(prefix, directionChar+strconv.FormatInt(message.Id, 10))
// show sender in group chats // show sender in group chats
if message.ChatId < 0 && message.SenderId != nil { if message.ChatId < 0 {
var senderId int64 prefix = append(prefix, c.formatSender(message))
switch message.SenderId.MessageSenderType() {
case client.TypeMessageSenderUser:
senderUser, _ := message.SenderId.(*client.MessageSenderUser)
senderId = senderUser.UserId
case client.TypeMessageSenderChat:
senderChat, _ := message.SenderId.(*client.MessageSenderChat)
senderId = senderChat.ChatId
}
prefix = append(prefix, c.formatContact(senderId))
} }
// reply to // reply to
if message.ReplyToMessageId != 0 { if message.ReplyToMessageId != 0 {
prefix = append(prefix, "reply: "+c.formatMessage(message.ChatId, message.ReplyToMessageId, true, nil)) prefix = append(prefix, "reply: "+c.formatMessage(message.ChatId, message.ReplyToMessageId, true, replyMsg))
} }
if message.ForwardInfo != nil { if message.ForwardInfo != nil {
prefix = append(prefix, "fwd: "+c.formatForward(message.ForwardInfo)) prefix = append(prefix, "fwd: "+c.formatForward(message.ForwardInfo))
@ -750,6 +770,9 @@ func (c *Client) ensureDownloadFile(file *client.File) *client.File {
// ProcessIncomingMessage transfers a message to XMPP side and marks it as read on Telegram side // ProcessIncomingMessage transfers a message to XMPP side and marks it as read on Telegram side
func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) { func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) {
var text, oob, auxText string var text, oob, auxText string
reply, replyMsg := c.getMessageReply(message)
content := message.Content content := message.Content
if content != nil && content.MessageContentType() == client.TypeMessageChatChangePhoto { if content != nil && content.MessageContentType() == client.TypeMessageChatChangePhoto {
chat, err := c.client.GetChat(&client.GetChatRequest{ chat, err := c.client.GetChat(&client.GetChatRequest{
@ -783,7 +806,7 @@ func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) {
text = oob text = oob
} else if !c.Session.RawMessages { } else if !c.Session.RawMessages {
var prefix strings.Builder var prefix strings.Builder
prefix.WriteString(c.messageToPrefix(message, previewName, fileName)) prefix.WriteString(c.messageToPrefix(message, previewName, fileName, replyMsg))
if text != "" { if text != "" {
// \n if it is groupchat and message is not empty // \n if it is groupchat and message is not empty
if chatId < 0 { if chatId < 0 {
@ -808,9 +831,9 @@ func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) {
// forward message to XMPP // forward message to XMPP
sId := strconv.FormatInt(message.Id, 10) sId := strconv.FormatInt(message.Id, 10)
sChatId := strconv.FormatInt(chatId, 10) sChatId := strconv.FormatInt(chatId, 10)
gateway.SendMessageWithOOB(c.jid, sChatId, text, sId, c.xmpp, oob) gateway.SendMessageWithOOB(c.jid, sChatId, text, sId, c.xmpp, reply, oob)
if auxText != "" { if auxText != "" {
gateway.SendMessage(c.jid, sChatId, auxText, sId, c.xmpp) gateway.SendMessage(c.jid, sChatId, auxText, sId, c.xmpp, reply)
} }
} }
@ -825,7 +848,7 @@ func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid str
// try to execute commands // try to execute commands
response, isCommand := c.ProcessChatCommand(chatID, text) response, isCommand := c.ProcessChatCommand(chatID, text)
if response != "" { if response != "" {
gateway.SendMessage(returnJid, strconv.FormatInt(chatID, 10), response, "", c.xmpp) gateway.SendTextMessage(returnJid, strconv.FormatInt(chatID, 10), response, c.xmpp)
} }
// do not send on success // do not send on success
if isCommand { if isCommand {
@ -847,11 +870,10 @@ func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid str
if chatID != 0 && c.content.Upload != "" && strings.HasPrefix(text, c.content.Upload) { if chatID != 0 && c.content.Upload != "" && strings.HasPrefix(text, c.content.Upload) {
response, err := http.Get(text) response, err := http.Get(text)
if err != nil { if err != nil {
gateway.SendMessage( gateway.SendTextMessage(
returnJid, returnJid,
strconv.FormatInt(chatID, 10), strconv.FormatInt(chatID, 10),
fmt.Sprintf("Failed to fetch the uploaded file: %s", err.Error()), fmt.Sprintf("Failed to fetch the uploaded file: %s", err.Error()),
"",
c.xmpp, c.xmpp,
) )
return nil return nil
@ -860,11 +882,10 @@ func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid str
defer response.Body.Close() defer response.Body.Close()
if response.StatusCode != 200 { if response.StatusCode != 200 {
gateway.SendMessage( gateway.SendTextMessage(
returnJid, returnJid,
strconv.FormatInt(chatID, 10), strconv.FormatInt(chatID, 10),
fmt.Sprintf("Received status code %v", response.StatusCode), fmt.Sprintf("Received status code %v", response.StatusCode),
"",
c.xmpp, c.xmpp,
) )
return nil return nil
@ -872,22 +893,20 @@ func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid str
tempDir, err := ioutil.TempDir("", "telegabber-*") tempDir, err := ioutil.TempDir("", "telegabber-*")
if err != nil { if err != nil {
gateway.SendMessage( gateway.SendTextMessage(
returnJid, returnJid,
strconv.FormatInt(chatID, 10), strconv.FormatInt(chatID, 10),
fmt.Sprintf("Failed to create a temporary directory: %s", err.Error()), fmt.Sprintf("Failed to create a temporary directory: %s", err.Error()),
"",
c.xmpp, c.xmpp,
) )
return nil return nil
} }
tempFile, err := os.Create(filepath.Join(tempDir, filepath.Base(text))) tempFile, err := os.Create(filepath.Join(tempDir, filepath.Base(text)))
if err != nil { if err != nil {
gateway.SendMessage( gateway.SendTextMessage(
returnJid, returnJid,
strconv.FormatInt(chatID, 10), strconv.FormatInt(chatID, 10),
fmt.Sprintf("Failed to create a temporary file: %s", err.Error()), fmt.Sprintf("Failed to create a temporary file: %s", err.Error()),
"",
c.xmpp, c.xmpp,
) )
return nil return nil
@ -895,11 +914,10 @@ func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid str
_, err = io.Copy(tempFile, response.Body) _, err = io.Copy(tempFile, response.Body)
if err != nil { if err != nil {
gateway.SendMessage( gateway.SendTextMessage(
returnJid, returnJid,
strconv.FormatInt(chatID, 10), strconv.FormatInt(chatID, 10),
fmt.Sprintf("Failed to write a temporary file: %s", err.Error()), fmt.Sprintf("Failed to write a temporary file: %s", err.Error()),
"",
c.xmpp, c.xmpp,
) )
return nil return nil
@ -946,11 +964,10 @@ func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid str
InputMessageContent: message, InputMessageContent: message,
}) })
if err != nil { if err != nil {
gateway.SendMessage( gateway.SendTextMessage(
returnJid, returnJid,
strconv.FormatInt(chatID, 10), strconv.FormatInt(chatID, 10),
fmt.Sprintf("Not sent: %s", err.Error()), fmt.Sprintf("Not sent: %s", err.Error()),
"",
c.xmpp, c.xmpp,
) )
} }

View file

@ -389,7 +389,7 @@ func TestMessageToPrefix1(t *testing.T) {
}, },
}, },
} }
prefix := (&Client{Session: &persistence.Session{}}).messageToPrefix(&message, "", "") prefix := (&Client{Session: &persistence.Session{}}).messageToPrefix(&message, "", "", nil)
if prefix != "➡ 42 | fwd: ziz" { if prefix != "➡ 42 | fwd: ziz" {
t.Errorf("Wrong prefix: %v", prefix) t.Errorf("Wrong prefix: %v", prefix)
} }
@ -404,7 +404,7 @@ func TestMessageToPrefix2(t *testing.T) {
}, },
}, },
} }
prefix := (&Client{Session: &persistence.Session{}}).messageToPrefix(&message, "y.jpg", "") prefix := (&Client{Session: &persistence.Session{}}).messageToPrefix(&message, "y.jpg", "", nil)
if prefix != "⬅ 56 | fwd: (zaz) | preview: y.jpg" { if prefix != "⬅ 56 | fwd: (zaz) | preview: y.jpg" {
t.Errorf("Wrong prefix: %v", prefix) t.Errorf("Wrong prefix: %v", prefix)
} }
@ -419,7 +419,7 @@ func TestMessageToPrefix3(t *testing.T) {
}, },
}, },
} }
prefix := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "a.jpg") prefix := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "a.jpg", nil)
if prefix != "< 56 | fwd: (zuz) | file: a.jpg" { if prefix != "< 56 | fwd: (zuz) | file: a.jpg" {
t.Errorf("Wrong prefix: %v", prefix) t.Errorf("Wrong prefix: %v", prefix)
} }
@ -430,7 +430,7 @@ func TestMessageToPrefix4(t *testing.T) {
Id: 23, Id: 23,
IsOutgoing: true, IsOutgoing: true,
} }
prefix := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "") prefix := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "", nil)
if prefix != "> 23" { if prefix != "> 23" {
t.Errorf("Wrong prefix: %v", prefix) t.Errorf("Wrong prefix: %v", prefix)
} }
@ -445,8 +445,60 @@ func TestMessageToPrefix5(t *testing.T) {
}, },
}, },
} }
prefix := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "h.jpg", "a.jpg") prefix := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "h.jpg", "a.jpg", nil)
if prefix != "< 560 | fwd: (zyz) | preview: h.jpg | file: a.jpg" { if prefix != "< 560 | fwd: (zyz) | preview: h.jpg | file: a.jpg" {
t.Errorf("Wrong prefix: %v", prefix) t.Errorf("Wrong prefix: %v", prefix)
} }
} }
func TestMessageToPrefix6(t *testing.T) {
message := client.Message{
Id: 23,
IsOutgoing: true,
ReplyToMessageId: 42,
}
reply := client.Message{
Id: 42,
Content: &client.MessageText{
Text: &client.FormattedText{
Text: "tist",
},
},
}
prefix := (&Client{Session: &persistence.Session{AsciiArrows: true}}).messageToPrefix(&message, "", "", &reply)
if prefix != "> 23 | reply: 42 | | tist" {
t.Errorf("Wrong prefix: %v", prefix)
}
}
func GetSenderIdEmpty(t *testing.T) {
message := client.Message{}
senderId := (&Client{}).getSenderId(&message)
if senderId != 0 {
t.Errorf("Wrong sender id: %v", senderId)
}
}
func GetSenderIdUser(t *testing.T) {
message := client.Message{
SenderId: &client.MessageSenderUser{
UserId: 42,
},
}
senderId := (&Client{}).getSenderId(&message)
if senderId != 42 {
t.Errorf("Wrong sender id: %v", senderId)
}
}
func GetSenderIdChat(t *testing.T) {
message := client.Message{
SenderId: &client.MessageSenderChat{
ChatId: -42,
},
}
senderId := (&Client{}).getSenderId(&message)
if senderId != -42 {
t.Errorf("Wrong sender id: %v", senderId)
}
}

View file

@ -111,6 +111,13 @@ type IqVcardDesc struct {
Text string `xml:",chardata"` Text string `xml:",chardata"`
} }
// Reply is from XEP-0461
type Reply struct {
XMLName xml.Name `xml:"urn:xmpp:reply:0 reply"`
To string `xml:"to,attr"`
Id string `xml:"id,attr"`
}
// Namespace is a namespace! // Namespace is a namespace!
func (c PresenceNickExtension) Namespace() string { func (c PresenceNickExtension) Namespace() string {
return c.XMLName.Space return c.XMLName.Space
@ -131,6 +138,11 @@ func (c IqVcardTemp) GetSet() *stanza.ResultSet {
return c.ResultSet return c.ResultSet
} }
// Namespace is a namespace!
func (c Reply) Namespace() string {
return c.XMLName.Space
}
func init() { func init() {
// presence nick // presence nick
stanza.TypeRegistry.MapExtension(stanza.PKTPresence, xml.Name{ stanza.TypeRegistry.MapExtension(stanza.PKTPresence, xml.Name{
@ -149,4 +161,10 @@ func init() {
"vcard-temp", "vcard-temp",
"vCard", "vCard",
}, IqVcardTemp{}) }, IqVcardTemp{})
// reply
stanza.TypeRegistry.MapExtension(stanza.PKTMessage, xml.Name{
"urn:xmpp:reply:0",
"reply",
}, Reply{})
} }

View file

@ -13,6 +13,11 @@ import (
"gosrc.io/xmpp/stanza" "gosrc.io/xmpp/stanza"
) )
type Reply struct {
Author string
Id string
}
const NSNick string = "http://jabber.org/protocol/nick" const NSNick string = "http://jabber.org/protocol/nick"
// Queue stores presences to send later // Queue stores presences to send later
@ -27,16 +32,26 @@ var Jid *stanza.Jid
var DirtySessions = false var DirtySessions = false
// SendMessage creates and sends a message stanza // SendMessage creates and sends a message stanza
func SendMessage(to string, from string, body string, id string, component *xmpp.Component) { func SendMessage(to string, from string, body string, id string, component *xmpp.Component, reply *Reply) {
sendMessageWrapper(to, from, body, id, component, "") sendMessageWrapper(to, from, body, id, component, reply, "")
}
// SendServiceMessage creates and sends a simple message stanza from transport
func SendServiceMessage(to string, body string, component *xmpp.Component) {
sendMessageWrapper(to, "", body, "", component, nil, "")
}
// SendTextMessage creates and sends a simple message stanza
func SendTextMessage(to string, from string, body string, component *xmpp.Component) {
sendMessageWrapper(to, from, body, "", component, nil, "")
} }
// SendMessageWithOOB creates and sends a message stanza with OOB URL // SendMessageWithOOB creates and sends a message stanza with OOB URL
func SendMessageWithOOB(to string, from string, body string, id string, component *xmpp.Component, oob string) { func SendMessageWithOOB(to string, from string, body string, id string, component *xmpp.Component, reply *Reply, oob string) {
sendMessageWrapper(to, from, body, id, component, oob) sendMessageWrapper(to, from, body, id, component, reply, oob)
} }
func sendMessageWrapper(to string, from string, body string, id string, component *xmpp.Component, oob string) { func sendMessageWrapper(to string, from string, body string, id string, component *xmpp.Component, reply *Reply, oob string) {
componentJid := Jid.Full() componentJid := Jid.Full()
var logFrom string var logFrom string
@ -69,6 +84,12 @@ func sendMessageWrapper(to string, from string, body string, id string, componen
URL: oob, URL: oob,
}) })
} }
if reply != nil {
message.Extensions = append(message.Extensions, extensions.Reply{
To: reply.Author,
Id: reply.Id,
})
}
sendMessage(&message, component) sendMessage(&message, component)
} }

View file

@ -106,7 +106,7 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) {
if err == nil && toJid.Bare() == gatewayJid && (strings.HasPrefix(msg.Body, "/") || strings.HasPrefix(msg.Body, "!")) { if err == nil && toJid.Bare() == gatewayJid && (strings.HasPrefix(msg.Body, "/") || strings.HasPrefix(msg.Body, "!")) {
response := session.ProcessTransportCommand(msg.Body, resource) response := session.ProcessTransportCommand(msg.Body, resource)
if response != "" { if response != "" {
gateway.SendMessage(msg.From, "", response, "", component) gateway.SendServiceMessage(msg.From, response, component)
} }
return return
} }