Bläddra i källkod

Switch to discordgo

rigormorrtiss 4 år sedan
förälder
incheckning
f8b8b5a144
10 ändrade filer med 186 tillägg och 206 borttagningar
  1. 60 62
      discordo.go
  2. 1 1
      go.mod
  3. 16 83
      go.sum
  4. 3 3
      ui/flex.go
  5. 9 9
      ui/forms.go
  6. 3 3
      ui/inputfields.go
  7. 4 4
      ui/textviews.go
  8. 18 20
      ui/treeviews.go
  9. 2 1
      util/config.go
  10. 70 20
      util/discord.go

+ 60 - 62
discordo.go

@@ -1,16 +1,12 @@
 package main
 
 import (
-	"os"
 	"sort"
 	"strings"
 
 	"github.com/atotto/clipboard"
-	"github.com/diamondburned/arikawa/v3/api"
-	"github.com/diamondburned/arikawa/v3/discord"
-	"github.com/diamondburned/arikawa/v3/gateway"
-	"github.com/diamondburned/arikawa/v3/state"
 	"github.com/gdamore/tcell/v2"
+	"github.com/rigormorrtiss/discordgo"
 	"github.com/rigormorrtiss/discordo/ui"
 	"github.com/rigormorrtiss/discordo/util"
 	"github.com/rivo/tview"
@@ -25,9 +21,9 @@ var (
 	messageInputField *tview.InputField
 	mainFlex          *tview.Flex
 
-	conf         *util.Config
-	discordState *state.State
-	channel      *discord.Channel
+	conf    *util.Config
+	session *discordgo.Session
+	channel *discordgo.Channel
 )
 
 func main() {
@@ -66,15 +62,7 @@ func main() {
 	messageInputField = ui.NewMessageInputField(onMessageInputFieldInputCapture, conf.Theme)
 	mainFlex = ui.NewMainFlex(guildsTreeView, channelsTreeView, messagesTextView, messageInputField)
 
-	api.UserAgent = "" +
-		"Mozilla/5.0 (X11; Linux x86_64) " +
-		"AppleWebKit/537.36 (KHTML, like Gecko) " +
-		"Chrome/92.0.4515.131 Safari/537.36"
-	gateway.DefaultIdentity.Browser = "Chrome"
-	gateway.DefaultIdentity.OS = "Linux"
-	gateway.DefaultIdentity.Device = ""
-
-	token := os.Getenv("DISCORDO_TOKEN")
+	token := conf.Token
 	if t := util.GetPassword("token"); t != "" {
 		token = t
 	}
@@ -84,7 +72,12 @@ func main() {
 			SetRoot(mainFlex, true).
 			SetFocus(guildsTreeView)
 
-		discordState = newState(token)
+		session = newSession()
+		session.Token = token
+		session.Identify.Token = token
+		if err := session.Open(); err != nil {
+			panic(err)
+		}
 	} else {
 		loginForm = ui.NewLoginForm(onLoginFormLoginButtonSelected)
 		app.SetRoot(loginForm, true)
@@ -118,7 +111,7 @@ func onMessageInputFieldInputCapture(e *tcell.EventKey) *tcell.EventKey {
 			return nil
 		}
 
-		discordState.SendMessage(channel.ID, t)
+		session.ChannelMessageSend(channel.ID, t)
 		messageInputField.SetText("")
 	case tcell.KeyCtrlV:
 		text, _ := clipboard.ReadAll()
@@ -129,23 +122,30 @@ func onMessageInputFieldInputCapture(e *tcell.EventKey) *tcell.EventKey {
 	return e
 }
 
-func newState(token string) (s *state.State) {
-	var err error
-	s, err = state.New(token)
+func newSession() *discordgo.Session {
+	s, err := discordgo.New()
 	if err != nil {
 		panic(err)
 	}
 
-	s.AddHandler(onSessionReady)
+	s.UserAgent = "" +
+		"Mozilla/5.0 (X11; Linux x86_64) " +
+		"AppleWebKit/537.36 (KHTML, like Gecko) " +
+		"Chrome/92.0.4515.131 Safari/537.36"
+	s.Identify.Compress = false
+	s.Identify.Intents = 0
+	s.Identify.LargeThreshold = 0
+	s.Identify.Properties.Device = ""
+	s.Identify.Properties.Browser = "Chrome"
+	s.Identify.Properties.OS = "Linux"
+
+	s.AddHandlerOnce(onSessionReady)
 	s.AddHandler(onSessionMessageCreate)
-	if err = s.Open(s.Context()); err != nil {
-		panic(err)
-	}
 
-	return
+	return s
 }
 
-func onSessionReady(r *gateway.ReadyEvent) {
+func onSessionReady(_ *discordgo.Session, r *discordgo.Ready) {
 	rootN := guildsTreeView.GetRoot()
 	for _, g := range r.Guilds {
 		gn := tview.NewTreeNode(g.Name).
@@ -156,10 +156,9 @@ func onSessionReady(r *gateway.ReadyEvent) {
 	guildsTreeView.SetCurrentNode(rootN)
 }
 
-func onSessionMessageCreate(m *gateway.MessageCreateEvent) {
+func onSessionMessageCreate(_ *discordgo.Session, m *discordgo.MessageCreate) {
 	if channel != nil && channel.ID == m.ChannelID {
-		me, _ := discordState.Cabinet.Me()
-		util.WriteMessage(messagesTextView, me.ID, m.Message)
+		util.WriteMessage(messagesTextView, m.Message, session.State.Ready.User.ID)
 	}
 }
 
@@ -168,8 +167,9 @@ func onGuildsTreeViewSelected(gn *tview.TreeNode) {
 	messagesTextView.SetTitle("")
 	messagesTextView.Clear()
 
-	gID := gn.GetReference().(discord.GuildID)
-	cs, _ := discordState.Cabinet.Channels(gID)
+	gID := gn.GetReference().(string)
+	g, _ := session.State.Guild(gID)
+	cs := g.Channels
 	sort.Slice(cs, func(i, j int) bool {
 		return cs[i].Position < cs[j].Position
 	})
@@ -181,7 +181,7 @@ func onGuildsTreeViewSelected(gn *tview.TreeNode) {
 	// Category channels
 CategoryLoop:
 	for _, c := range cs {
-		if c.Type == discord.GuildCategory {
+		if c.Type == discordgo.ChannelTypeGuildCategory {
 			for _, child := range cs {
 				if child.ParentID == c.ID {
 					cn := tview.NewTreeNode(c.Name).
@@ -203,12 +203,12 @@ CategoryLoop:
 }
 
 func onChannelsTreeViewSelected(n *tview.TreeNode) {
-	cID := n.GetReference().(discord.ChannelID)
-	c, _ := discordState.Cabinet.Channel(cID)
+	cID := n.GetReference().(string)
+	c, _ := session.State.Channel(cID)
 	switch c.Type {
-	case discord.GuildCategory:
+	case discordgo.ChannelTypeGuildCategory:
 		n.SetExpanded(!n.IsExpanded())
-	case discord.GuildText, discord.GuildNews:
+	case discordgo.ChannelTypeGuildText, discordgo.ChannelTypeGuildNews:
 		if len(n.GetChildren()) == 0 {
 			channel = c
 			app.SetFocus(messageInputField)
@@ -224,24 +224,13 @@ func onChannelsTreeViewSelected(n *tview.TreeNode) {
 		} else {
 			n.SetExpanded(!n.IsExpanded())
 		}
-	case discord.GuildNewsThread, discord.GuildPrivateThread, discord.GuildPublicThread:
-		channel = c
-		app.SetFocus(messageInputField)
-		messagesTextView.Clear()
-		messagesTextView.SetTitle(c.Name)
-
-		go writeMessages(c.ID)
-	case discord.GuildStageVoice, discord.GuildVoice:
-		messagesTextView.Clear()
-		messagesTextView.SetTitle(c.Name)
 	}
 }
 
-func writeMessages(cID discord.ChannelID) {
-	msgs, _ := discordState.Messages(cID, conf.GetMessagesLimit)
-	me, _ := discordState.Cabinet.Me()
+func writeMessages(cID string) {
+	msgs, _ := session.ChannelMessages(cID, conf.GetMessagesLimit, "", "", "")
 	for i := len(msgs) - 1; i >= 0; i-- {
-		util.WriteMessage(messagesTextView, me.ID, msgs[i])
+		util.WriteMessage(messagesTextView, msgs[i], session.State.Ready.User.ID)
 	}
 }
 
@@ -252,29 +241,33 @@ func onLoginFormLoginButtonSelected() {
 		return
 	}
 
-	// Make a scratch HTTP client without a token
-	client := api.NewClient("")
+	session = newSession()
 	// Try to login without TOTP
-	l, err := client.Login(email, password)
+	lr, err := util.Login(session, email, password)
 	if err != nil {
 		panic(err)
 	}
 
-	if l.Token != "" && !l.MFA {
+	if lr.Token != "" && !lr.MFA {
 		app.
 			SetRoot(mainFlex, true).
 			SetFocus(guildsTreeView)
 
-		discordState = newState(l.Token)
-		go util.SetPassword("token", l.Token)
-	} else if l.MFA {
+		session.Token = lr.Token
+		session.Identify.Token = lr.Token
+		if err = session.Open(); err != nil {
+			panic(err)
+		}
+
+		go util.SetPassword("token", lr.Token)
+	} else if lr.MFA {
 		loginForm = ui.NewMfaLoginForm(func() {
 			code := loginForm.GetFormItem(0).(*tview.InputField).GetText()
 			if code == "" {
 				return
 			}
 
-			l, err := client.TOTP(code, l.Ticket)
+			lr, err = util.TOTP(session, code, lr.Ticket)
 			if err != nil {
 				panic(err)
 			}
@@ -283,8 +276,13 @@ func onLoginFormLoginButtonSelected() {
 				SetRoot(mainFlex, true).
 				SetFocus(guildsTreeView)
 
-			discordState = newState(l.Token)
-			go util.SetPassword("token", l.Token)
+			session.Token = lr.Token
+			session.Identify.Token = lr.Token
+			if err = session.Open(); err != nil {
+				panic(err)
+			}
+
+			go util.SetPassword("token", lr.Token)
 		})
 
 		app.SetRoot(loginForm, true)

+ 1 - 1
go.mod

@@ -4,8 +4,8 @@ go 1.16
 
 require (
 	github.com/atotto/clipboard v0.1.4
-	github.com/diamondburned/arikawa/v3 v3.0.0-20210817064836-eece98903ab1
 	github.com/gdamore/tcell/v2 v2.4.0
+	github.com/rigormorrtiss/discordgo v0.23.3-0.20210821171441-1f1f1a283940
 	github.com/rivo/tview v0.0.0-20210624165335-29d673af0ce2
 	github.com/zalando/go-keyring v0.1.1
 )

+ 16 - 83
go.sum

@@ -1,128 +1,61 @@
-github.com/99designs/keyring v1.1.6 h1:kVDC2uCgVwecxCk+9zoCt2uEL6dt+dfVzMvGgnVcIuM=
-github.com/99designs/keyring v1.1.6/go.mod h1:16e0ds7LGQQcT59QqkTg72Hh5ShM51Byv5PEmW6uoRU=
 github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
 github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/danieljoos/wincred v1.0.2 h1:zf4bhty2iLuwgjgpraD2E9UbvO+fe54XXGJbOwe23fU=
-github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U=
+github.com/bwmarrin/discordgo v0.23.2 h1:BzrtTktixGHIu9Tt7dEE6diysEF9HWnXeHuoJEt2fH4=
+github.com/bwmarrin/discordgo v0.23.2/go.mod h1:c1WtWUGN6nREDmzIpyTp/iD3VYt4Fpx+bVyfBG7JE+M=
 github.com/danieljoos/wincred v1.1.0 h1:3RNcEpBg4IhIChZdFRSdlQt1QjCp1sMAPIrOnm7Yf8g=
 github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=
-github.com/danieljoos/wincred v1.1.1 h1:FgOybUqUGGwgBz+ga92qD4f/ZPvuPryRjashrk/p9IA=
-github.com/danieljoos/wincred v1.1.1/go.mod h1:gSBQmTx6G0VmLowygiA7ZD0p0E09HJ68vta8z/RT2d0=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/diamondburned/arikawa/v3 v3.0.0-20210711201954-9651328b74d3 h1:pkLTxFSFSgjHd8234CuO6lgb2GOZ8m1vI1sbusx5c8Y=
-github.com/diamondburned/arikawa/v3 v3.0.0-20210711201954-9651328b74d3/go.mod h1:sNqM/iGXuH87wEH1rpQBEY1PR0AAkRKJuUhJGOdo7To=
-github.com/diamondburned/arikawa/v3 v3.0.0-20210812184548-e97dcb750c7c h1:z8ZrHZO9jZN4Iy7bXD+jzLkv0IPqoZJbrFfD4nVMUSg=
-github.com/diamondburned/arikawa/v3 v3.0.0-20210812184548-e97dcb750c7c/go.mod h1:sNqM/iGXuH87wEH1rpQBEY1PR0AAkRKJuUhJGOdo7To=
-github.com/diamondburned/arikawa/v3 v3.0.0-20210817064836-eece98903ab1 h1:G9lzJO+/R/r8Hj4h6n6eCkLe73EM39OCCEmLv5lDgSk=
-github.com/diamondburned/arikawa/v3 v3.0.0-20210817064836-eece98903ab1/go.mod h1:sNqM/iGXuH87wEH1rpQBEY1PR0AAkRKJuUhJGOdo7To=
-github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b h1:HBah4D48ypg3J7Np4N+HY/ZR76fx3HEUGxDU6Uk39oQ=
-github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
-github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM=
-github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU=
 github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
 github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
 github.com/gdamore/tcell/v2 v2.3.3/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
-github.com/gdamore/tcell/v2 v2.3.11 h1:ECO6WqHGbKZ3HrSL7bG/zArMCmLaNr5vcjjMVnLHpzc=
-github.com/gdamore/tcell/v2 v2.3.11/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
 github.com/gdamore/tcell/v2 v2.4.0 h1:W6dxJEmaxYvhICFoTY3WrLLEXsQ11SaFnKGVEXW57KM=
 github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
-github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0=
-github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
 github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
 github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
-github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
+github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU=
-github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0=
-github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM=
-github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc=
-github.com/keybase/go-keychain v0.0.0-20201121013009-976c83ec27a6 h1:Mj0fhP9dzHKPijsmli/XbXMDKe1/KWy5xKci8e3nmBg=
-github.com/keybase/go-keychain v0.0.0-20201121013009-976c83ec27a6/go.mod h1:N83iQ9rnnzi2KZuTu+0xBcD1JNWn1jSN140ggAF7HeE=
-github.com/keybase/go.dbus v0.0.0-20200324223359-a94be52c0b03/go.mod h1:a8clEhrrGV/d76/f9r2I41BwANMihfZYV9C223vaxqE=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
 github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
 github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
 github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
 github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
-github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
-github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs=
-github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rigormorrtiss/discordgo v0.23.3-0.20210821163811-db65d56a4b18 h1:D0sC2reaRk7X1bxOItwZZQYVxSPPeRKXSJ7uXZkAv2c=
+github.com/rigormorrtiss/discordgo v0.23.3-0.20210821163811-db65d56a4b18/go.mod h1:Gyt2AvlO/hpjiDzHiTBknKGgwL/dTL+BHbiNXdXE5bU=
+github.com/rigormorrtiss/discordgo v0.23.3-0.20210821170026-7f73e903f58c h1:SAkw14xJXJFTBmQVu0n5dkOzEgAhKBqIys/KZC590x8=
+github.com/rigormorrtiss/discordgo v0.23.3-0.20210821170026-7f73e903f58c/go.mod h1:Gyt2AvlO/hpjiDzHiTBknKGgwL/dTL+BHbiNXdXE5bU=
+github.com/rigormorrtiss/discordgo v0.23.3-0.20210821171441-1f1f1a283940 h1:Qg0MhvFI7Mg4Za9/Htou4y3nDaU0uABMYRys4U1WK4g=
+github.com/rigormorrtiss/discordgo v0.23.3-0.20210821171441-1f1f1a283940/go.mod h1:Gyt2AvlO/hpjiDzHiTBknKGgwL/dTL+BHbiNXdXE5bU=
 github.com/rivo/tview v0.0.0-20210624165335-29d673af0ce2 h1:I5N0WNMgPSq5NKUFspB4jMJ6n2P0ipz5FlOlB4BXviQ=
 github.com/rivo/tview v0.0.0-20210624165335-29d673af0ce2/go.mod h1:IxQujbYMAh4trWr0Dwa8jfciForjVmxyHpskZX6aydQ=
 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
-github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
-github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
-github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/zalando/go-keyring v0.1.1 h1:w2V9lcx/Uj4l+dzAf1m9s+DJ1O8ROkEHnynonHjTcYE=
 github.com/zalando/go-keyring v0.1.1/go.mod h1:OIC+OZ28XbmwFxU/Rp9V7eKzZjamBJwRzC8UFJH9+L8=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
-golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
-golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
+golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY=
 golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU=
-golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
-golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
-golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
-golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 3 - 3
ui/flex.go

@@ -5,7 +5,7 @@ import (
 )
 
 // NewMainFlex creates and returns a new main flex.
-func NewMainFlex(gTreeV *tview.TreeView, cTreeV *tview.TreeView, textV *tview.TextView, i *tview.InputField) (mf *tview.Flex) {
+func NewMainFlex(gTreeV *tview.TreeView, cTreeV *tview.TreeView, textV *tview.TextView, i *tview.InputField) *tview.Flex {
 	lf := tview.NewFlex().
 		SetDirection(tview.FlexRow).
 		AddItem(gTreeV, 0, 1, false).
@@ -14,9 +14,9 @@ func NewMainFlex(gTreeV *tview.TreeView, cTreeV *tview.TreeView, textV *tview.Te
 		SetDirection(tview.FlexRow).
 		AddItem(textV, 0, 1, false).
 		AddItem(i, 3, 1, false)
-	mf = tview.NewFlex().
+	mf := tview.NewFlex().
 		AddItem(lf, 0, 1, false).
-		AddItem(rf, 0, 5, false)
+		AddItem(rf, 0, 4, false)
 
 	return mf
 }

+ 9 - 9
ui/forms.go

@@ -5,33 +5,33 @@ import (
 	"github.com/rivo/tview"
 )
 
-func newBaseLoginForm() (f *tview.Form) {
-	f = tview.NewForm()
+func newBaseLoginForm() *tview.Form {
+	f := tview.NewForm()
 	f.
 		SetButtonsAlign(tview.AlignCenter).
 		SetButtonBackgroundColor(tcell.GetColor("#5865F2")).
 		SetBorder(true).
 		SetBorderPadding(0, 0, 1, 0)
 
-	return
+	return f
 }
 
 // NewLoginForm creates and returns a new login form.
-func NewLoginForm(onLoginFormLoginButtonSelected func()) (f *tview.Form) {
-	f = newBaseLoginForm()
+func NewLoginForm(onLoginFormLoginButtonSelected func()) *tview.Form {
+	f := newBaseLoginForm()
 	f.
 		AddInputField("Email", "", 0, nil, nil).
 		AddPasswordField("Password", "", 0, 0, nil).
 		AddButton("Login", onLoginFormLoginButtonSelected)
 
-	return
+	return f
 }
 
 // NewMfaLoginForm creates and returns a new MFA login form.
-func NewMfaLoginForm(onMfaLoginFormLoginButtonSelected func()) (f *tview.Form) {
-	f = newBaseLoginForm().
+func NewMfaLoginForm(onMfaLoginFormLoginButtonSelected func()) *tview.Form {
+	f := newBaseLoginForm().
 		AddPasswordField("Code", "", 0, 0, nil).
 		AddButton("Login", onMfaLoginFormLoginButtonSelected)
 
-	return
+	return f
 }

+ 3 - 3
ui/inputfields.go

@@ -7,8 +7,8 @@ import (
 )
 
 // NewMessageInputField creates and returns a new message inputfield.
-func NewMessageInputField(onMessageInputFieldInputCapture func(*tcell.EventKey) *tcell.EventKey, t *util.Theme) (i *tview.InputField) {
-	i = tview.NewInputField()
+func NewMessageInputField(onMessageInputFieldInputCapture func(*tcell.EventKey) *tcell.EventKey, t *util.Theme) *tview.InputField {
+	i := tview.NewInputField()
 	i.
 		SetPlaceholder("Message...").
 		SetPlaceholderTextColor(tcell.ColorWhite).
@@ -18,5 +18,5 @@ func NewMessageInputField(onMessageInputFieldInputCapture func(*tcell.EventKey)
 		SetBorder(true).
 		SetBorderPadding(0, 0, 1, 0)
 
-	return
+	return i
 }

+ 4 - 4
ui/textviews.go

@@ -5,9 +5,9 @@ import (
 )
 
 // NewMessagesTextView creates and returns a new messages textview.
-func NewMessagesTextView(app *tview.Application) (textV *tview.TextView) {
-	textV = tview.NewTextView()
-	textV.
+func NewMessagesTextView(app *tview.Application) *tview.TextView {
+	v := tview.NewTextView()
+	v.
 		SetDynamicColors(true).
 		SetWordWrap(true).
 		ScrollToEnd().
@@ -18,5 +18,5 @@ func NewMessagesTextView(app *tview.Application) (textV *tview.TextView) {
 		SetBorderPadding(0, 0, 1, 0).
 		SetTitleAlign(tview.AlignLeft)
 
-	return
+	return v
 }

+ 18 - 20
ui/treeviews.go

@@ -1,48 +1,46 @@
 package ui
 
 import (
-	"github.com/diamondburned/arikawa/v3/discord"
+	"github.com/rigormorrtiss/discordgo"
 	"github.com/rivo/tview"
 )
 
 // NewGuildsTreeView creates and returns a new guilds treeview.
-func NewGuildsTreeView(onGuildsTreeViewSelected func(*tview.TreeNode)) (treeV *tview.TreeView) {
-	treeN := tview.NewTreeNode("")
-	treeV = tview.NewTreeView()
-	treeV.
+func NewGuildsTreeView(onGuildsTreeViewSelected func(*tview.TreeNode)) *tview.TreeView {
+	v := tview.NewTreeView()
+	v.
 		SetTopLevel(1).
-		SetRoot(treeN).
+		SetRoot(tview.NewTreeNode("")).
 		SetSelectedFunc(onGuildsTreeViewSelected).
 		SetBorder(true).
 		SetBorderPadding(0, 0, 1, 0).
 		SetTitle("Guilds").
 		SetTitleAlign(tview.AlignLeft)
 
-	return
+	return v
 }
 
 // NewChannelsTreeView creates and returns a new channels treeview.
-func NewChannelsTreeView(onChannelsTreeViewSelected func(*tview.TreeNode)) (treeV *tview.TreeView) {
-	treeN := tview.NewTreeNode("")
-	treeV = tview.NewTreeView()
-	treeV.
+func NewChannelsTreeView(onChannelsTreeViewSelected func(*tview.TreeNode)) *tview.TreeView {
+	v := tview.NewTreeView()
+	v.
 		SetTopLevel(1).
-		SetRoot(treeN).
+		SetRoot(tview.NewTreeNode("")).
 		SetSelectedFunc(onChannelsTreeViewSelected).
 		SetBorder(true).
 		SetBorderPadding(0, 0, 1, 0).
 		SetTitle("Channels").
 		SetTitleAlign(tview.AlignLeft)
 
-	return
+	return v
 }
 
 // NewTextChannelTreeNode creates and returns a new text channel treenode.
-func NewTextChannelTreeNode(c discord.Channel) (n *tview.TreeNode) {
-	n = tview.NewTreeNode("[::d]#" + c.Name + "[::-]").
+func NewTextChannelTreeNode(c *discordgo.Channel) *tview.TreeNode {
+	n := tview.NewTreeNode("[::d]#" + c.Name + "[::-]").
 		SetReference(c.ID)
 
-	return
+	return n
 }
 
 // GetTreeNodeByReference gets the TreeNode that has reference r from the given treeview.
@@ -60,9 +58,9 @@ func GetTreeNodeByReference(r interface{}, treeV *tview.TreeView) (mn *tview.Tre
 }
 
 // CreateTopLevelTreeNodes creates treenodes for the top-level (orphan) channels.
-func CreateTopLevelTreeNodes(rootN *tview.TreeNode, cs []discord.Channel) {
+func CreateTopLevelTreeNodes(rootN *tview.TreeNode, cs []*discordgo.Channel) {
 	for _, c := range cs {
-		if (c.Type == discord.GuildText || c.Type == discord.GuildNews) && (c.ParentID == 0 || c.ParentID == discord.NullChannelID) {
+		if (c.Type == discordgo.ChannelTypeGuildText || c.Type == discordgo.ChannelTypeGuildNews) && (c.ParentID == "") {
 			cn := NewTextChannelTreeNode(c)
 			rootN.AddChild(cn)
 			continue
@@ -71,9 +69,9 @@ func CreateTopLevelTreeNodes(rootN *tview.TreeNode, cs []discord.Channel) {
 }
 
 // CreateSecondLevelTreeNodes creates treenodes for the second-level (category children) channels.
-func CreateSecondLevelTreeNodes(channelsTreeView *tview.TreeView, rootN *tview.TreeNode, cs []discord.Channel) {
+func CreateSecondLevelTreeNodes(channelsTreeView *tview.TreeView, rootN *tview.TreeNode, cs []*discordgo.Channel) {
 	for _, c := range cs {
-		if (c.Type == discord.GuildText || c.Type == discord.GuildNews) && (c.ParentID != 0 && c.ParentID != discord.NullChannelID) {
+		if (c.Type == discordgo.ChannelTypeGuildText || c.Type == discordgo.ChannelTypeGuildNews) && (c.ParentID != "") {
 			if pn := GetTreeNodeByReference(c.ParentID, channelsTreeView); pn != nil {
 				cn := NewTextChannelTreeNode(c)
 				pn.AddChild(cn)

+ 2 - 1
util/config.go

@@ -14,8 +14,9 @@ type Theme struct {
 
 // Config consists of fields, such as theme, mouse, so on, that may be customized by the user.
 type Config struct {
+	Token            string `json:"token,omitempty"`
 	Mouse            bool   `json:"mouse,omitempty"`
-	GetMessagesLimit uint   `json:"getMessagesLimit,omitempty"`
+	GetMessagesLimit int    `json:"getMessagesLimit,omitempty"`
 	Theme            *Theme `json:"theme,omitempty"`
 }
 

+ 70 - 20
util/discord.go

@@ -1,24 +1,22 @@
 package util
 
 import (
+	"encoding/json"
 	"fmt"
 	"strings"
 
-	"github.com/diamondburned/arikawa/v3/discord"
+	"github.com/rigormorrtiss/discordgo"
 	"github.com/rivo/tview"
 )
 
 // WriteMessage parses and writes the parsed message to the provided textview.
-func WriteMessage(v *tview.TextView, clientID discord.UserID, m discord.Message) {
+func WriteMessage(v *tview.TextView, m *discordgo.Message, clientID string) {
 	var b strings.Builder
 	switch m.Type {
-	case discord.DefaultMessage, discord.InlinedReplyMessage:
+	case discordgo.MessageTypeDefault, discordgo.MessageTypeReply:
 		parseMessage(v, &b, m, clientID)
 		fmt.Fprintln(v, b.String())
-	case discord.ThreadStarterMessage:
-		parseMessage(v, &b, *m.ReferencedMessage, clientID)
-		fmt.Fprintln(v, b.String())
-	case discord.GuildMemberJoinMessage:
+	case discordgo.MessageTypeGuildMemberJoin:
 		b.WriteString("[#5865F2]")
 		b.WriteString(m.Author.Username)
 		b.WriteString("[-]")
@@ -27,11 +25,11 @@ func WriteMessage(v *tview.TextView, clientID discord.UserID, m discord.Message)
 	}
 }
 
-func parseMessage(v *tview.TextView, b *strings.Builder, m discord.Message, clientID discord.UserID) {
+func parseMessage(v *tview.TextView, b *strings.Builder, m *discordgo.Message, clientID string) {
 	// $  ╭ AUTHOR_USERNAME (BOT) MESSAGE_CONTENT*linebreak*
-	parseReferencedMessage(b, clientID, m.ReferencedMessage)
+	parseReferencedMessage(b, m.ReferencedMessage, clientID)
 	// $ AUTHOR_USERNAME (BOT)*spacee*
-	parseAuthor(b, clientID, m.Author)
+	parseAuthor(b, m.Author, clientID)
 	// $ MESSAGE_CONTENT
 	parseContent(b, m, clientID)
 	// $ *space*(edited)
@@ -42,20 +40,20 @@ func parseMessage(v *tview.TextView, b *strings.Builder, m discord.Message, clie
 	parseAttachments(b, m.Attachments)
 }
 
-func parseContent(b *strings.Builder, m discord.Message, clientID discord.UserID) {
+func parseContent(b *strings.Builder, m *discordgo.Message, clientID string) {
 	if m.Content != "" {
 		m.Content = parseMessageMentions(m.Content, m.Mentions, clientID)
 		b.WriteString(m.Content)
 	}
 }
 
-func parseEditedTimestamp(b *strings.Builder, t discord.Timestamp) {
-	if t.IsValid() {
+func parseEditedTimestamp(b *strings.Builder, t discordgo.Timestamp) {
+	if t != "" {
 		b.WriteString(" [::d](edited)[::-]")
 	}
 }
 
-func parseMessageMentions(content string, mentions []discord.GuildUser, clientID discord.UserID) string {
+func parseMessageMentions(content string, mentions []*discordgo.User, clientID string) string {
 	for _, mUser := range mentions {
 		var color string
 		if mUser.ID == clientID {
@@ -66,10 +64,10 @@ func parseMessageMentions(content string, mentions []discord.GuildUser, clientID
 
 		content = strings.NewReplacer(
 			// <@USER_ID>
-			"<@"+mUser.ID.String()+">",
+			"<@"+mUser.ID+">",
 			color+"@"+mUser.Username+"[-:-]",
 			// <@!USER_ID>
-			"<@!"+mUser.ID.String()+">",
+			"<@!"+mUser.ID+">",
 			color+"@"+mUser.Username+"[-:-]",
 		).Replace(content)
 	}
@@ -77,13 +75,13 @@ func parseMessageMentions(content string, mentions []discord.GuildUser, clientID
 	return content
 }
 
-func parseEmbeds(b *strings.Builder, embeds []discord.Embed) {
+func parseEmbeds(b *strings.Builder, embeds []*discordgo.MessageEmbed) {
 	for range embeds {
 		b.WriteString("\n<EMBED>")
 	}
 }
 
-func parseAttachments(b *strings.Builder, attachments []discord.Attachment) {
+func parseAttachments(b *strings.Builder, attachments []*discordgo.MessageAttachment) {
 	for _, a := range attachments {
 		b.WriteString("\n[")
 		b.WriteString(a.Filename)
@@ -92,7 +90,7 @@ func parseAttachments(b *strings.Builder, attachments []discord.Attachment) {
 	}
 }
 
-func parseAuthor(b *strings.Builder, clientID discord.UserID, u discord.User) {
+func parseAuthor(b *strings.Builder, u *discordgo.User, clientID string) {
 	if u.ID == clientID {
 		b.WriteString("[#57F287]")
 	} else {
@@ -107,7 +105,7 @@ func parseAuthor(b *strings.Builder, clientID discord.UserID, u discord.User) {
 	}
 }
 
-func parseReferencedMessage(b *strings.Builder, clientID discord.UserID, rm *discord.Message) {
+func parseReferencedMessage(b *strings.Builder, rm *discordgo.Message, clientID string) {
 	if rm != nil {
 		b.WriteString(" ╭ ")
 
@@ -131,3 +129,55 @@ func parseReferencedMessage(b *strings.Builder, clientID discord.UserID, rm *dis
 		b.WriteString("[::-]\n")
 	}
 }
+
+type loginResponse struct {
+	MFA    bool   `json:"mfa"`
+	SMS    bool   `json:"sms"`
+	Ticket string `json:"ticket"`
+	Token  string `json:"token"`
+}
+
+// Login creates a new request to the `/login` endpoint for essential login information.
+func Login(s *discordgo.Session, email, password string) (*loginResponse, error) {
+	data := struct {
+		Email    string `json:"email"`
+		Password string `json:"password"`
+	}{email, password}
+	resp, err := s.RequestWithBucketID("POST", discordgo.EndpointLogin, data, discordgo.EndpointLogin)
+	if err != nil {
+		return nil, err
+	}
+
+	var lr loginResponse
+	err = json.Unmarshal(resp, &lr)
+	if err != nil {
+		return nil, err
+	}
+
+	return &lr, nil
+}
+
+// TOTP creates a new request to `/mfa/totp` endpoint for time-based one-time
+// passcode for essential login information
+func TOTP(s *discordgo.Session, code, ticket string) (*loginResponse, error) {
+	var data struct {
+		Code   string `json:"code"`
+		Ticket string `json:"ticket"`
+	}
+	data.Code = code
+	data.Ticket = ticket
+
+	endpoint := discordgo.EndpointAuth + "mfa/totp"
+	resp, err := s.RequestWithBucketID("POST", endpoint, data, endpoint)
+	if err != nil {
+		return nil, err
+	}
+
+	var lr *loginResponse
+	err = json.Unmarshal(resp, &lr)
+	if err != nil {
+		return nil, err
+	}
+
+	return lr, nil
+}