Basic Ad-Hoc support for transport commands

This commit is contained in:
Bohdan Horbeshko 2024-01-30 21:38:46 -05:00
parent 20e6d2558e
commit fd0d7411c2
4 changed files with 133 additions and 29 deletions

2
go.mod
View file

@ -33,5 +33,5 @@ require (
nhooyr.io/websocket v1.6.5 // indirect
)
replace gosrc.io/xmpp => dev.narayana.im/narayana/go-xmpp v0.0.0-20220524203317-306b4ff58e8f
replace gosrc.io/xmpp => dev.narayana.im/narayana/go-xmpp v0.0.0-20240131013505-18c46e6c59fd
replace github.com/zelenin/go-tdlib => dev.narayana.im/narayana/go-tdlib v0.0.0-20240124222245-b4c12addb061

2
go.sum
View file

@ -7,6 +7,8 @@ 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-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-20240131013505-18c46e6c59fd h1:+UW+E7JjI88aH4beDn1cw6D8rs1I061hN91HU4Y4pT8=
dev.narayana.im/narayana/go-xmpp v0.0.0-20240131013505-18c46e6c59fd/go.mod h1:L3NFMqYOxyLz3JGmgFyWf7r9htE91zVGiK40oW4RwdY=
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/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=

View file

@ -48,6 +48,7 @@ var permissionsMember = client.ChatPermissions{
var permissionsReadonly = client.ChatPermissions{}
var transportCommands = map[string]command{
"help": command{"", "help"},
"login": command{"phone", "sign in"},
"logout": command{"", "sign out"},
"cancelauth": command{"", "quit the signin wizard"},
@ -66,6 +67,7 @@ var transportCommands = map[string]command{
}
var chatCommands = map[string]command{
"help": command{"", "help"},
"d": command{"[n]", "delete your last message(s)"},
"s": command{"edited message", "edit your last message"},
"silent": command{"message", "send a message without sound"},
@ -110,38 +112,56 @@ type command struct {
}
type configurationOption command
type helpType int
// CommandType disinguishes command sets by chat
type CommandType int
const (
helpTypeTransport helpType = iota
helpTypeChat
CommandTypeTransport CommandType = iota
CommandTypeChat
)
func helpString(ht helpType) string {
var str strings.Builder
// GetCommands exposes the set of commands
func GetCommands(typ CommandType) map[string]command {
var commandMap map[string]command
switch ht {
case helpTypeTransport:
switch typ {
case CommandTypeTransport:
commandMap = transportCommands
case helpTypeChat:
case CommandTypeChat:
commandMap = chatCommands
}
return commandMap
}
// CommandToHelpString builds a text description of a command
func CommandToHelpString(name string, cmd command) string {
var str strings.Builder
str.WriteString("/")
str.WriteString(name)
if cmd.arguments != "" {
str.WriteString(" ")
str.WriteString(cmd.arguments)
}
str.WriteString(" — ")
str.WriteString(cmd.description)
return str.String()
}
func helpString(typ CommandType) string {
var str strings.Builder
commandMap := GetCommands(typ)
str.WriteString("Available commands:\n")
for name, command := range commandMap {
str.WriteString("/")
str.WriteString(name)
if command.arguments != "" {
str.WriteString(" ")
str.WriteString(command.arguments)
}
str.WriteString(" — ")
str.WriteString(command.description)
str.WriteString(CommandToHelpString(name, command))
str.WriteString("\n")
}
if ht == helpTypeTransport {
if typ == CommandTypeTransport {
str.WriteString("Configuration options\n")
for name, option := range transportConfigurationOptions {
str.WriteString(name)
@ -448,7 +468,7 @@ func (c *Client) ProcessTransportCommand(cmdline string, resource string) string
case "channel":
return c.cmdChannel(args, cmdline)
case "help":
return helpString(helpTypeTransport)
return helpString(CommandTypeTransport)
}
return ""
@ -1088,7 +1108,7 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool)
return strings.Join(entries, "\n"), true
case "help":
return helpString(helpTypeChat), true
return helpString(CommandTypeChat), true
default:
return "", false
}

View file

@ -26,6 +26,7 @@ const (
TypeVCard4
)
const NodeVCard4 string = "urn:xmpp:vcard4"
const NSCommand string = "http://jabber.org/protocol/commands"
func logPacketType(p stanza.Packet) {
log.Warnf("Ignoring packet: %T\n", p)
@ -53,14 +54,14 @@ func HandleIq(s xmpp.Sender, p stanza.Packet) {
return
}
}
_, ok = iq.Payload.(*stanza.DiscoInfo)
discoInfo, ok := iq.Payload.(*stanza.DiscoInfo)
if ok {
go handleGetDiscoInfo(s, iq)
go handleGetDiscoInfo(s, iq, discoInfo)
return
}
_, ok = iq.Payload.(*stanza.DiscoItems)
discoItems, ok := iq.Payload.(*stanza.DiscoItems)
if ok {
go handleGetDiscoItems(s, iq)
go handleGetDiscoItems(s, iq, discoItems)
return
}
_, ok = iq.Payload.(*extensions.QueryRegister)
@ -74,6 +75,11 @@ func HandleIq(s xmpp.Sender, p stanza.Packet) {
go handleSetQueryRegister(s, iq, query)
return
}
command, ok := iq.Payload.(*stanza.Command)
if ok {
go handleSetQueryCommand(s, iq, command)
return
}
}
}
@ -468,7 +474,7 @@ func handleGetVcardIq(s xmpp.Sender, iq *stanza.IQ, typ byte) {
_ = gateway.ResumableSend(component, &answer)
}
func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ) {
func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoInfo) {
answer, err := stanza.NewIQ(stanza.Attrs{
Type: stanza.IQTypeResult,
From: iq.To,
@ -488,8 +494,20 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ) {
disco.AddFeatures(stanza.NSMsgChatMarkers)
disco.AddFeatures(stanza.NSMsgReceipts)
} else {
disco.AddIdentity("Telegram Gateway", "gateway", "telegram")
disco.AddFeatures("jabber:iq:register")
if di.Node == "" {
disco.AddIdentity("Telegram Gateway", "gateway", "telegram")
disco.AddFeatures("jabber:iq:register")
disco.AddFeatures(NSCommand)
} 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
}
}
}
}
answer.Payload = disco
@ -504,7 +522,7 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ) {
_ = gateway.ResumableSend(component, answer)
}
func handleGetDiscoItems(s xmpp.Sender, iq *stanza.IQ) {
func handleGetDiscoItems(s xmpp.Sender, iq *stanza.IQ, di *stanza.DiscoItems) {
answer, err := stanza.NewIQ(stanza.Attrs{
Type: stanza.IQTypeResult,
From: iq.To,
@ -517,7 +535,20 @@ func handleGetDiscoItems(s xmpp.Sender, iq *stanza.IQ) {
return
}
answer.Payload = answer.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))
}
} else {
answer.Payload = answer.DiscoItems()
}
}
component, ok := s.(*xmpp.Component)
if !ok {
@ -647,6 +678,57 @@ 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)
if command.Action == "" || command.Action == stanza.CommandActionExecute {
_, ok := toToID(iq.To)
if !ok {
bare, resource, ok := gateway.SplitJID(iq.From)
if !ok {
return
}
session, ok := sessions[bare]
if !ok {
return
}
response := session.ProcessTransportCommand("/" + command.Node, resource)
answer.Payload = &stanza.Command{
Node: command.Node,
Status: stanza.CommandStatusCompleted,
CommandElement: &stanza.Note{
Text: response,
Type: stanza.CommandNoteTypeInfo,
},
}
log.Debugf("command response: %#v", answer.Payload)
}
}
}
func iqAnswerSetError(answer *stanza.IQ, payload *extensions.QueryRegister, code int) {
answer.Type = stanza.IQTypeError
answer.Payload = *payload