Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
Bohdan Horbeshko | ba8f4c08cf |
2
Makefile
2
Makefile
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
COMMIT := $(shell git rev-parse --short HEAD)
|
COMMIT := $(shell git rev-parse --short HEAD)
|
||||||
TD_COMMIT := "5bbfc1cf5dab94f82e02f3430ded7241d4653551"
|
TD_COMMIT := "5bbfc1cf5dab94f82e02f3430ded7241d4653551"
|
||||||
VERSION := "v1.10.0-dev"
|
VERSION := "v1.9.6"
|
||||||
MAKEOPTS := "-j4"
|
MAKEOPTS := "-j4"
|
||||||
|
|
||||||
all:
|
all:
|
||||||
|
|
5
go.mod
5
go.mod
|
@ -4,7 +4,6 @@ go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/dgraph-io/badger/v4 v4.1.0
|
github.com/dgraph-io/badger/v4 v4.1.0
|
||||||
github.com/google/uuid v1.1.1
|
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/santhosh-tekuri/jsonschema v1.2.4
|
github.com/santhosh-tekuri/jsonschema v1.2.4
|
||||||
github.com/sirupsen/logrus v1.4.2
|
github.com/sirupsen/logrus v1.4.2
|
||||||
|
@ -24,6 +23,7 @@ require (
|
||||||
github.com/golang/protobuf v1.3.2 // indirect
|
github.com/golang/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/snappy v0.0.3 // indirect
|
github.com/golang/snappy v0.0.3 // indirect
|
||||||
github.com/google/flatbuffers v1.12.1 // indirect
|
github.com/google/flatbuffers v1.12.1 // indirect
|
||||||
|
github.com/google/uuid v1.1.1 // indirect
|
||||||
github.com/klauspost/compress v1.12.3 // indirect
|
github.com/klauspost/compress v1.12.3 // indirect
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||||
go.opencensus.io v0.22.5 // indirect
|
go.opencensus.io v0.22.5 // indirect
|
||||||
|
@ -33,6 +33,5 @@ require (
|
||||||
nhooyr.io/websocket v1.6.5 // indirect
|
nhooyr.io/websocket v1.6.5 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace gosrc.io/xmpp => dev.narayana.im/narayana/go-xmpp v0.0.0-20240512132113-6725c3862314
|
replace gosrc.io/xmpp => dev.narayana.im/narayana/go-xmpp v0.0.0-20220524203317-306b4ff58e8f
|
||||||
|
|
||||||
replace github.com/zelenin/go-tdlib => dev.narayana.im/narayana/go-tdlib v0.0.0-20240124222245-b4c12addb061
|
replace github.com/zelenin/go-tdlib => dev.narayana.im/narayana/go-tdlib v0.0.0-20240124222245-b4c12addb061
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -7,10 +7,6 @@ dev.narayana.im/narayana/go-tdlib v0.0.0-20240124222245-b4c12addb061 h1:CWAQT74L
|
||||||
dev.narayana.im/narayana/go-tdlib v0.0.0-20240124222245-b4c12addb061/go.mod h1:Xs8fXbk5n7VaPyrSs9DP7QYoBScWYsjX+lUcWmx1DIU=
|
dev.narayana.im/narayana/go-tdlib v0.0.0-20240124222245-b4c12addb061/go.mod h1:Xs8fXbk5n7VaPyrSs9DP7QYoBScWYsjX+lUcWmx1DIU=
|
||||||
dev.narayana.im/narayana/go-xmpp v0.0.0-20220524203317-306b4ff58e8f h1:6249ajbMjgYz53Oq0IjTvjHXbxTfu29Mj1J/6swRHs4=
|
dev.narayana.im/narayana/go-xmpp v0.0.0-20220524203317-306b4ff58e8f h1:6249ajbMjgYz53Oq0IjTvjHXbxTfu29Mj1J/6swRHs4=
|
||||||
dev.narayana.im/narayana/go-xmpp v0.0.0-20220524203317-306b4ff58e8f/go.mod h1:L3NFMqYOxyLz3JGmgFyWf7r9htE91zVGiK40oW4RwdY=
|
dev.narayana.im/narayana/go-xmpp v0.0.0-20220524203317-306b4ff58e8f/go.mod h1:L3NFMqYOxyLz3JGmgFyWf7r9htE91zVGiK40oW4RwdY=
|
||||||
dev.narayana.im/narayana/go-xmpp v0.0.0-20240131013505-18c46e6c59fd h1:+UW+E7JjI88aH4beDn1cw6D8rs1I061hN91HU4Y4pT8=
|
|
||||||
dev.narayana.im/narayana/go-xmpp v0.0.0-20240131013505-18c46e6c59fd/go.mod h1:L3NFMqYOxyLz3JGmgFyWf7r9htE91zVGiK40oW4RwdY=
|
|
||||||
dev.narayana.im/narayana/go-xmpp v0.0.0-20240512132113-6725c3862314 h1:29/NjOGOUDceO73Hk4Nj4uVa1je8MULJlsDSvKxSN/k=
|
|
||||||
dev.narayana.im/narayana/go-xmpp v0.0.0-20240512132113-6725c3862314/go.mod h1:L3NFMqYOxyLz3JGmgFyWf7r9htE91zVGiK40oW4RwdY=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/agnivade/wasmbrowsertest v0.3.1/go.mod h1:zQt6ZTdl338xxRaMW395qccVE2eQm0SjC/SDz0mPWQI=
|
github.com/agnivade/wasmbrowsertest v0.3.1/go.mod h1:zQt6ZTdl338xxRaMW395qccVE2eQm0SjC/SDz0mPWQI=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
|
|
@ -26,8 +26,8 @@ WORKDIR /src
|
||||||
RUN go env -w GOCACHE=/go-cache
|
RUN go env -w GOCACHE=/go-cache
|
||||||
RUN go env -w GOMODCACHE=/gomod-cache
|
RUN go env -w GOMODCACHE=/gomod-cache
|
||||||
RUN --mount=type=cache,target=/gomod-cache \
|
RUN --mount=type=cache,target=/gomod-cache \
|
||||||
--mount=type=bind,source=./,target=/src,rw \
|
--mount=type=bind,source=./,target=/src \
|
||||||
/bin/bash -c 'go mod tidy; go get -t'
|
go mod download
|
||||||
|
|
||||||
FROM cache AS build
|
FROM cache AS build
|
||||||
ARG MAKEOPTS
|
ARG MAKEOPTS
|
||||||
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
goxmpp "gosrc.io/xmpp"
|
goxmpp "gosrc.io/xmpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version string = "1.10.0-dev"
|
var version string = "1.9.6"
|
||||||
var commit string
|
var commit string
|
||||||
|
|
||||||
var sm *goxmpp.StreamManager
|
var sm *goxmpp.StreamManager
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -46,7 +46,6 @@ type messageStub struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var errOffline = errors.New("TDlib instance is offline")
|
var errOffline = errors.New("TDlib instance is offline")
|
||||||
var errOverLimit = errors.New("Over limit")
|
|
||||||
|
|
||||||
var spaceRegex = regexp.MustCompile(`\s+`)
|
var spaceRegex = regexp.MustCompile(`\s+`)
|
||||||
var replyRegex = regexp.MustCompile("\\A>>? ?([0-9]+)\\n")
|
var replyRegex = regexp.MustCompile("\\A>>? ?([0-9]+)\\n")
|
||||||
|
@ -54,28 +53,6 @@ var replyRegex = regexp.MustCompile("\\A>>? ?([0-9]+)\\n")
|
||||||
const newlineChar string = "\n"
|
const newlineChar string = "\n"
|
||||||
const messageHeaderSeparator string = " | " // no hrunicode allowed here yet
|
const messageHeaderSeparator string = " | " // no hrunicode allowed here yet
|
||||||
|
|
||||||
// ChatType is an enum of chat types, roughly corresponding to TDLib's one but better
|
|
||||||
type ChatType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
ChatTypeUnknown ChatType = iota
|
|
||||||
ChatTypePrivate
|
|
||||||
ChatTypeBasicGroup
|
|
||||||
ChatTypeSupergroup
|
|
||||||
ChatTypeSecret
|
|
||||||
ChatTypeChannel
|
|
||||||
)
|
|
||||||
|
|
||||||
// MembersList is an enum of member list filters
|
|
||||||
type MembersList int
|
|
||||||
|
|
||||||
const (
|
|
||||||
MembersListMembers MembersList = iota
|
|
||||||
MembersListRestricted
|
|
||||||
MembersListBanned
|
|
||||||
MembersListBannedAndAdministrators
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetContactByUsername resolves username to user id retrieves user and chat information
|
// GetContactByUsername resolves username to user id retrieves user and chat information
|
||||||
func (c *Client) GetContactByUsername(username string) (*client.Chat, *client.User, error) {
|
func (c *Client) GetContactByUsername(username string) (*client.Chat, *client.User, error) {
|
||||||
if !c.Online() {
|
if !c.Online() {
|
||||||
|
@ -153,10 +130,10 @@ func (c *Client) GetContactByID(id int64, chat *client.Chat) (*client.Chat, *cli
|
||||||
return chat, user, nil
|
return chat, user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChatType obtains chat type from its information
|
// IsPM checks if a chat is PM
|
||||||
func (c *Client) GetChatType(id int64) (ChatType, error) {
|
func (c *Client) IsPM(id int64) (bool, error) {
|
||||||
if !c.Online() || id == 0 {
|
if !c.Online() || id == 0 {
|
||||||
return ChatTypeUnknown, errOffline
|
return false, errOffline
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
@ -167,38 +144,14 @@ func (c *Client) GetChatType(id int64) (ChatType, error) {
|
||||||
ChatId: id,
|
ChatId: id,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ChatTypeUnknown, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.cache.SetChat(id, chat)
|
c.cache.SetChat(id, chat)
|
||||||
}
|
}
|
||||||
|
|
||||||
chatType := chat.Type.ChatTypeType()
|
chatType := chat.Type.ChatTypeType()
|
||||||
if chatType == client.TypeChatTypePrivate {
|
if chatType == client.TypeChatTypePrivate || chatType == client.TypeChatTypeSecret {
|
||||||
return ChatTypePrivate, nil
|
|
||||||
} else if chatType == client.TypeChatTypeBasicGroup {
|
|
||||||
return ChatTypeBasicGroup, nil
|
|
||||||
} else if chatType == client.TypeChatTypeSupergroup {
|
|
||||||
supergroup, _ := chat.Type.(*client.ChatTypeSupergroup)
|
|
||||||
if supergroup.IsChannel {
|
|
||||||
return ChatTypeChannel, nil
|
|
||||||
}
|
|
||||||
return ChatTypeSupergroup, nil
|
|
||||||
} else if chatType == client.TypeChatTypeSecret {
|
|
||||||
return ChatTypeSecret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return ChatTypeUnknown, errors.New("Unknown chat type")
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsPM checks if a chat is PM
|
|
||||||
func (c *Client) IsPM(id int64) (bool, error) {
|
|
||||||
typ, err := c.GetChatType(id)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if typ == ChatTypePrivate || typ == ChatTypeSecret {
|
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
|
@ -341,8 +294,7 @@ func (c *Client) ProcessStatusUpdate(chatID int64, status string, show string, o
|
||||||
return c.sendPresence(newArgs...)
|
return c.sendPresence(newArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormatContact retrieves a complete "full name (@usernames)" string for display
|
func (c *Client) formatContact(chatID int64) string {
|
||||||
func (c *Client) FormatContact(chatID int64) string {
|
|
||||||
if chatID == 0 {
|
if chatID == 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -374,27 +326,23 @@ func (c *Client) FormatContact(chatID int64) string {
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetSenderId(sender client.MessageSender) (senderId int64) {
|
func (c *Client) getSenderId(message *client.Message) (senderId int64) {
|
||||||
switch sender.MessageSenderType() {
|
|
||||||
case client.TypeMessageSenderUser:
|
|
||||||
senderUser, _ := sender.(*client.MessageSenderUser)
|
|
||||||
senderId = senderUser.UserId
|
|
||||||
case client.TypeMessageSenderChat:
|
|
||||||
senderChat, _ := sender.(*client.MessageSenderChat)
|
|
||||||
senderId = senderChat.ChatId
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) getMessageSenderId(message *client.Message) (senderId int64) {
|
|
||||||
if message.SenderId != nil {
|
if message.SenderId != nil {
|
||||||
senderId = c.GetSenderId(message.SenderId)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) formatSender(message *client.Message) string {
|
func (c *Client) formatSender(message *client.Message) string {
|
||||||
return c.FormatContact(c.getMessageSenderId(message))
|
return c.formatContact(c.getSenderId(message))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) messageToStub(message *client.Message, preview bool, text string) *messageStub {
|
func (c *Client) messageToStub(message *client.Message, preview bool, text string) *messageStub {
|
||||||
|
@ -444,7 +392,7 @@ func (c *Client) getMessageReply(message *client.Message, preview bool, noConten
|
||||||
}
|
}
|
||||||
|
|
||||||
gatewayReply = &gateway.Reply{
|
gatewayReply = &gateway.Reply{
|
||||||
Author: fmt.Sprintf("%v@%s", c.getMessageSenderId(replyMsg), gateway.Jid.Full()),
|
Author: fmt.Sprintf("%v@%s", c.getSenderId(replyMsg), gateway.Jid.Full()),
|
||||||
Id: replyId,
|
Id: replyId,
|
||||||
}
|
}
|
||||||
} else if !noContent {
|
} else if !noContent {
|
||||||
|
@ -461,7 +409,7 @@ func (c *Client) getMessageReply(message *client.Message, preview bool, noConten
|
||||||
}
|
}
|
||||||
|
|
||||||
tgReply = &messageStub{
|
tgReply = &messageStub{
|
||||||
Sender: c.formatOrigin(replyTo.Origin) + " @ " + c.FormatContact(replyTo.ChatId),
|
Sender: c.formatOrigin(replyTo.Origin) + " @ " + c.formatContact(replyTo.ChatId),
|
||||||
Date: replyTo.OriginSendDate,
|
Date: replyTo.OriginSendDate,
|
||||||
Text: text,
|
Text: text,
|
||||||
}
|
}
|
||||||
|
@ -531,14 +479,14 @@ func (c *Client) formatOrigin(origin client.MessageOrigin) string {
|
||||||
switch origin.MessageOriginType() {
|
switch origin.MessageOriginType() {
|
||||||
case client.TypeMessageOriginUser:
|
case client.TypeMessageOriginUser:
|
||||||
originUser := origin.(*client.MessageOriginUser)
|
originUser := origin.(*client.MessageOriginUser)
|
||||||
return c.FormatContact(originUser.SenderUserId)
|
return c.formatContact(originUser.SenderUserId)
|
||||||
case client.TypeMessageOriginChat:
|
case client.TypeMessageOriginChat:
|
||||||
originChat := origin.(*client.MessageOriginChat)
|
originChat := origin.(*client.MessageOriginChat)
|
||||||
var signature string
|
var signature string
|
||||||
if originChat.AuthorSignature != "" {
|
if originChat.AuthorSignature != "" {
|
||||||
signature = fmt.Sprintf(" (%s)", originChat.AuthorSignature)
|
signature = fmt.Sprintf(" (%s)", originChat.AuthorSignature)
|
||||||
}
|
}
|
||||||
return c.FormatContact(originChat.SenderChatId) + signature
|
return c.formatContact(originChat.SenderChatId) + signature
|
||||||
case client.TypeMessageOriginHiddenUser:
|
case client.TypeMessageOriginHiddenUser:
|
||||||
originUser := origin.(*client.MessageOriginHiddenUser)
|
originUser := origin.(*client.MessageOriginHiddenUser)
|
||||||
return originUser.SenderName
|
return originUser.SenderName
|
||||||
|
@ -548,7 +496,7 @@ func (c *Client) formatOrigin(origin client.MessageOrigin) string {
|
||||||
if channel.AuthorSignature != "" {
|
if channel.AuthorSignature != "" {
|
||||||
signature = fmt.Sprintf(" (%s)", channel.AuthorSignature)
|
signature = fmt.Sprintf(" (%s)", channel.AuthorSignature)
|
||||||
}
|
}
|
||||||
return c.FormatContact(channel.ChatId) + signature
|
return c.formatContact(channel.ChatId) + signature
|
||||||
}
|
}
|
||||||
return "Unknown origin type"
|
return "Unknown origin type"
|
||||||
}
|
}
|
||||||
|
@ -717,13 +665,13 @@ func (c *Client) messageContentToText(content client.MessageContent, chatId int6
|
||||||
|
|
||||||
text := "invited "
|
text := "invited "
|
||||||
if len(addMembers.MemberUserIds) > 0 {
|
if len(addMembers.MemberUserIds) > 0 {
|
||||||
text += c.FormatContact(addMembers.MemberUserIds[0])
|
text += c.formatContact(addMembers.MemberUserIds[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
return text
|
return text
|
||||||
case client.TypeMessageChatDeleteMember:
|
case client.TypeMessageChatDeleteMember:
|
||||||
deleteMember, _ := content.(*client.MessageChatDeleteMember)
|
deleteMember, _ := content.(*client.MessageChatDeleteMember)
|
||||||
return "kicked " + c.FormatContact(deleteMember.UserId)
|
return "kicked " + c.formatContact(deleteMember.UserId)
|
||||||
case client.TypeMessagePinMessage:
|
case client.TypeMessagePinMessage:
|
||||||
pinMessage, _ := content.(*client.MessagePinMessage)
|
pinMessage, _ := content.(*client.MessagePinMessage)
|
||||||
return "pinned message: " + c.formatMessage(chatId, pinMessage.MessageId, preview, nil)
|
return "pinned message: " + c.formatMessage(chatId, pinMessage.MessageId, preview, nil)
|
||||||
|
@ -873,7 +821,7 @@ func (c *Client) messageContentToText(content client.MessageContent, chatId int6
|
||||||
}
|
}
|
||||||
case client.TypeMessageChatSetMessageAutoDeleteTime:
|
case client.TypeMessageChatSetMessageAutoDeleteTime:
|
||||||
ttl, _ := content.(*client.MessageChatSetMessageAutoDeleteTime)
|
ttl, _ := content.(*client.MessageChatSetMessageAutoDeleteTime)
|
||||||
name := c.FormatContact(ttl.FromUserId)
|
name := c.formatContact(ttl.FromUserId)
|
||||||
if name == "" {
|
if name == "" {
|
||||||
if ttl.MessageAutoDeleteTime == 0 {
|
if ttl.MessageAutoDeleteTime == 0 {
|
||||||
return "The self-destruct timer was disabled"
|
return "The self-destruct timer was disabled"
|
||||||
|
@ -1110,13 +1058,19 @@ func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) {
|
||||||
fileName, link := c.formatFile(file, false)
|
fileName, link := c.formatFile(file, false)
|
||||||
|
|
||||||
oob = link
|
oob = link
|
||||||
if c.Session.OOBMode && oob != "" {
|
oobSwap := c.Session.OOBMode && oob != ""
|
||||||
typ := message.Content.MessageContentType()
|
|
||||||
if typ != client.TypeMessageSticker {
|
var ignorePrefix bool
|
||||||
auxText = text
|
if oobSwap {
|
||||||
|
if text == "" || message.Content.MessageContentType() == client.TypeMessageSticker {
|
||||||
|
isPM, err := c.IsPM(chatId)
|
||||||
|
if err == nil {
|
||||||
|
ignorePrefix = isPM && c.isCarbonsEnabled()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
text = oob
|
}
|
||||||
} else if !c.Session.RawMessages {
|
|
||||||
|
if !c.Session.RawMessages && !ignorePrefix {
|
||||||
var newText strings.Builder
|
var newText strings.Builder
|
||||||
|
|
||||||
prefix, prefixReply := c.messageToPrefix(message, previewName, fileName, false)
|
prefix, prefixReply := c.messageToPrefix(message, previewName, fileName, false)
|
||||||
|
@ -1132,6 +1086,13 @@ func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) {
|
||||||
}
|
}
|
||||||
text = newText.String()
|
text = newText.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if oobSwap {
|
||||||
|
if !ignorePrefix {
|
||||||
|
auxText = text
|
||||||
|
}
|
||||||
|
text = oob
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !replyObtained {
|
if !replyObtained {
|
||||||
|
@ -1179,7 +1140,7 @@ func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid str
|
||||||
|
|
||||||
if replaceId == 0 && (strings.HasPrefix(text, "/") || strings.HasPrefix(text, "!")) {
|
if replaceId == 0 && (strings.HasPrefix(text, "/") || strings.HasPrefix(text, "!")) {
|
||||||
// try to execute commands
|
// try to execute commands
|
||||||
response, isCommand, _ := c.ProcessChatCommand(chatID, text)
|
response, isCommand := c.ProcessChatCommand(chatID, text)
|
||||||
if response != "" {
|
if response != "" {
|
||||||
c.returnMessage(returnJid, chatID, response)
|
c.returnMessage(returnJid, chatID, response)
|
||||||
}
|
}
|
||||||
|
@ -1680,76 +1641,3 @@ func (c *Client) usernamesToString(usernames []string) string {
|
||||||
}
|
}
|
||||||
return strings.Join(atUsernames, ", ")
|
return strings.Join(atUsernames, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChatMembers retrieves a list of chat members. "Limited" mode works only if there are no more than 20 members at all
|
|
||||||
func (c *Client) GetChatMembers(chatID int64, limited bool, query string, membersList MembersList) ([]*client.ChatMember, error) {
|
|
||||||
var filters []client.ChatMembersFilter
|
|
||||||
switch membersList {
|
|
||||||
case MembersListMembers:
|
|
||||||
filters = []client.ChatMembersFilter{&client.ChatMembersFilterMembers{}}
|
|
||||||
case MembersListRestricted:
|
|
||||||
filters = []client.ChatMembersFilter{&client.ChatMembersFilterRestricted{}}
|
|
||||||
case MembersListBanned:
|
|
||||||
filters = []client.ChatMembersFilter{&client.ChatMembersFilterBanned{}}
|
|
||||||
case MembersListBannedAndAdministrators:
|
|
||||||
filters = []client.ChatMembersFilter{&client.ChatMembersFilterBanned{}, &client.ChatMembersFilterAdministrators{}}
|
|
||||||
}
|
|
||||||
|
|
||||||
limit := int32(9999)
|
|
||||||
if limited {
|
|
||||||
limit = 20
|
|
||||||
|
|
||||||
chat, _, err := c.GetContactByID(chatID, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if chat == nil {
|
|
||||||
return nil, errors.New("Chat not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
chatType := chat.Type.ChatTypeType()
|
|
||||||
if chatType == client.TypeChatTypeBasicGroup {
|
|
||||||
basicGroupType, _ := chat.Type.(*client.ChatTypeBasicGroup)
|
|
||||||
fullInfo, err := c.client.GetBasicGroupFullInfo(&client.GetBasicGroupFullInfoRequest{
|
|
||||||
BasicGroupId: basicGroupType.BasicGroupId,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(fullInfo.Members) > int(limit) {
|
|
||||||
return nil, errOverLimit
|
|
||||||
}
|
|
||||||
|
|
||||||
return fullInfo.Members, nil
|
|
||||||
} else if chatType == client.TypeChatTypeSupergroup {
|
|
||||||
supergroupType, _ := chat.Type.(*client.ChatTypeSupergroup)
|
|
||||||
fullInfo, err := c.client.GetSupergroupFullInfo(&client.GetSupergroupFullInfoRequest{
|
|
||||||
SupergroupId: supergroupType.SupergroupId,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if fullInfo.MemberCount > limit {
|
|
||||||
return nil, errOverLimit
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("Inapplicable chat type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var members []*client.ChatMember
|
|
||||||
for _, filter := range filters {
|
|
||||||
chatMembers, err := c.client.SearchChatMembers(&client.SearchChatMembersRequest{
|
|
||||||
ChatId: chatID,
|
|
||||||
Limit: limit,
|
|
||||||
Query: query,
|
|
||||||
Filter: filter,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
members = append(members, chatMembers.Members...)
|
|
||||||
}
|
|
||||||
return members, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -593,7 +593,7 @@ func TestMessageToPrefix8(t *testing.T) {
|
||||||
|
|
||||||
func GetSenderIdEmpty(t *testing.T) {
|
func GetSenderIdEmpty(t *testing.T) {
|
||||||
message := client.Message{}
|
message := client.Message{}
|
||||||
senderId := (&Client{}).getMessageSenderId(&message)
|
senderId := (&Client{}).getSenderId(&message)
|
||||||
if senderId != 0 {
|
if senderId != 0 {
|
||||||
t.Errorf("Wrong sender id: %v", senderId)
|
t.Errorf("Wrong sender id: %v", senderId)
|
||||||
}
|
}
|
||||||
|
@ -605,7 +605,7 @@ func GetSenderIdUser(t *testing.T) {
|
||||||
UserId: 42,
|
UserId: 42,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
senderId := (&Client{}).getMessageSenderId(&message)
|
senderId := (&Client{}).getSenderId(&message)
|
||||||
if senderId != 42 {
|
if senderId != 42 {
|
||||||
t.Errorf("Wrong sender id: %v", senderId)
|
t.Errorf("Wrong sender id: %v", senderId)
|
||||||
}
|
}
|
||||||
|
@ -617,7 +617,7 @@ func GetSenderIdChat(t *testing.T) {
|
||||||
ChatId: -42,
|
ChatId: -42,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
senderId := (&Client{}).getMessageSenderId(&message)
|
senderId := (&Client{}).getSenderId(&message)
|
||||||
if senderId != -42 {
|
if senderId != -42 {
|
||||||
t.Errorf("Wrong sender id: %v", senderId)
|
t.Errorf("Wrong sender id: %v", senderId)
|
||||||
}
|
}
|
||||||
|
|
288
xmpp/handlers.go
288
xmpp/handlers.go
|
@ -7,7 +7,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"io"
|
"io"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -27,7 +26,6 @@ const (
|
||||||
TypeVCard4
|
TypeVCard4
|
||||||
)
|
)
|
||||||
const NodeVCard4 string = "urn:xmpp:vcard4"
|
const NodeVCard4 string = "urn:xmpp:vcard4"
|
||||||
const NSCommand string = "http://jabber.org/protocol/commands"
|
|
||||||
|
|
||||||
func logPacketType(p stanza.Packet) {
|
func logPacketType(p stanza.Packet) {
|
||||||
log.Warnf("Ignoring packet: %T\n", p)
|
log.Warnf("Ignoring packet: %T\n", p)
|
||||||
|
@ -55,14 +53,14 @@ func HandleIq(s xmpp.Sender, p stanza.Packet) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
discoInfo, ok := iq.Payload.(*stanza.DiscoInfo)
|
_, ok = iq.Payload.(*stanza.DiscoInfo)
|
||||||
if ok {
|
if ok {
|
||||||
go handleGetDiscoInfo(s, iq, discoInfo)
|
go handleGetDiscoInfo(s, iq)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
discoItems, ok := iq.Payload.(*stanza.DiscoItems)
|
_, ok = iq.Payload.(*stanza.DiscoItems)
|
||||||
if ok {
|
if ok {
|
||||||
go handleGetDiscoItems(s, iq, discoItems)
|
go handleGetDiscoItems(s, iq)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, ok = iq.Payload.(*extensions.QueryRegister)
|
_, ok = iq.Payload.(*extensions.QueryRegister)
|
||||||
|
@ -76,11 +74,6 @@ func HandleIq(s xmpp.Sender, p stanza.Packet) {
|
||||||
go handleSetQueryRegister(s, iq, query)
|
go handleSetQueryRegister(s, iq, query)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
command, ok := iq.Payload.(*stanza.Command)
|
|
||||||
if ok {
|
|
||||||
go handleSetQueryCommand(s, iq, command)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +223,7 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) {
|
||||||
} else {
|
} else {
|
||||||
toJid, err := stanza.NewJid(msg.To)
|
toJid, err := stanza.NewJid(msg.To)
|
||||||
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.SendServiceMessage(msg.From, response, component)
|
gateway.SendServiceMessage(msg.From, response, component)
|
||||||
}
|
}
|
||||||
|
@ -475,22 +468,7 @@ func handleGetVcardIq(s xmpp.Sender, iq *stanza.IQ, typ byte) {
|
||||||
_ = gateway.ResumableSend(component, &answer)
|
_ = gateway.ResumableSend(component, &answer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTelegramChatType(from string, to string) (telegram.ChatType, error) {
|
func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ) {
|
||||||
toId, ok := toToID(to)
|
|
||||||
if ok {
|
|
||||||
bare, _, ok := gateway.SplitJID(from)
|
|
||||||
if ok {
|
|
||||||
session, ok := sessions[bare]
|
|
||||||
if ok {
|
|
||||||
return session.GetChatType(toId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return telegram.ChatTypeUnknown, errors.New("Unknown chat type")
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoInfo) {
|
|
||||||
answer, err := stanza.NewIQ(stanza.Attrs{
|
answer, err := stanza.NewIQ(stanza.Attrs{
|
||||||
Type: stanza.IQTypeResult,
|
Type: stanza.IQTypeResult,
|
||||||
From: iq.To,
|
From: iq.To,
|
||||||
|
@ -505,37 +483,13 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoInfo) {
|
||||||
|
|
||||||
disco := answer.DiscoInfo()
|
disco := answer.DiscoInfo()
|
||||||
_, ok := toToID(iq.To)
|
_, ok := toToID(iq.To)
|
||||||
if di.Node == "" {
|
if ok {
|
||||||
if ok {
|
disco.AddIdentity("", "account", "registered")
|
||||||
disco.AddIdentity("", "account", "registered")
|
disco.AddFeatures(stanza.NSMsgChatMarkers)
|
||||||
disco.AddFeatures(stanza.NSMsgChatMarkers)
|
disco.AddFeatures(stanza.NSMsgReceipts)
|
||||||
disco.AddFeatures(stanza.NSMsgReceipts)
|
|
||||||
} else {
|
|
||||||
disco.AddIdentity("Telegram Gateway", "gateway", "telegram")
|
|
||||||
disco.AddFeatures("jabber:iq:register")
|
|
||||||
}
|
|
||||||
disco.AddFeatures(NSCommand)
|
|
||||||
} else {
|
} else {
|
||||||
chatType, chatTypeErr := getTelegramChatType(iq.From, iq.To)
|
disco.AddIdentity("Telegram Gateway", "gateway", "telegram")
|
||||||
|
disco.AddFeatures("jabber:iq:register")
|
||||||
var cmdType telegram.CommandType
|
|
||||||
if ok {
|
|
||||||
cmdType = telegram.CommandTypeChat
|
|
||||||
} else {
|
|
||||||
cmdType = telegram.CommandTypeTransport
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, command := range telegram.GetCommands(cmdType) {
|
|
||||||
if di.Node == name {
|
|
||||||
if chatTypeErr == nil && !telegram.IsCommandForChatType(command, chatType) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
answer.Payload = di
|
|
||||||
di.AddIdentity(telegram.CommandToHelpString(name, command), "automation", "command-node")
|
|
||||||
di.AddFeatures(NSCommand, "jabber:x:data")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
answer.Payload = disco
|
answer.Payload = disco
|
||||||
|
|
||||||
|
@ -550,7 +504,7 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoInfo) {
|
||||||
_ = gateway.ResumableSend(component, answer)
|
_ = gateway.ResumableSend(component, answer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleGetDiscoItems(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoItems) {
|
func handleGetDiscoItems(s xmpp.Sender, iq *stanza.IQ) {
|
||||||
answer, err := stanza.NewIQ(stanza.Attrs{
|
answer, err := stanza.NewIQ(stanza.Attrs{
|
||||||
Type: stanza.IQTypeResult,
|
Type: stanza.IQTypeResult,
|
||||||
From: iq.To,
|
From: iq.To,
|
||||||
|
@ -563,32 +517,7 @@ func handleGetDiscoItems(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoItems) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("discoItems: %#v", di)
|
answer.Payload = answer.DiscoItems()
|
||||||
|
|
||||||
_, ok := toToID(iq.To)
|
|
||||||
if di.Node == NSCommand {
|
|
||||||
answer.Payload = di
|
|
||||||
|
|
||||||
chatType, chatTypeErr := getTelegramChatType(iq.From, iq.To)
|
|
||||||
|
|
||||||
var cmdType telegram.CommandType
|
|
||||||
if ok {
|
|
||||||
cmdType = telegram.CommandTypeChat
|
|
||||||
} else {
|
|
||||||
cmdType = telegram.CommandTypeTransport
|
|
||||||
}
|
|
||||||
|
|
||||||
commands := telegram.GetCommands(cmdType)
|
|
||||||
for _, name := range telegram.SortedCommandKeys(commands) {
|
|
||||||
command := commands[name]
|
|
||||||
if chatTypeErr == nil && !telegram.IsCommandForChatType(command, chatType) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
di.AddItem(iq.To, name, telegram.CommandToHelpString(name, command))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
answer.Payload = answer.DiscoItems()
|
|
||||||
}
|
|
||||||
|
|
||||||
component, ok := s.(*xmpp.Component)
|
component, ok := s.(*xmpp.Component)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -718,195 +647,6 @@ func handleSetQueryRegister(s xmpp.Sender, iq *stanza.IQ, query *extensions.Quer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSetQueryCommand(s xmpp.Sender, iq *stanza.IQ, command *stanza.Command) {
|
|
||||||
component, ok := s.(*xmpp.Component)
|
|
||||||
if !ok {
|
|
||||||
log.Error("Not a component")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
answer, err := stanza.NewIQ(stanza.Attrs{
|
|
||||||
Type: stanza.IQTypeResult,
|
|
||||||
From: iq.To,
|
|
||||||
To: iq.From,
|
|
||||||
Id: iq.Id,
|
|
||||||
Lang: "en",
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed to create answer IQ: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer gateway.ResumableSend(component, answer)
|
|
||||||
|
|
||||||
log.Debugf("command: %#v", command)
|
|
||||||
|
|
||||||
bare, resource, ok := gateway.SplitJID(iq.From)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
toId, toOk := toToID(iq.To)
|
|
||||||
|
|
||||||
var cmdString string
|
|
||||||
var cmdType telegram.CommandType
|
|
||||||
var form *stanza.Form
|
|
||||||
for _, ce := range command.CommandElements {
|
|
||||||
fo, formOk := ce.(*stanza.Form)
|
|
||||||
if formOk {
|
|
||||||
form = fo
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if toOk {
|
|
||||||
cmdType = telegram.CommandTypeChat
|
|
||||||
} else {
|
|
||||||
cmdType = telegram.CommandTypeTransport
|
|
||||||
}
|
|
||||||
if form != nil {
|
|
||||||
// just for the case the client messed the order somehow
|
|
||||||
sort.Slice(form.Fields, func(i int, j int) bool {
|
|
||||||
iField := form.Fields[i]
|
|
||||||
jField := form.Fields[j]
|
|
||||||
if iField != nil && jField != nil {
|
|
||||||
ii, iErr := strconv.ParseInt(iField.Var, 10, 64)
|
|
||||||
ji, jErr := strconv.ParseInt(jField.Var, 10, 64)
|
|
||||||
return iErr == nil && jErr == nil && ii < ji
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
var cmd strings.Builder
|
|
||||||
cmd.WriteString("/")
|
|
||||||
cmd.WriteString(command.Node)
|
|
||||||
for _, field := range form.Fields {
|
|
||||||
cmd.WriteString(" ")
|
|
||||||
if len(field.ValuesList) > 0 {
|
|
||||||
cmd.WriteString(field.ValuesList[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdString = cmd.String()
|
|
||||||
} else {
|
|
||||||
if command.Action == "" || command.Action == stanza.CommandActionExecute {
|
|
||||||
cmd, ok := telegram.GetCommand(cmdType, command.Node)
|
|
||||||
if ok && len(cmd.Arguments) > 0 {
|
|
||||||
var fields []*stanza.Field
|
|
||||||
for i, arg := range cmd.Arguments {
|
|
||||||
var required *string
|
|
||||||
if i < cmd.RequiredArgs {
|
|
||||||
dummyString := ""
|
|
||||||
required = &dummyString
|
|
||||||
}
|
|
||||||
|
|
||||||
var fieldType string
|
|
||||||
var options []stanza.Option
|
|
||||||
if toOk && i == 0 {
|
|
||||||
switch command.Node {
|
|
||||||
case "mute", "kick", "ban", "promote", "unmute", "unban":
|
|
||||||
session, ok := sessions[bare]
|
|
||||||
if ok {
|
|
||||||
var membersList telegram.MembersList
|
|
||||||
switch command.Node {
|
|
||||||
case "unmute":
|
|
||||||
membersList = telegram.MembersListRestricted
|
|
||||||
case "unban":
|
|
||||||
membersList = telegram.MembersListBannedAndAdministrators
|
|
||||||
}
|
|
||||||
members, err := session.GetChatMembers(toId, true, "", membersList)
|
|
||||||
if err == nil {
|
|
||||||
fieldType = stanza.FieldTypeListSingle
|
|
||||||
switch command.Node {
|
|
||||||
// allow empty form
|
|
||||||
case "mute", "unmute":
|
|
||||||
options = append(options, stanza.Option{
|
|
||||||
ValuesList: []string{""},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for _, member := range members {
|
|
||||||
senderId := session.GetSenderId(member.MemberId)
|
|
||||||
options = append(options, stanza.Option{
|
|
||||||
Label: session.FormatContact(senderId),
|
|
||||||
ValuesList: []string{strconv.FormatInt(senderId, 10)},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
field := stanza.Field{
|
|
||||||
Var: strconv.FormatInt(int64(i), 10),
|
|
||||||
Label: arg,
|
|
||||||
Required: required,
|
|
||||||
Type: fieldType,
|
|
||||||
Options: options,
|
|
||||||
}
|
|
||||||
fields = append(fields, &field)
|
|
||||||
log.Debugf("field: %#v", field)
|
|
||||||
}
|
|
||||||
form := stanza.Form{
|
|
||||||
Type: stanza.FormTypeForm,
|
|
||||||
Title: command.Node,
|
|
||||||
Instructions: []string{cmd.Description},
|
|
||||||
Fields: fields,
|
|
||||||
}
|
|
||||||
answer.Payload = &stanza.Command{
|
|
||||||
SessionId: command.Node,
|
|
||||||
Node: command.Node,
|
|
||||||
Status: stanza.CommandStatusExecuting,
|
|
||||||
CommandElements: []stanza.CommandElement{&form},
|
|
||||||
}
|
|
||||||
log.Debugf("form: %#v", form)
|
|
||||||
} else {
|
|
||||||
cmdString = "/" + command.Node
|
|
||||||
}
|
|
||||||
} else if command.Action == stanza.CommandActionCancel {
|
|
||||||
answer.Payload = &stanza.Command{
|
|
||||||
SessionId: command.Node,
|
|
||||||
Node: command.Node,
|
|
||||||
Status: stanza.CommandStatusCancelled,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cmdString != "" {
|
|
||||||
session, ok := sessions[bare]
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var response string
|
|
||||||
var success bool
|
|
||||||
if toOk {
|
|
||||||
response, _, success = session.ProcessChatCommand(toId, cmdString)
|
|
||||||
} else {
|
|
||||||
response, success = session.ProcessTransportCommand(cmdString, resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
var noteType string
|
|
||||||
if success {
|
|
||||||
noteType = stanza.CommandNoteTypeInfo
|
|
||||||
} else {
|
|
||||||
noteType = stanza.CommandNoteTypeErr
|
|
||||||
}
|
|
||||||
|
|
||||||
answer.Payload = &stanza.Command{
|
|
||||||
SessionId: command.Node,
|
|
||||||
Node: command.Node,
|
|
||||||
Status: stanza.CommandStatusCompleted,
|
|
||||||
CommandElements: []stanza.CommandElement{
|
|
||||||
&stanza.Note{
|
|
||||||
Text: response,
|
|
||||||
Type: noteType,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("command response: %#v", answer.Payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
func iqAnswerSetError(answer *stanza.IQ, payload *extensions.QueryRegister, code int) {
|
func iqAnswerSetError(answer *stanza.IQ, payload *extensions.QueryRegister, code int) {
|
||||||
answer.Type = stanza.IQTypeError
|
answer.Type = stanza.IQTypeError
|
||||||
answer.Payload = *payload
|
answer.Payload = *payload
|
||||||
|
|
Loading…
Reference in a new issue