mirror of
https://dev.narayana.im/narayana/telegabber.git
synced 2026-08-05 04:07:07 +00:00
Bot Menu via Ad-Hoc
This commit is contained in:
parent
3cac57e0f3
commit
4414c147d8
5 changed files with 162 additions and 16 deletions
|
|
@ -58,6 +58,8 @@ type Client struct {
|
||||||
lastMsgIds map[int64]string
|
lastMsgIds map[int64]string
|
||||||
msgHashSeed maphash.Seed
|
msgHashSeed maphash.Seed
|
||||||
|
|
||||||
|
LastBotCmdString string
|
||||||
|
|
||||||
XmppClientFeatures map[string]*[]string
|
XmppClientFeatures map[string]*[]string
|
||||||
XmppClientFeaturesLock sync.Mutex
|
XmppClientFeaturesLock sync.Mutex
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -247,10 +247,23 @@ func (c *Client) updateNewMessage(update *client.UpdateNewMessage) {
|
||||||
|
|
||||||
c.updateLastMessageHash(update.Message.ChatId, update.Message.Id, update.Message.Content)
|
c.updateLastMessageHash(update.Message.ChatId, update.Message.Id, update.Message.Content)
|
||||||
|
|
||||||
|
var forceCmd bool
|
||||||
|
if c.LastBotCmdString != "" && update.Message.IsOutgoing {
|
||||||
|
if update.Message.Content.MessageContentType() == client.TypeMessageText {
|
||||||
|
textMessage, _ := update.Message.Content.(*client.MessageText)
|
||||||
|
|
||||||
|
if textMessage.Text != nil && textMessage.Text.Text == c.LastBotCmdString {
|
||||||
|
forceCmd = true
|
||||||
|
c.LastBotCmdString = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ignore self outgoing messages
|
// ignore self outgoing messages
|
||||||
if update.Message.IsOutgoing &&
|
if update.Message.IsOutgoing &&
|
||||||
update.Message.SendingState != nil &&
|
update.Message.SendingState != nil &&
|
||||||
update.Message.SendingState.MessageSendingStateType() == client.TypeMessageSendingStatePending {
|
update.Message.SendingState.MessageSendingStateType() == client.TypeMessageSendingStatePending &&
|
||||||
|
!forceCmd {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,16 @@ type messageStub struct {
|
||||||
Text string
|
Text string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BotCommand struct {
|
||||||
|
Command string
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
type BotLink struct {
|
||||||
|
Description string
|
||||||
|
Link string
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
typeFileDataSha1 byte = iota
|
typeFileDataSha1 byte = iota
|
||||||
typeFileDataBase64
|
typeFileDataBase64
|
||||||
|
|
@ -211,6 +221,40 @@ func (c *Client) IsPM(id int64) (bool, error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsBot checks if a chat is a bot
|
||||||
|
func (c *Client) IsBot(id int64) (bool, error) {
|
||||||
|
_, user, err := c.GetContactByID(id, nil)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if user == nil || user.Type == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return user.Type.UserTypeType() == client.TypeUserTypeBot, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBotMenu retrieves the bot's attachment menu
|
||||||
|
func (c *Client) GetBotMenu(id int64) (*BotLink, []*BotCommand, error) {
|
||||||
|
fullInfo, err := c.client.GetUserFullInfo(&client.GetUserFullInfoRequest{
|
||||||
|
UserId: id,
|
||||||
|
})
|
||||||
|
if err == nil && fullInfo.BotInfo != nil {
|
||||||
|
if fullInfo.BotInfo.MenuButton != nil {
|
||||||
|
menuButton := fullInfo.BotInfo.MenuButton
|
||||||
|
return &BotLink{menuButton.Text, menuButton.Url}, nil, nil
|
||||||
|
} else {
|
||||||
|
var commands []*BotCommand
|
||||||
|
for _, command := range fullInfo.BotInfo.Commands {
|
||||||
|
commands = append(commands, &BotCommand{command.Command, command.Description})
|
||||||
|
}
|
||||||
|
return nil, commands, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) userStatusToText(status client.UserStatus, chatID int64) (string, string, string) {
|
func (c *Client) userStatusToText(status client.UserStatus, chatID int64) (string, string, string) {
|
||||||
var show, textStatus, presenceType string
|
var show, textStatus, presenceType string
|
||||||
|
|
||||||
|
|
@ -1275,13 +1319,13 @@ func (c *Client) PrepareOutgoingMessageContent(text string) client.InputMessageC
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessOutgoingMessage executes commands or sends messages to mapped chats, returns message id
|
// ProcessOutgoingMessage executes commands or sends messages to mapped chats, returns message id
|
||||||
func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid string, replyId int64, replaceId int64) int64 {
|
func (c *Client) ProcessOutgoingMessage(chatID int64, text string, returnJid string, replyId int64, replaceId int64, raw bool) int64 {
|
||||||
if !c.Online() {
|
if !c.Online() {
|
||||||
// we're offline
|
// we're offline
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if replaceId == 0 && (strings.HasPrefix(text, "/") || strings.HasPrefix(text, "!")) {
|
if replaceId == 0 && !raw && (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 != "" {
|
||||||
|
|
|
||||||
104
xmpp/handlers.go
104
xmpp/handlers.go
|
|
@ -204,7 +204,7 @@ func HandleMessage(s xmpp.Sender, p stanza.Packet) {
|
||||||
|
|
||||||
session.SendMessageLock.Lock()
|
session.SendMessageLock.Lock()
|
||||||
defer session.SendMessageLock.Unlock()
|
defer session.SendMessageLock.Unlock()
|
||||||
tgMessageId := session.ProcessOutgoingMessage(toID, text, msg.From, replyId, replaceId)
|
tgMessageId := session.ProcessOutgoingMessage(toID, text, msg.From, replyId, replaceId, false)
|
||||||
if tgMessageId != 0 {
|
if tgMessageId != 0 {
|
||||||
if replaceId != 0 {
|
if replaceId != 0 {
|
||||||
// not needed (is it persistent among clients though?)
|
// not needed (is it persistent among clients though?)
|
||||||
|
|
@ -679,7 +679,7 @@ func handleGetDiscoItems(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoItems) {
|
||||||
|
|
||||||
log.Debugf("discoItems: %#v", di)
|
log.Debugf("discoItems: %#v", di)
|
||||||
|
|
||||||
_, toOk := toToID(iq.To)
|
toID, toOk := toToID(iq.To)
|
||||||
if di.Node == gateway.NSCommand {
|
if di.Node == gateway.NSCommand {
|
||||||
answer.Payload = di
|
answer.Payload = di
|
||||||
|
|
||||||
|
|
@ -698,6 +698,13 @@ func handleGetDiscoItems(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoItems) {
|
||||||
session, ok := sessions[bare]
|
session, ok := sessions[bare]
|
||||||
if ok {
|
if ok {
|
||||||
isOnline = session.Online()
|
isOnline = session.Online()
|
||||||
|
|
||||||
|
if toOk {
|
||||||
|
isBot, err := session.IsBot(toID)
|
||||||
|
if err == nil && isBot {
|
||||||
|
di.AddItem(iq.To, "botmenu", "Bot Menu")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -966,7 +973,7 @@ func handleSetQueryCommand(s xmpp.Sender, iq *stanza.IQ, command *stanza.Command
|
||||||
CommandElements: elements,
|
CommandElements: elements,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if command.Node == "loginwizard" {
|
} else if !toOk && command.Node == "loginwizard" {
|
||||||
var session *telegram.Client
|
var session *telegram.Client
|
||||||
answer.Payload, cancelSend, session = loginWizardPayload(bare, form, resource)
|
answer.Payload, cancelSend, session = loginWizardPayload(bare, form, resource)
|
||||||
|
|
||||||
|
|
@ -974,6 +981,31 @@ func handleSetQueryCommand(s xmpp.Sender, iq *stanza.IQ, command *stanza.Command
|
||||||
if cancelSend {
|
if cancelSend {
|
||||||
go sendLoginWizardResponse(component, answer, session)
|
go sendLoginWizardResponse(component, answer, session)
|
||||||
}
|
}
|
||||||
|
} else if toOk && command.Node == "botmenu" {
|
||||||
|
payload := &stanza.Command{
|
||||||
|
SessionId: command.Node,
|
||||||
|
Node: command.Node,
|
||||||
|
}
|
||||||
|
answer.Payload = payload
|
||||||
|
|
||||||
|
if len(form.Fields) == 1 && form.Fields[0] != nil &&
|
||||||
|
form.Fields[0].Var == "command" && len(form.Fields[0].ValuesList) == 1 {
|
||||||
|
session, ok := sessions[bare]
|
||||||
|
if ok {
|
||||||
|
msgText := "/"+form.Fields[0].ValuesList[0]
|
||||||
|
session.LastBotCmdString = msgText
|
||||||
|
tgMessageId := session.ProcessOutgoingMessage(toId, msgText, iq.From, 0, 0, true)
|
||||||
|
if tgMessageId != 0 {
|
||||||
|
payload.Status = stanza.CommandStatusCompleted
|
||||||
|
} else {
|
||||||
|
setCommandPayloadError(payload, "Failed to send a bot command")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setCommandPayloadError(payload, "Session is lost")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setCommandPayloadError(payload, "Broken form")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// just for the case the client messed the order somehow
|
// just for the case the client messed the order somehow
|
||||||
sort.Slice(form.Fields, func(i int, j int) bool {
|
sort.Slice(form.Fields, func(i int, j int) bool {
|
||||||
|
|
@ -1102,7 +1134,7 @@ func handleSetQueryCommand(s xmpp.Sender, iq *stanza.IQ, command *stanza.Command
|
||||||
CommandElements: []stanza.CommandElement{&form},
|
CommandElements: []stanza.CommandElement{&form},
|
||||||
}
|
}
|
||||||
log.Debugf("form: %#v", form)
|
log.Debugf("form: %#v", form)
|
||||||
} else if command.Node == "loginwizard" {
|
} else if !toOk && command.Node == "loginwizard" {
|
||||||
var session *telegram.Client
|
var session *telegram.Client
|
||||||
answer.Payload, cancelSend, session = loginWizardPayload(bare, nil, resource)
|
answer.Payload, cancelSend, session = loginWizardPayload(bare, nil, resource)
|
||||||
|
|
||||||
|
|
@ -1110,6 +1142,61 @@ func handleSetQueryCommand(s xmpp.Sender, iq *stanza.IQ, command *stanza.Command
|
||||||
if cancelSend {
|
if cancelSend {
|
||||||
go sendLoginWizardResponse(component, answer, session)
|
go sendLoginWizardResponse(component, answer, session)
|
||||||
}
|
}
|
||||||
|
} else if toOk && command.Node == "botmenu" {
|
||||||
|
session, ok := sessions[bare]
|
||||||
|
|
||||||
|
var link *telegram.BotLink
|
||||||
|
var commands []*telegram.BotCommand
|
||||||
|
var err error
|
||||||
|
if ok {
|
||||||
|
link, commands, err = session.GetBotMenu(toId)
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := &stanza.Command{
|
||||||
|
SessionId: command.Node,
|
||||||
|
Node: command.Node,
|
||||||
|
}
|
||||||
|
answer.Payload = payload
|
||||||
|
|
||||||
|
if !ok || err != nil {
|
||||||
|
setCommandPayloadError(payload, "Cannot retrieve commands")
|
||||||
|
} else {
|
||||||
|
if link != nil {
|
||||||
|
payload.Status = stanza.CommandStatusCompleted
|
||||||
|
payload.CommandElements = []stanza.CommandElement{
|
||||||
|
&stanza.Note{
|
||||||
|
Text: fmt.Sprintf("%v: %v", link.Description, link.Link),
|
||||||
|
Type: stanza.CommandNoteTypeInfo,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var options []stanza.Option
|
||||||
|
for _, cmd := range commands {
|
||||||
|
options = append(options, stanza.Option{
|
||||||
|
Label: fmt.Sprintf("/%v — %v", cmd.Command, cmd.Description),
|
||||||
|
ValuesList: []string{cmd.Command},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
dummyString := ""
|
||||||
|
field := stanza.Field{
|
||||||
|
Var: "command",
|
||||||
|
Type: stanza.FieldTypeListSingle,
|
||||||
|
Required: &dummyString,
|
||||||
|
Options: options,
|
||||||
|
}
|
||||||
|
log.Debugf("field: %#v", field)
|
||||||
|
|
||||||
|
form := stanza.Form{
|
||||||
|
Type: stanza.FormTypeForm,
|
||||||
|
Fields: []*stanza.Field{&field},
|
||||||
|
}
|
||||||
|
log.Debugf("form: %#v", form)
|
||||||
|
|
||||||
|
payload.Status = stanza.CommandStatusExecuting
|
||||||
|
payload.CommandElements = []stanza.CommandElement{&form}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
cmdString = "/" + command.Node
|
cmdString = "/" + command.Node
|
||||||
}
|
}
|
||||||
|
|
@ -1206,6 +1293,15 @@ func iqAnswerSetError(answer *stanza.IQ, payload *extensions.QueryRegister, code
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setCommandPayloadError(payload *stanza.Command, err string) {
|
||||||
|
note := stanza.Note{
|
||||||
|
Text: err,
|
||||||
|
Type: stanza.CommandNoteTypeErr,
|
||||||
|
}
|
||||||
|
payload.Status = stanza.CommandStatusCompleted
|
||||||
|
payload.CommandElements = append(payload.CommandElements, ¬e)
|
||||||
|
}
|
||||||
|
|
||||||
func probeClientFeatures(jid string, component *xmpp.Component) {
|
func probeClientFeatures(jid string, component *xmpp.Component) {
|
||||||
id, err := uuid.NewRandom()
|
id, err := uuid.NewRandom()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,6 @@ import (
|
||||||
"gosrc.io/xmpp/stanza"
|
"gosrc.io/xmpp/stanza"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setCommandPayloadError(payload *stanza.Command, err string) {
|
|
||||||
note := stanza.Note{
|
|
||||||
Text: err,
|
|
||||||
Type: stanza.CommandNoteTypeErr,
|
|
||||||
}
|
|
||||||
payload.Status = stanza.CommandStatusCompleted
|
|
||||||
payload.CommandElements = append(payload.CommandElements, ¬e)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loginWizardPayload(bare string, requestForm *stanza.Form, resource string) (payload *stanza.Command, cancelSend bool, returnSession *telegram.Client) {
|
func loginWizardPayload(bare string, requestForm *stanza.Form, resource string) (payload *stanza.Command, cancelSend bool, returnSession *telegram.Client) {
|
||||||
payload = &stanza.Command{
|
payload = &stanza.Command{
|
||||||
SessionId: "loginwizard",
|
SessionId: "loginwizard",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue