123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- package bridge
- import (
- "context"
- "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"
- "go.mau.fi/mautrix-discord/consts"
- "go.mau.fi/mautrix-discord/remoteauth"
- "go.mau.fi/mautrix-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
- Disconnect disconnectCmd `kong:"cmd,help='Disconnect from Discord'"`
- Help helpCmd `kong:"cmd,help='Displays this message.'"`
- Login loginCmd `kong:"cmd,help='Log in to Discord.'"`
- Logout logoutCmd `kong:"cmd,help='Log out of Discord.'"`
- Reconnect reconnectCmd `kong:"cmd,help='Reconnect to Discord'"`
- Version versionCmd `kong:"cmd,help='Displays the version of the bridge.'"`
- Guilds guildsCmd `kong:"cmd,help='Guild bridging management.'"`
- LoginMatrix loginMatrixCmd `kong:"cmd,help='Replace the puppet for your Discord account with your real Matrix account.'"`
- LogoutMatrix logoutMatrixCmd `kong:"cmd,help='Switch the puppet for your Discord account back to the default one.'"`
- PingMatrix pingMatrixCmd `kong:"cmd,help='check if your double puppet is working properly'"`
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Help Command
- ///////////////////////////////////////////////////////////////////////////////
- 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
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Version Command
- ///////////////////////////////////////////////////////////////////////////////
- type versionCmd struct{}
- func (c *versionCmd) Run(g *globals) error {
- fmt.Fprintln(g.context.Stdout, consts.Name, version.String)
- return nil
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Login Command
- ///////////////////////////////////////////////////////////////////////////////
- type loginCmd struct{}
- func (l *loginCmd) Run(g *globals) error {
- if g.user.LoggedIn() {
- fmt.Fprintf(g.context.Stdout, "You are already logged in")
- return fmt.Errorf("user already logged in")
- }
- client, err := remoteauth.New()
- if err != nil {
- return err
- }
- qrChan := make(chan string)
- doneChan := make(chan struct{})
- var qrCodeEvent id.EventID
- go func() {
- code := <-qrChan
- resp, err := g.user.sendQRCode(g.bot, g.roomID, code)
- if err != nil {
- fmt.Fprintln(g.context.Stdout, "Failed to generate the qrcode")
- return
- }
- qrCodeEvent = resp
- }()
- ctx := context.Background()
- if err := client.Dial(ctx, qrChan, doneChan); err != nil {
- close(qrChan)
- close(doneChan)
- return err
- }
- <-doneChan
- if qrCodeEvent != "" {
- _, err := g.bot.RedactEvent(g.roomID, qrCodeEvent)
- if err != nil {
- fmt.Errorf("Failed to redact the qrcode: %v", err)
- }
- }
- user, err := client.Result()
- if err != nil {
- fmt.Fprintln(g.context.Stdout, "Failed to log in")
- return err
- }
- if err := g.user.Login(user.Token); err != nil {
- fmt.Fprintln(g.context.Stdout, "Failed to login", err)
- return err
- }
- g.user.Lock()
- g.user.ID = user.UserID
- g.user.Update()
- g.user.Unlock()
- fmt.Fprintln(g.context.Stdout, "Successfully logged in")
- return nil
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Logout Command
- ///////////////////////////////////////////////////////////////////////////////
- type logoutCmd struct{}
- func (l *logoutCmd) Run(g *globals) error {
- if !g.user.LoggedIn() {
- fmt.Fprintln(g.context.Stdout, "You are not logged in")
- return fmt.Errorf("user is not logged in")
- }
- err := g.user.Logout()
- if err != nil {
- fmt.Fprintln(g.context.Stdout, "Failed to log out")
- return err
- }
- fmt.Fprintln(g.context.Stdout, "Successfully logged out")
- return nil
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Disconnect Command
- ///////////////////////////////////////////////////////////////////////////////
- type disconnectCmd struct{}
- func (d *disconnectCmd) Run(g *globals) error {
- if !g.user.Connected() {
- fmt.Fprintln(g.context.Stdout, "You are not connected")
- return fmt.Errorf("user is not connected")
- }
- if err := g.user.Disconnect(); err != nil {
- fmt.Fprintln(g.context.Stdout, "Failed to disconnect")
- return err
- }
- fmt.Fprintln(g.context.Stdout, "Successfully disconnected")
- return nil
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Reconnect Command
- ///////////////////////////////////////////////////////////////////////////////
- type reconnectCmd struct{}
- func (r *reconnectCmd) Run(g *globals) error {
- if g.user.Connected() {
- fmt.Fprintln(g.context.Stdout, "You are already connected")
- return fmt.Errorf("user is already connected")
- }
- if err := g.user.Connect(); err != nil {
- fmt.Fprintln(g.context.Stdout, "Failed to connect")
- return err
- }
- fmt.Fprintln(g.context.Stdout, "Successfully connected")
- return nil
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LoginMatrix Command
- ///////////////////////////////////////////////////////////////////////////////
- type loginMatrixCmd struct {
- AccessToken string `kong:"arg,help='The shared secret to use the bridge'"`
- }
- func (m *loginMatrixCmd) Run(g *globals) error {
- puppet := g.bridge.GetPuppetByID(g.user.ID)
- err := puppet.SwitchCustomMXID(m.AccessToken, g.user.MXID)
- if err != nil {
- fmt.Fprintf(g.context.Stdout, "Failed to switch puppet: %v", err)
- return err
- }
- fmt.Fprintf(g.context.Stdout, "Successfully switched puppet")
- return nil
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LogoutMatrix Command
- ///////////////////////////////////////////////////////////////////////////////
- type logoutMatrixCmd struct{}
- func (m *logoutMatrixCmd) Run(g *globals) error {
- return nil
- }
- ///////////////////////////////////////////////////////////////////////////////
- // PingMatrix Command
- ///////////////////////////////////////////////////////////////////////////////
- type pingMatrixCmd struct{}
- func (m *pingMatrixCmd) Run(g *globals) error {
- puppet := g.bridge.GetPuppetByCustomMXID(g.user.MXID)
- if puppet == nil || puppet.CustomIntent() == nil {
- fmt.Fprintf(g.context.Stdout, "You have not changed your Discord account's Matrix puppet.")
- return fmt.Errorf("double puppet not configured")
- }
- resp, err := puppet.CustomIntent().Whoami()
- if err != nil {
- fmt.Fprintf(g.context.Stdout, "Failed to validate Matrix login: %v", err)
- return err
- }
- fmt.Fprintf(g.context.Stdout, "Confirmed valid access token for %s / %s", resp.UserID, resp.DeviceID)
- return nil
- }
- ///////////////////////////////////////////////////////////////////////////////
- // Guilds Commands
- ///////////////////////////////////////////////////////////////////////////////
- type guildsCmd struct {
- Status guildStatusCmd `kong:"cmd,help='Show the bridge status for the guilds you are in'"`
- Bridge guildBridgeCmd `kong:"cmd,help='Bridge a guild'"`
- Unbridge guildUnbridgeCmd `kong:"cmd,help='Unbridge a guild'"`
- }
- type guildStatusCmd struct{}
- func (c *guildStatusCmd) Run(g *globals) error {
- g.user.guildsLock.Lock()
- defer g.user.guildsLock.Unlock()
- if len(g.user.guilds) == 0 {
- fmt.Fprintf(g.context.Stdout, "you haven't joined any guilds.")
- } else {
- for _, guild := range g.user.guilds {
- status := "not bridged"
- if guild.Bridge {
- status = "bridged"
- }
- fmt.Fprintf(g.context.Stdout, "%s %s %s\n", guild.GuildName, guild.GuildID, status)
- }
- }
- return nil
- }
- type guildBridgeCmd struct {
- GuildID string `kong:"arg,help='the id of the guild to unbridge'"`
- Entire bool `kong:"flag,help='whether or not to bridge all channels'"`
- }
- func (c *guildBridgeCmd) Run(g *globals) error {
- if err := g.user.bridgeGuild(c.GuildID, c.Entire); err != nil {
- return err
- }
- fmt.Fprintf(g.context.Stdout, "Successfully bridged guild %s", c.GuildID)
- return nil
- }
- type guildUnbridgeCmd struct {
- GuildID string `kong:"arg,help='the id of the guild to unbridge'"`
- }
- func (c *guildUnbridgeCmd) Run(g *globals) error {
- if err := g.user.unbridgeGuild(c.GuildID); err != nil {
- return err
- }
- fmt.Fprintf(g.context.Stdout, "Successfully unbridged guild %s", c.GuildID)
- return nil
- }
|