From b0c5302c82b78c2f83ef12545437f31ab5406927 Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Sat, 10 Feb 2024 13:46:02 -0500 Subject: [PATCH] Ad-Hoc support for chat commands --- telegram/commands.go | 74 +++--------------- xmpp/handlers.go | 174 ++++++++++++++++++++++++------------------- 2 files changed, 108 insertions(+), 140 deletions(-) diff --git a/telegram/commands.go b/telegram/commands.go index 4dafdf5..d9b9f13 100644 --- a/telegram/commands.go +++ b/telegram/commands.go @@ -14,6 +14,7 @@ import ( "github.com/zelenin/go-tdlib/client" ) +const unknownCommand string = "Unknown command" const notEnoughArguments string = "Not enough arguments" const TelegramNotInitialized string = "Telegram connection is not initialized yet" const TelegramAuthDone string = "Authorization is done already" @@ -272,7 +273,7 @@ func (c *Client) ProcessTransportCommand(cmdline string, resource string) string cmd, args := parseCommand(cmdline) command, ok := transportCommands[cmd] if !ok { - return "Unknown command" + return unknownCommand } if len(args) < command.RequiredArgs { return notEnoughArguments @@ -498,6 +499,14 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) } cmd, args := parseCommand(cmdline) + command, ok := chatCommands[cmd] + if !ok { + return unknownCommand, false + } + if len(args) < command.RequiredArgs { + return notEnoughArguments, true + } + switch cmd { // delete message case "d": @@ -542,9 +551,6 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) if c.me == nil { return "@me is not initialized", true } - if len(args) < 1 { - return "Not enough arguments", true - } messages, err := c.getLastMessages(chatID, "", c.me.Id, 1) if err != nil { @@ -575,10 +581,6 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) } // send without sound case "silent": - if len(args) < 1 { - return "Not enough arguments", true - } - content := c.PrepareOutgoingMessageContent(rawCmdArguments(cmdline, 0)) if content != nil { @@ -597,10 +599,6 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) } // schedule a message to timestamp or to going online case "schedule": - if len(args) < 2 { - return "Not enough arguments", true - } - var state client.MessageSchedulingState var result string due := args[0] @@ -677,10 +675,6 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) } // forward a message to chat case "forward": - if len(args) < 2 { - return notEnoughArguments, true - } - messageId, err := strconv.ParseInt(args[0], 10, 64) if err != nil { return "Cannot parse message ID", true @@ -742,10 +736,6 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) } // create group chat with current user case "group": - if len(args) < 1 { - return notEnoughArguments, true - } - _, err := c.client.CreateNewBasicGroupChat(&client.CreateNewBasicGroupChatRequest{ UserIds: []int64{chatID}, Title: args[0], @@ -773,10 +763,6 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) } // invite @username to current groupchat case "invite": - if len(args) < 1 { - return notEnoughArguments, true - } - contact, _, err := c.GetContactByUsername(args[0]) if err != nil { return err.Error(), true @@ -801,10 +787,6 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) return link.InviteLink, true // kick @username from current group chat case "kick": - if len(args) < 1 { - return notEnoughArguments, true - } - contact, _, err := c.GetContactByUsername(args[0]) if err != nil { return err.Error(), true @@ -820,10 +802,6 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) } // mute @username [n hours] case "mute": - if len(args) < 1 { - return notEnoughArguments, true - } - contact, _, err := c.GetContactByUsername(args[0]) if err != nil { return err.Error(), true @@ -851,10 +829,6 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) } // unmute @username case "unmute": - if len(args) < 1 { - return notEnoughArguments, true - } - contact, _, err := c.GetContactByUsername(args[0]) if err != nil { return err.Error(), true @@ -874,10 +848,6 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) } // ban @username from current chat [for N hours] case "ban": - if len(args) < 1 { - return notEnoughArguments, true - } - contact, _, err := c.GetContactByUsername(args[0]) if err != nil { return err.Error(), true @@ -903,10 +873,6 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) } // unban @username case "unban": - if len(args) < 1 { - return notEnoughArguments, true - } - contact, _, err := c.GetContactByUsername(args[0]) if err != nil { return err.Error(), true @@ -922,10 +888,6 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) } // promote @username to admin case "promote": - if len(args) < 1 { - return notEnoughArguments, true - } - contact, _, err := c.GetContactByUsername(args[0]) if err != nil { return err.Error(), true @@ -1133,10 +1095,6 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool) } func (c *Client) cmdAdd(args []string) string { - if len(args) < 1 { - return notEnoughArguments - } - chat, err := c.client.SearchPublicChat(&client.SearchPublicChatRequest{ Username: args[0], }) @@ -1153,10 +1111,6 @@ func (c *Client) cmdAdd(args []string) string { } func (c *Client) cmdJoin(args []string) string { - if len(args) < 1 { - return notEnoughArguments - } - if strings.HasPrefix(args[0], "@") { chat, err := c.client.SearchPublicChat(&client.SearchPublicChatRequest{ Username: args[0], @@ -1186,10 +1140,6 @@ func (c *Client) cmdJoin(args []string) string { } func (c *Client) cmdSupergroup(args []string, cmdline string) string { - if len(args) < 1 { - return notEnoughArguments - } - _, err := c.client.CreateNewSupergroupChat(&client.CreateNewSupergroupChatRequest{ Title: args[0], Description: rawCmdArguments(cmdline, 1), @@ -1202,10 +1152,6 @@ func (c *Client) cmdSupergroup(args []string, cmdline string) string { } func (c *Client) cmdChannel(args []string, cmdline string) string { - if len(args) < 1 { - return notEnoughArguments - } - _, err := c.client.CreateNewSupergroupChat(&client.CreateNewSupergroupChatRequest{ Title: args[0], Description: rawCmdArguments(cmdline, 1), diff --git a/xmpp/handlers.go b/xmpp/handlers.go index 6fb1af2..1885aae 100644 --- a/xmpp/handlers.go +++ b/xmpp/handlers.go @@ -490,23 +490,30 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoInfo) { disco := answer.DiscoInfo() _, ok := toToID(iq.To) - if ok { - disco.AddIdentity("", "account", "registered") - disco.AddFeatures(stanza.NSMsgChatMarkers) - disco.AddFeatures(stanza.NSMsgReceipts) - } else { - if di.Node == "" { + if di.Node == "" { + if ok { + disco.AddIdentity("", "account", "registered") + disco.AddFeatures(stanza.NSMsgChatMarkers) + disco.AddFeatures(stanza.NSMsgReceipts) + } else { disco.AddIdentity("Telegram Gateway", "gateway", "telegram") disco.AddFeatures("jabber:iq:register") - disco.AddFeatures(NSCommand) + } + disco.AddFeatures(NSCommand) + } else { + var cmdType telegram.CommandType + if ok { + cmdType = telegram.CommandTypeChat } else { - for name, command := range telegram.GetCommands(telegram.CommandTypeTransport) { - if di.Node == name { - answer.Payload = di - di.AddIdentity(telegram.CommandToHelpString(name, command), "automation", "command-node") - di.AddFeatures(NSCommand, "jabber:x:data") - break - } + cmdType = telegram.CommandTypeTransport + } + + for name, command := range telegram.GetCommands(cmdType) { + if di.Node == name { + answer.Payload = di + di.AddIdentity(telegram.CommandToHelpString(name, command), "automation", "command-node") + di.AddFeatures(NSCommand, "jabber:x:data") + break } } } @@ -539,16 +546,22 @@ func handleGetDiscoItems(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoItems) { log.Debugf("discoItems: %#v", di) _, ok := toToID(iq.To) - if !ok { - commands := telegram.GetCommands(telegram.CommandTypeTransport) - if di.Node == NSCommand { - answer.Payload = di - for name, command := range commands { - di.AddItem(iq.To, name, telegram.CommandToHelpString(name, command)) - } + if di.Node == NSCommand { + answer.Payload = di + + var cmdType telegram.CommandType + if ok { + cmdType = telegram.CommandTypeChat } else { - answer.Payload = answer.DiscoItems() + cmdType = telegram.CommandTypeTransport } + + commands := telegram.GetCommands(cmdType) + for name, command := range commands { + di.AddItem(iq.To, name, telegram.CommandToHelpString(name, command)) + } + } else { + answer.Payload = answer.DiscoItems() } component, ok := s.(*xmpp.Component) @@ -706,66 +719,70 @@ func handleSetQueryCommand(s xmpp.Sender, iq *stanza.IQ, command *stanza.Command if !ok { return } - _, toOk := toToID(iq.To) + toId, toOk := toToID(iq.To) var cmdString string - if !toOk { - form, formOk := command.CommandElement.(*stanza.Form) - if formOk { - // 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]) - } + var cmdType telegram.CommandType + form, formOk := command.CommandElement.(*stanza.Form) + if toOk { + cmdType = telegram.CommandTypeChat + } else { + cmdType = telegram.CommandTypeTransport + } + if formOk { + // 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 + }) - cmdString = cmd.String() - } else { - if command.Action == "" || command.Action == stanza.CommandActionExecute { - cmd, ok := telegram.GetCommand(telegram.CommandTypeTransport, command.Node) - if ok && cmd.RequiredArgs > 0 { - var fields []*stanza.Field - for i, arg := range cmd.Arguments { - fields = append(fields, &stanza.Field{ - Var: strconv.FormatInt(int64(i), 10), - Label: arg, - }) - } - answer.Payload = &stanza.Command{ - SessionId: command.Node, - Node: command.Node, - Status: stanza.CommandStatusExecuting, - CommandElement: &stanza.Form{ - Type: stanza.FormTypeForm, - Title: command.Node, - Instructions: []string{cmd.Description}, - Fields: fields, - }, - } - } else { - cmdString = "/" + command.Node + 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 { + fields = append(fields, &stanza.Field{ + Var: strconv.FormatInt(int64(i), 10), + Label: arg, + }) } - } else if command.Action == stanza.CommandActionCancel { answer.Payload = &stanza.Command{ - SessionId: command.Node, - Node: command.Node, - Status: stanza.CommandStatusCancelled, + SessionId: command.Node, + Node: command.Node, + Status: stanza.CommandStatusExecuting, + CommandElement: &stanza.Form{ + Type: stanza.FormTypeForm, + Title: command.Node, + Instructions: []string{cmd.Description}, + Fields: fields, + }, } + } else { + cmdString = "/" + command.Node + } + } else if command.Action == stanza.CommandActionCancel { + answer.Payload = &stanza.Command{ + SessionId: command.Node, + Node: command.Node, + Status: stanza.CommandStatusCancelled, } } } @@ -776,7 +793,12 @@ func handleSetQueryCommand(s xmpp.Sender, iq *stanza.IQ, command *stanza.Command return } - response := session.ProcessTransportCommand(cmdString, resource) + var response string + if toOk { + response, _ = session.ProcessChatCommand(toId, cmdString) + } else { + response = session.ProcessTransportCommand(cmdString, resource) + } answer.Payload = &stanza.Command{ SessionId: command.Node,