Basic Ad-Hoc support for transport commands
This commit is contained in:
parent
20e6d2558e
commit
fd0d7411c2
2
go.mod
2
go.mod
|
@ -33,5 +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-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
|
replace github.com/zelenin/go-tdlib => dev.narayana.im/narayana/go-tdlib v0.0.0-20240124222245-b4c12addb061
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -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-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=
|
||||||
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=
|
||||||
|
|
|
@ -48,6 +48,7 @@ var permissionsMember = client.ChatPermissions{
|
||||||
var permissionsReadonly = client.ChatPermissions{}
|
var permissionsReadonly = client.ChatPermissions{}
|
||||||
|
|
||||||
var transportCommands = map[string]command{
|
var transportCommands = map[string]command{
|
||||||
|
"help": command{"", "help"},
|
||||||
"login": command{"phone", "sign in"},
|
"login": command{"phone", "sign in"},
|
||||||
"logout": command{"", "sign out"},
|
"logout": command{"", "sign out"},
|
||||||
"cancelauth": command{"", "quit the signin wizard"},
|
"cancelauth": command{"", "quit the signin wizard"},
|
||||||
|
@ -66,6 +67,7 @@ var transportCommands = map[string]command{
|
||||||
}
|
}
|
||||||
|
|
||||||
var chatCommands = map[string]command{
|
var chatCommands = map[string]command{
|
||||||
|
"help": command{"", "help"},
|
||||||
"d": command{"[n]", "delete your last message(s)"},
|
"d": command{"[n]", "delete your last message(s)"},
|
||||||
"s": command{"edited message", "edit your last message"},
|
"s": command{"edited message", "edit your last message"},
|
||||||
"silent": command{"message", "send a message without sound"},
|
"silent": command{"message", "send a message without sound"},
|
||||||
|
@ -110,38 +112,56 @@ type command struct {
|
||||||
}
|
}
|
||||||
type configurationOption command
|
type configurationOption command
|
||||||
|
|
||||||
type helpType int
|
// CommandType disinguishes command sets by chat
|
||||||
|
type CommandType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
helpTypeTransport helpType = iota
|
CommandTypeTransport CommandType = iota
|
||||||
helpTypeChat
|
CommandTypeChat
|
||||||
)
|
)
|
||||||
|
|
||||||
func helpString(ht helpType) string {
|
// GetCommands exposes the set of commands
|
||||||
var str strings.Builder
|
func GetCommands(typ CommandType) map[string]command {
|
||||||
var commandMap map[string]command
|
var commandMap map[string]command
|
||||||
|
|
||||||
switch ht {
|
switch typ {
|
||||||
case helpTypeTransport:
|
case CommandTypeTransport:
|
||||||
commandMap = transportCommands
|
commandMap = transportCommands
|
||||||
case helpTypeChat:
|
case CommandTypeChat:
|
||||||
commandMap = chatCommands
|
commandMap = chatCommands
|
||||||
}
|
}
|
||||||
|
|
||||||
str.WriteString("Available commands:\n")
|
return commandMap
|
||||||
for name, command := range commandMap {
|
}
|
||||||
|
|
||||||
|
// CommandToHelpString builds a text description of a command
|
||||||
|
func CommandToHelpString(name string, cmd command) string {
|
||||||
|
var str strings.Builder
|
||||||
|
|
||||||
str.WriteString("/")
|
str.WriteString("/")
|
||||||
str.WriteString(name)
|
str.WriteString(name)
|
||||||
if command.arguments != "" {
|
if cmd.arguments != "" {
|
||||||
str.WriteString(" ")
|
str.WriteString(" ")
|
||||||
str.WriteString(command.arguments)
|
str.WriteString(cmd.arguments)
|
||||||
}
|
}
|
||||||
str.WriteString(" — ")
|
str.WriteString(" — ")
|
||||||
str.WriteString(command.description)
|
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(CommandToHelpString(name, command))
|
||||||
str.WriteString("\n")
|
str.WriteString("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
if ht == helpTypeTransport {
|
if typ == CommandTypeTransport {
|
||||||
str.WriteString("Configuration options\n")
|
str.WriteString("Configuration options\n")
|
||||||
for name, option := range transportConfigurationOptions {
|
for name, option := range transportConfigurationOptions {
|
||||||
str.WriteString(name)
|
str.WriteString(name)
|
||||||
|
@ -448,7 +468,7 @@ func (c *Client) ProcessTransportCommand(cmdline string, resource string) string
|
||||||
case "channel":
|
case "channel":
|
||||||
return c.cmdChannel(args, cmdline)
|
return c.cmdChannel(args, cmdline)
|
||||||
case "help":
|
case "help":
|
||||||
return helpString(helpTypeTransport)
|
return helpString(CommandTypeTransport)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
@ -1088,7 +1108,7 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool)
|
||||||
|
|
||||||
return strings.Join(entries, "\n"), true
|
return strings.Join(entries, "\n"), true
|
||||||
case "help":
|
case "help":
|
||||||
return helpString(helpTypeChat), true
|
return helpString(CommandTypeChat), true
|
||||||
default:
|
default:
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ 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)
|
||||||
|
@ -53,14 +54,14 @@ func HandleIq(s xmpp.Sender, p stanza.Packet) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, ok = iq.Payload.(*stanza.DiscoInfo)
|
discoInfo, ok := iq.Payload.(*stanza.DiscoInfo)
|
||||||
if ok {
|
if ok {
|
||||||
go handleGetDiscoInfo(s, iq)
|
go handleGetDiscoInfo(s, iq, discoInfo)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, ok = iq.Payload.(*stanza.DiscoItems)
|
discoItems, ok := iq.Payload.(*stanza.DiscoItems)
|
||||||
if ok {
|
if ok {
|
||||||
go handleGetDiscoItems(s, iq)
|
go handleGetDiscoItems(s, iq, discoItems)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, ok = iq.Payload.(*extensions.QueryRegister)
|
_, ok = iq.Payload.(*extensions.QueryRegister)
|
||||||
|
@ -74,6 +75,11 @@ 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,7 +474,7 @@ func handleGetVcardIq(s xmpp.Sender, iq *stanza.IQ, typ byte) {
|
||||||
_ = gateway.ResumableSend(component, &answer)
|
_ = 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{
|
answer, err := stanza.NewIQ(stanza.Attrs{
|
||||||
Type: stanza.IQTypeResult,
|
Type: stanza.IQTypeResult,
|
||||||
From: iq.To,
|
From: iq.To,
|
||||||
|
@ -488,8 +494,20 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ) {
|
||||||
disco.AddFeatures(stanza.NSMsgChatMarkers)
|
disco.AddFeatures(stanza.NSMsgChatMarkers)
|
||||||
disco.AddFeatures(stanza.NSMsgReceipts)
|
disco.AddFeatures(stanza.NSMsgReceipts)
|
||||||
} else {
|
} else {
|
||||||
|
if di.Node == "" {
|
||||||
disco.AddIdentity("Telegram Gateway", "gateway", "telegram")
|
disco.AddIdentity("Telegram Gateway", "gateway", "telegram")
|
||||||
disco.AddFeatures("jabber:iq:register")
|
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
|
answer.Payload = disco
|
||||||
|
|
||||||
|
@ -504,7 +522,7 @@ func handleGetDiscoInfo(s xmpp.Sender, iq *stanza.IQ) {
|
||||||
_ = gateway.ResumableSend(component, answer)
|
_ = 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{
|
answer, err := stanza.NewIQ(stanza.Attrs{
|
||||||
Type: stanza.IQTypeResult,
|
Type: stanza.IQTypeResult,
|
||||||
From: iq.To,
|
From: iq.To,
|
||||||
|
@ -517,7 +535,20 @@ func handleGetDiscoItems(s xmpp.Sender, iq *stanza.IQ) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
answer.Payload = answer.DiscoItems()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
component, ok := s.(*xmpp.Component)
|
component, ok := s.(*xmpp.Component)
|
||||||
if !ok {
|
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) {
|
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