Implement the command parser
Right now this just supports help and version, but will be getting new commands shortly.
This commit is contained in:
112
bridge/commandhandler.go
Normal file
112
bridge/commandhandler.go
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/alecthomas/kong"
|
||||||
|
"github.com/google/shlex"
|
||||||
|
|
||||||
|
"maunium.net/go/maulogger/v2"
|
||||||
|
"maunium.net/go/mautrix/id"
|
||||||
|
)
|
||||||
|
|
||||||
|
type commandHandler struct {
|
||||||
|
bridge *Bridge
|
||||||
|
log maulogger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCommandHandler(bridge *Bridge) *commandHandler {
|
||||||
|
return &commandHandler{
|
||||||
|
bridge: bridge,
|
||||||
|
log: bridge.log.Sub("Commands"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func commandsHelpPrinter(options kong.HelpOptions, ctx *kong.Context) error {
|
||||||
|
selected := ctx.Selected()
|
||||||
|
|
||||||
|
if selected == nil {
|
||||||
|
for _, cmd := range ctx.Model.Leaves(true) {
|
||||||
|
fmt.Fprintf(ctx.Stdout, " * %s - %s\n", cmd.Path(), cmd.Help)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(ctx.Stdout, "%s - %s\n", selected.Path(), selected.Help)
|
||||||
|
if selected.Detail != "" {
|
||||||
|
fmt.Fprintf(ctx.Stdout, "\n%s\n", selected.Detail)
|
||||||
|
}
|
||||||
|
if len(selected.Positional) > 0 {
|
||||||
|
fmt.Fprintf(ctx.Stdout, "\nArguments:\n")
|
||||||
|
for _, arg := range selected.Positional {
|
||||||
|
fmt.Fprintf(ctx.Stdout, "%s %s\n", arg.Summary(), arg.Help)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *commandHandler) handle(roomID id.RoomID, user *User, message string, replyTo id.EventID) {
|
||||||
|
cmd := commands{
|
||||||
|
globals: globals{
|
||||||
|
bot: h.bridge.bot,
|
||||||
|
bridge: h.bridge,
|
||||||
|
portal: h.bridge.GetPortalByMXID(roomID),
|
||||||
|
handler: h,
|
||||||
|
roomID: roomID,
|
||||||
|
user: user,
|
||||||
|
replyTo: replyTo,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := &strings.Builder{}
|
||||||
|
|
||||||
|
parse, err := kong.New(
|
||||||
|
&cmd,
|
||||||
|
kong.Exit(func(int) {}),
|
||||||
|
kong.NoDefaultHelp(),
|
||||||
|
kong.Writers(buf, buf),
|
||||||
|
kong.Help(commandsHelpPrinter),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
h.log.Warnf("Failed to create argument parser for %q: %v", roomID, err)
|
||||||
|
|
||||||
|
cmd.globals.reply("unexpected error, please try again shortly")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err := shlex.Split(message)
|
||||||
|
if err != nil {
|
||||||
|
h.log.Warnf("Failed to split message %q: %v", message, err)
|
||||||
|
|
||||||
|
cmd.globals.reply("failed to process the command")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, err := parse.Parse(args)
|
||||||
|
if err != nil {
|
||||||
|
h.log.Warnf("Failed to parse command %q: %v", message, err)
|
||||||
|
|
||||||
|
cmd.globals.reply("failed to process the command")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.globals.context = ctx
|
||||||
|
|
||||||
|
err = ctx.Run(&cmd.globals)
|
||||||
|
if err != nil {
|
||||||
|
h.log.Warnf("Command %q failed: %v", message, err)
|
||||||
|
|
||||||
|
cmd.globals.reply("unexpected failure")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if buf.Len() > 0 {
|
||||||
|
cmd.globals.reply(buf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
81
bridge/commands.go
Normal file
81
bridge/commands.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package bridge
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/alecthomas/kong"
|
||||||
|
|
||||||
|
"maunium.net/go/mautrix/appservice"
|
||||||
|
"maunium.net/go/mautrix/event"
|
||||||
|
"maunium.net/go/mautrix/format"
|
||||||
|
"maunium.net/go/mautrix/id"
|
||||||
|
|
||||||
|
"gitlab.com/beeper/discord/consts"
|
||||||
|
"gitlab.com/beeper/discord/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
type globals struct {
|
||||||
|
context *kong.Context
|
||||||
|
|
||||||
|
bridge *Bridge
|
||||||
|
bot *appservice.IntentAPI
|
||||||
|
portal *Portal
|
||||||
|
handler *commandHandler
|
||||||
|
roomID id.RoomID
|
||||||
|
user *User
|
||||||
|
replyTo id.EventID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *globals) reply(msg string) {
|
||||||
|
content := format.RenderMarkdown(msg, true, false)
|
||||||
|
content.MsgType = event.MsgNotice
|
||||||
|
intent := g.bot
|
||||||
|
|
||||||
|
if g.portal != nil && g.portal.IsPrivateChat() {
|
||||||
|
intent = g.portal.MainIntent()
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := intent.SendMessageEvent(g.roomID, event.EventMessage, content)
|
||||||
|
if err != nil {
|
||||||
|
g.handler.log.Warnfln("Failed to reply to command from %q: %v", g.user.MXID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type commands struct {
|
||||||
|
globals
|
||||||
|
|
||||||
|
Help helpCmd `kong:"cmd,help='Displays this message.'"`
|
||||||
|
Version versionCmd `kong:"cmd,help='Displays the version of the bridge.'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type helpCmd struct {
|
||||||
|
Command []string `kong:"arg,optional,help='The command to get help on.'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *helpCmd) Run(g *globals) error {
|
||||||
|
ctx, err := kong.Trace(g.context.Kong, c.Command)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Error != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ctx.PrintUsage(true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(g.context.Stdout)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type versionCmd struct{}
|
||||||
|
|
||||||
|
func (c *versionCmd) Run(g *globals) error {
|
||||||
|
fmt.Fprintln(g.context.Stdout, consts.Name, version.String)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package bridge
|
package bridge
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"maunium.net/go/maulogger/v2"
|
"maunium.net/go/maulogger/v2"
|
||||||
"maunium.net/go/mautrix"
|
"maunium.net/go/mautrix"
|
||||||
"maunium.net/go/mautrix/appservice"
|
"maunium.net/go/mautrix/appservice"
|
||||||
@@ -13,6 +15,7 @@ type matrixHandler struct {
|
|||||||
as *appservice.AppService
|
as *appservice.AppService
|
||||||
bridge *Bridge
|
bridge *Bridge
|
||||||
log maulogger.Logger
|
log maulogger.Logger
|
||||||
|
cmd *commandHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Bridge) setupEvents() {
|
func (b *Bridge) setupEvents() {
|
||||||
@@ -22,6 +25,7 @@ func (b *Bridge) setupEvents() {
|
|||||||
as: b.as,
|
as: b.as,
|
||||||
bridge: b,
|
bridge: b,
|
||||||
log: b.log.Sub("Matrix"),
|
log: b.log.Sub("Matrix"),
|
||||||
|
cmd: newCommandHandler(b),
|
||||||
}
|
}
|
||||||
|
|
||||||
b.eventProcessor.On(event.EventMessage, b.matrixHandler.handleMessage)
|
b.eventProcessor.On(event.EventMessage, b.matrixHandler.handleMessage)
|
||||||
@@ -61,11 +65,31 @@ func (mh *matrixHandler) ignoreEvent(evt *event.Event) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mh *matrixHandler) handleMessage(evt *event.Event) {
|
func (mh *matrixHandler) handleMessage(evt *event.Event) {
|
||||||
mh.log.Debugfln("received message from %q: %q", evt.Sender, evt.Content.AsMessage())
|
|
||||||
if mh.ignoreEvent(evt) {
|
if mh.ignoreEvent(evt) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user := mh.bridge.GetUserByMXID(evt.Sender)
|
||||||
|
if user == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
content := evt.Content.AsMessage()
|
||||||
|
content.RemoveReplyFallback()
|
||||||
|
|
||||||
|
if content.MsgType == event.MsgText {
|
||||||
|
prefix := mh.bridge.config.Bridge.CommandPrefix
|
||||||
|
|
||||||
|
hasPrefix := strings.HasPrefix(content.Body, prefix)
|
||||||
|
if hasPrefix {
|
||||||
|
content.Body = strings.TrimLeft(content.Body[len(prefix):], " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasPrefix || evt.RoomID == user.ManagementRoom {
|
||||||
|
mh.cmd.handle(evt.RoomID, user, content.Body, content.GetReplyTo())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mh *matrixHandler) joinAndCheckMembers(evt *event.Event, intent *appservice.IntentAPI) *mautrix.RespJoinedMembers {
|
func (mh *matrixHandler) joinAndCheckMembers(evt *event.Event, intent *appservice.IntentAPI) *mautrix.RespJoinedMembers {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
log "maunium.net/go/maulogger/v2"
|
log "maunium.net/go/maulogger/v2"
|
||||||
|
|
||||||
|
"maunium.net/go/mautrix/appservice"
|
||||||
"maunium.net/go/mautrix/event"
|
"maunium.net/go/mautrix/event"
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
|
|
||||||
@@ -93,3 +95,15 @@ func (p *Portal) messageLoop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Portal) IsPrivateChat() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Portal) MainIntent() *appservice.IntentAPI {
|
||||||
|
if p.IsPrivateChat() {
|
||||||
|
return p.bridge.GetPuppetByID(p.Key.ID).DefaultIntent()
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.bridge.bot
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
log "maunium.net/go/maulogger/v2"
|
log "maunium.net/go/maulogger/v2"
|
||||||
|
"maunium.net/go/mautrix/appservice"
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
|
|
||||||
"gitlab.com/beeper/discord/database"
|
"gitlab.com/beeper/discord/database"
|
||||||
@@ -85,3 +86,7 @@ func (b *Bridge) FormatPuppetMXID(did string) id.UserID {
|
|||||||
b.config.Homeserver.Domain,
|
b.config.Homeserver.Domain,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Puppet) DefaultIntent() *appservice.IntentAPI {
|
||||||
|
return p.bridge.as.Intent(p.MXID)
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import (
|
|||||||
type bridge struct {
|
type bridge struct {
|
||||||
UsernameTemplate string `yaml:"username_template"`
|
UsernameTemplate string `yaml:"username_template"`
|
||||||
|
|
||||||
|
CommandPrefix string `yaml:"command_prefix"`
|
||||||
|
|
||||||
ManagementRoomText managementRoomText `yaml:"management_root_text"`
|
ManagementRoomText managementRoomText `yaml:"management_root_text"`
|
||||||
|
|
||||||
PortalMessageBuffer int `yaml:"portal_message_buffer"`
|
PortalMessageBuffer int `yaml:"portal_message_buffer"`
|
||||||
@@ -31,6 +33,10 @@ func (b *bridge) validate() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.CommandPrefix == "" {
|
||||||
|
b.CommandPrefix = "!dis"
|
||||||
|
}
|
||||||
|
|
||||||
if err := b.ManagementRoomText.validate(); err != nil {
|
if err := b.ManagementRoomText.validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -15,6 +15,7 @@ require (
|
|||||||
require (
|
require (
|
||||||
github.com/btcsuite/btcutil v1.0.2 // indirect
|
github.com/btcsuite/btcutil v1.0.2 // indirect
|
||||||
github.com/bwmarrin/discordgo v0.23.2 // indirect
|
github.com/bwmarrin/discordgo v0.23.2 // indirect
|
||||||
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||||
github.com/gorilla/mux v1.8.0 // indirect
|
github.com/gorilla/mux v1.8.0 // indirect
|
||||||
github.com/gorilla/websocket v1.4.2 // indirect
|
github.com/gorilla/websocket v1.4.2 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -21,6 +21,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
|||||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
||||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||||
|
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
|
|||||||
Reference in New Issue
Block a user