-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from tune-bot/user-accounts
User accounts
- Loading branch information
Showing
10 changed files
with
256 additions
and
163 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 "" | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.