From 60c260a4713743ce56820b68aaadad9863c19792 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 27 Feb 2023 00:43:11 +0200 Subject: [PATCH] Add initial support for bot accounts. Fixes #12 --- commands.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++--- user.go | 23 ++++++++++++++++++++- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/commands.go b/commands.go index 4896d4e..2d3aa7e 100644 --- a/commands.go +++ b/commands.go @@ -19,6 +19,7 @@ package main import ( "bytes" "context" + "encoding/base64" "errors" "fmt" "html" @@ -80,12 +81,45 @@ var cmdLoginToken = &commands.FullHandler{ Help: commands.HelpMeta{ Section: commands.HelpSectionAuth, Description: "Link the bridge to your Discord account by extracting the access token manually.", + Args: " <_token_>", }, } +const discordTokenEpoch = 1293840000 + +func decodeToken(token string) (userID int64, err error) { + parts := strings.Split(token, ".") + if len(parts) != 3 { + err = fmt.Errorf("invalid number of parts in token") + return + } + var userIDStr []byte + userIDStr, err = base64.RawURLEncoding.DecodeString(parts[0]) + if err != nil { + err = fmt.Errorf("invalid base64 in user ID part: %w", err) + return + } + _, err = base64.RawURLEncoding.DecodeString(parts[1]) + if err != nil { + err = fmt.Errorf("invalid base64 in random part: %w", err) + return + } + _, err = base64.RawURLEncoding.DecodeString(parts[2]) + if err != nil { + err = fmt.Errorf("invalid base64 in checksum part: %w", err) + return + } + userID, err = strconv.ParseInt(string(userIDStr), 10, 64) + if err != nil { + err = fmt.Errorf("invalid number in decoded user ID part: %w", err) + return + } + return +} + func fnLoginToken(ce *WrappedCommandEvent) { - if len(ce.Args) == 0 { - ce.Reply("**Usage**: `$cmdprefix login-token `") + if len(ce.Args) != 2 { + ce.Reply("**Usage**: `$cmdprefix login-token `") return } ce.MarkRead() @@ -94,7 +128,25 @@ func fnLoginToken(ce *WrappedCommandEvent) { ce.Reply("You're already logged in") return } - if err := ce.User.Login(ce.Args[0]); err != nil { + token := ce.Args[1] + userID, err := decodeToken(token) + if err != nil { + ce.Reply("Invalid token") + return + } + switch strings.ToLower(ce.Args[0]) { + case "user": + // Token is used as-is + case "bot": + token = "Bot " + token + case "oauth": + token = "Bearer " + token + default: + ce.Reply("Token type must be `user`, `bot` or `oauth`") + return + } + ce.Reply("Connecting to Discord as user ID %d", userID) + if err = ce.User.Login(token); err != nil { ce.Reply("Error connecting to Discord: %v", err) return } diff --git a/user.go b/user.go index eee7498..38edbe6 100644 --- a/user.go +++ b/user.go @@ -487,6 +487,24 @@ func (user *User) Connected() bool { return user.Session != nil } +const BotIntents = discordgo.IntentGuilds | + discordgo.IntentGuildMessages | + discordgo.IntentGuildMessageReactions | + discordgo.IntentGuildMessageTyping | + discordgo.IntentGuildBans | + discordgo.IntentGuildEmojis | + discordgo.IntentGuildIntegrations | + discordgo.IntentGuildInvites | + //discordgo.IntentGuildVoiceStates | + //discordgo.IntentGuildScheduledEvents | + discordgo.IntentDirectMessages | + discordgo.IntentDirectMessageTyping | + discordgo.IntentDirectMessageTyping | + // Privileged intents + discordgo.IntentMessageContent | + //discordgo.IntentGuildPresences | + discordgo.IntentGuildMembers + func (user *User) Connect() error { user.Lock() defer user.Unlock() @@ -505,6 +523,9 @@ func (user *User) Connect() error { if os.Getenv("DISCORD_DEBUG") == "1" { session.LogLevel = discordgo.LogDebug } + if !session.IsUser { + session.Identify.Intents = BotIntents + } user.Session = session @@ -593,7 +614,7 @@ func (user *User) readyHandler(_ *discordgo.Session, r *discordgo.Ready) { } user.PrunePortalList(updateTS) - if r.ReadState.Version > user.ReadStateVersion { + if r.ReadState != nil && r.ReadState.Version > user.ReadStateVersion { // TODO can we figure out which read states are actually new? for _, entry := range r.ReadState.Entries { user.messageAckHandler(nil, &discordgo.MessageAck{