mirror of
https://github.com/mautrix/discord.git
synced 2025-03-14 14:15:37 +00:00
360 lines
9 KiB
Go
360 lines
9 KiB
Go
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
|
|
}
|