Skip to content

Commit

Permalink
Merge pull request #2 from tune-bot/user-accounts
Browse files Browse the repository at this point in the history
User accounts
  • Loading branch information
cbeimers113 committed Jul 25, 2023
2 parents cc1d533 + ad0123e commit 1855ddb
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 163 deletions.
128 changes: 128 additions & 0 deletions bot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package main

import (
"fmt"
"log"
"os"
"strings"
"sync"
"time"

"github.com/bwmarrin/discordgo"

"github.com/tune-bot/database"
)

// Wrap a Discord session and other bot data
type Bot struct {
*discordgo.Session

running bool
}

// The bot singleton
var Tunebot *Bot

// A Reaction is a function and a set of args to pass to that function
// that runs after a react response occurs on a bot message
type Reaction struct {
action reactionCallback
args []string
}

// Store message IDs that are awaiting reaction response
// Messages map to an action that must be run after receiving a response
var pendingResponse = make(map[string]*Reaction)

// Initialize the bot
func init() {
var session *discordgo.Session
var err error

if session, err = discordgo.New(fmt.Sprintf("Bot %s", os.Getenv("DISCORD_TOKEN"))); err != nil {
log.Fatal(err)
return
}

Tunebot = &Bot{Session: session}
}

func (b *Bot) start() {
// Listen for message create and react events
b.Identify.Intents = discordgo.IntentsGuildMessages | discordgo.IntentsGuildMessageReactions
b.AddHandler(onChannelMessage)
b.AddHandler(onMessageReact)

// Open a connection to Discord and begin listening
err := b.Open()
if err != nil {
log.Fatal(err)
return
}
log.Println("Started Discord session")

// Open a connection to the database
if err = database.Connect(); err != nil {
log.Fatal(err)
return
}
log.Println("Connected to database")

// Wait until the bot is told to close
var wg sync.WaitGroup
wg.Add(1)
b.running = true

go func() {
defer func() {
log.Println("Ending Discord session")
database.Disconnect()
b.Close()
wg.Done()
}()

log.Printf("%s ready!\n", Title)
for b.running {
time.Sleep(1 * time.Second)
}
}()

wg.Wait()
}

func (b *Bot) stop() {
b.running = false
}

func onChannelMessage(s *discordgo.Session, m *discordgo.MessageCreate) {
// Ignore messages from Tunebot
if m.Author.ID == s.State.User.ID {
return
}

if strings.HasPrefix(m.Content, CmdPrefix) {
// Parse a command
tokens := strings.Split(m.Content[strings.LastIndex(m.Content, CmdPrefix)+1:], " ")
cmd := tokens[0]
args := tokens[1:]

if cmdCallback, ok := commands[cmd]; ok {
output, rxnCallback := cmdCallback(Tunebot, m, args)

// Send the response, store this message's ID if it requires a reaction response
if msg, err := s.ChannelMessageSend(m.ChannelID, output); err == nil && rxnCallback != nil {
reaction := &Reaction{action: rxnCallback}
reaction.args = append(reaction.args, args...)
pendingResponse[msg.ID] = reaction
}
}
}
}

func onMessageReact(s *discordgo.Session, r *discordgo.MessageReactionAdd) {
// If this message is pending a response, run the callback on this response
if rxnCallback, ok := pendingResponse[r.MessageID]; ok {
reaction := rxnCallback.action
s.ChannelMessageSend(r.ChannelID, reaction(Tunebot, r, rxnCallback.args))
}
}
100 changes: 0 additions & 100 deletions bot/bot.go

This file was deleted.

48 changes: 0 additions & 48 deletions bot/command.go

This file was deleted.

65 changes: 65 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

import (
"fmt"

"github.com/bwmarrin/discordgo"
"github.com/tune-bot/database"
)

// A command callback takes a reference to the bot singleton and
// the list of args it was passed and returns a reply to send the server
// as well as a reaction callback if the message is awaiting a response
type commandCallback func(b *Bot, m *discordgo.MessageCreate, args []string) (string, reactionCallback)

var commands = map[string]commandCallback{
"help": cmdHelp,
"kill": cmdKill,
"link": cmdLink,
}

func cmdHelp(_ *Bot, _ *discordgo.MessageCreate, _ []string) (string, reactionCallback) {
rsp := fmt.Sprintf(":notes: Hello, I'm %s!\n", Title)
rsp += fmt.Sprintf(":notes: To get started, create an account on the %s app.\n", Title)
rsp += fmt.Sprintf(":notes: Then, link your Discord account using the following command: `%slink <%s username>`\n", CmdPrefix, Title)
rsp += fmt.Sprintf("\n%s v%s\nReact to this message to browse the source code!", Title, version())
return rsp, browseSourceCallback
}

func cmdKill(b *Bot, _ *discordgo.MessageCreate, _ []string) (string, reactionCallback) {
b.stop()
return "", nil
}

func cmdLink(b *Bot, m *discordgo.MessageCreate, args []string) (string, reactionCallback) {
var err error
var rsp string

if len(args) < 1 {
rsp = fmt.Sprintf("No %s username supplied", Title)
return rsp, nil
}

discordUser := database.Discord{Name: m.Author.Username}
tunebotUser := database.User{Username: args[0]}

// Check if the Tunebot user exists before trying to link a Discord account to it
if _, err := database.FindUser(tunebotUser.Username); err != nil {
return err.Error(), nil
}

// Check if this Discord account is already linked
if tunebotUser, err = discordUser.GetUser(); err == nil {
rsp = fmt.Sprintf("Your Discord account is already linked to %s user %s", Title, tunebotUser.Username)
return rsp, nil
}

// If there isn't an account linked, GetUser will return a specific error
if err == database.ErrNoDiscordUser {
rsp = fmt.Sprintf("%s\nTo link your Discord account, react to this message", err)
return rsp, linkUserCallback
}

// Any remaining errors that could have been returned from GetUser will be reported here
return err.Error(), nil
}
16 changes: 16 additions & 0 deletions data.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package main

import "os/exec"

const (
Title = "TuneBot"
CmdPrefix = "?"
)

func version() string {
if v, err := exec.Command("git", "describe", "--tags", "--abbrev=0").Output(); err == nil {
return string(v)
}

return ""
}
6 changes: 0 additions & 6 deletions data/constants.go

This file was deleted.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.18

require (
github.com/bwmarrin/discordgo v0.27.1
github.com/tune-bot/database v1.1.3-0.20230723030957-45dccd47b9b4
github.com/tune-bot/database v1.1.8
)

require (
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/tune-bot/database v1.1.3-0.20230723030957-45dccd47b9b4 h1:sDo9HA0+RcY1rhpK/SCqAt2gA4YqXM7IESYCPapC0wQ=
github.com/tune-bot/database v1.1.3-0.20230723030957-45dccd47b9b4/go.mod h1:rcfrvSP6o/K/FJKOJFF1WxPMHTcDWc0T8pdCWOC7U+c=
github.com/tune-bot/database v1.1.8 h1:kfVl3mnLxXfCo1/DIYPDJ+uoOqt9OnSX1k0WEgy8wtk=
github.com/tune-bot/database v1.1.8/go.mod h1:G4ZHaIWI7HPxgYBa7T2SPfK6PKda3/2dCQ1X4w4E12k=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
Expand Down
Loading

0 comments on commit 1855ddb

Please sign in to comment.