diff options
| -rw-r--r-- | commands.go | 39 | ||||
| -rw-r--r-- | config.example.json | 2 | ||||
| -rw-r--r-- | downloader/download_manager.go | 82 | ||||
| -rw-r--r-- | downloader/downloader.go | 29 | ||||
| -rw-r--r-- | thulani.go | 24 | ||||
| -rw-r--r-- | util.go | 6 | ||||
| -rw-r--r-- | wav/wav.go | 6 |
7 files changed, 114 insertions, 74 deletions
diff --git a/commands.go b/commands.go index 0157adb..428e87a 100644 --- a/commands.go +++ b/commands.go @@ -4,40 +4,35 @@ import "github.com/mammothbane/thulani-go/downloader" var cmdMap = map[string]func(*messageCtx){ "help": printHelp, - "skip": commandNotImplemented, - "pause": commandNotImplemented, - "resume": commandNotImplemented, - "sudoku": commandNotImplemented, - "die": commandNotImplemented, - "list": commandNotImplemented, - "queue": commandNotImplemented, + "skip": skip, + "pause": pause, + "resume": resume, + "sudoku": stop, + "die": stop, + "list": list, + "queue": list, } func printHelp(c *messageCtx) { c.sendMessage(help, c.Tts) } -func commandNotImplemented(c *messageCtx) { - log.Errorf("%q not implemented", c.Command) - c.sendMessage("not implemented", c.Tts) +func skip(_ *messageCtx) { + manager.PlayState <- downloader.Play } -func skip(c *messageCtx) { - log.Error("skip not implemented") +func resume(_ *messageCtx) { + manager.PlayState <- downloader.Play } -func resume(c *messageCtx) { - log.Error("skip not implemented") +func pause(_ *messageCtx) { + manager.PlayState <- downloader.Pause } -func pause(c *messageCtx) { - log.Error("skip not implemented") +func stop(_ *messageCtx) { + manager.PlayState <- downloader.Clear } -func stop(c *messageCtx) { - log.Error("skip not implemented") -} - -func list(c *messageCtx) { - log.Error("skip not implemented") +func list(_ *messageCtx) { + log.Error("list not implemented") } diff --git a/config.example.json b/config.example.json index 5387493..e4a7d79 100644 --- a/config.example.json +++ b/config.example.json @@ -2,7 +2,7 @@ "trigger": "bot", "queue_size": 5, "admin_user_id": 12345, - "op_role": "bot-op", + "op_role_id": 12345, "guild_id": 12345, "voice_channel_id": 12345, "token": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa", diff --git a/downloader/download_manager.go b/downloader/download_manager.go index c0ceecb..16054ae 100644 --- a/downloader/download_manager.go +++ b/downloader/download_manager.go @@ -12,6 +12,7 @@ const ( Clear DlMessage = iota Pause Play + Skip ) type playBundle struct { @@ -43,50 +44,61 @@ func NewManager(s *discordgo.Session, guildID string, voiceChanID string) *Downl } func (m *DownloadManager) playFromQueue() { - for dl := range m.dls { - conn, err := m.session.ChannelVoiceJoin(m.guildID, m.voiceID, false, false) - if err != nil { - log.Errorf("unable to connect to the voice channel: %q", err) - time.Sleep(1 * time.Second) - break - } + for { + select { + case <-m.PlayState: // ignore play state updates while not playing anything + case dl := <-m.dls: + conn, err := m.session.ChannelVoiceJoin(m.guildID, m.voiceID, false, false) + if err != nil { + log.Errorf("unable to connect to the voice channel: %q", err) + time.Sleep(1 * time.Second) + break + } - out, _ := dl.Start() + m.session.UpdateStatus(0, dl.info.Url.Host) + out := dl.Start() - playState := Play - conn.Speaking(true) + playState := Play + conn.Speaking(true) - cleanup := func() { - conn.Speaking(false) - conn.Disconnect() - } + cleanup := func() { + conn.Speaking(false) + conn.Disconnect() + m.session.UpdateStatus(0, "literally nothing") + } - inner: - for { - switch playState { - case Clear: - for { - select { - case <-m.PlayState: - default: - } - } - break inner - case Pause: - playState = <-m.PlayState - case Play: - select { // first check if we have a state update message coming in - case playState = <-m.PlayState: - case elem, ok := <-out: - if !ok { - break inner + inner: + for { + switch playState { + case Clear: + dl.Stop() + for { + select { + case dl := <-m.dls: + dl.Stop() + dl.Start() // this is a hacky way of ensuring all the wavs are closed and cleaned up + default: + } } + break inner + case Skip: + break inner + case Pause: + playState = <-m.PlayState + case Play: + select { // first check if we have a state update message coming in + case playState = <-m.PlayState: + case elem, ok := <-out: + if !ok { + break inner + } - conn.OpusSend <- elem + conn.OpusSend <- elem + } } } + cleanup() } - cleanup() } } diff --git a/downloader/downloader.go b/downloader/downloader.go index 6abe7f0..b7645da 100644 --- a/downloader/downloader.go +++ b/downloader/downloader.go @@ -62,13 +62,21 @@ func (d *downloader) Stop() { }) } -func (d *downloader) Start() (<-chan []byte, <-chan struct{}) { +func (d *downloader) Start() <-chan []byte { out := make(chan []byte, 1024) - done := make(chan struct{}, 1) go func() { - defer close(done) defer close(out) + select { + case <-d.done: + for wavB := range d.pb { + wavB.wav.Stop() + wavB.cleanup() + } + return + default: + } + for wavB := range d.pb { wavB.wav.Start(out) @@ -78,17 +86,22 @@ func (d *downloader) Start() (<-chan []byte, <-chan struct{}) { wavB.cleanup() case <-wavB.wav.Done: - break } } }() - return out, done + return out } func (d *downloader) schedule() { defer close(d.pb) for i := 0; ; i++ { + select { + case <-d.done: + return + default: + } + clipStart := time.Duration(i)*clipTime + d.StartTime clipEnd := time.Duration(i+1)*clipTime + d.StartTime @@ -107,7 +120,11 @@ func (d *downloader) schedule() { return } - d.pb <- wavb + select { + case d.pb <- wavb: + case <-d.done: + return + } } } @@ -68,12 +68,14 @@ func onGuildCreate(s *discordgo.Session, m *discordgo.GuildCreate) { perms := 0 + //log.Debugf("listing roles for %v", m.Name) for _, role := range m.Roles { + //log.Debugf("%q (%v)", role.Name, role.ID) for _, mRole := range member.Roles { if role.ID == mRole { perms |= role.Permissions - log.Infof("discovered role: %v (%v)", role.Name, role.ID) + log.Infof("discovered own role: %v (%v)", role.Name, role.ID) } } } @@ -125,15 +127,20 @@ func onMessage(s *discordgo.Session, m *discordgo.MessageCreate) { fn, ok := cmdMap[strings.ToLower(ctx.Command)] if ok { - authorized := false + log.Debugf("message matched a known command: %q", strings.ToLower(ctx.Command)) - for _, role := range ctx.Guild.Roles { - for _, v := range ctx.Member.Roles { - if v != role.Name { - continue - } + authorized := ctx.Author.ID == config.AdminStr() + + if !authorized { + authorMember, err := ctx.GuildMember(ctx.Guild.ID, ctx.Author.ID) + if err != nil { + log.Errorf("unable to get guild member for id %q", ctx.Author.Username) + ctx.sendMessage("who the fuck are you?", true) + return + } - if role.Name == config.OpRole { + for _, v := range authorMember.Roles { + if v == config.OpRoleStr() { authorized = true } } @@ -145,6 +152,7 @@ func onMessage(s *discordgo.Session, m *discordgo.MessageCreate) { return } + log.Debugf("user was authorized for %q. executing.", strings.ToLower(ctx.Command)) fn(ctx) return } @@ -31,7 +31,7 @@ type Config struct { Trigger string `json:"trigger"` QueueSize uint `json:"queue_size"` AdminID uint `json:"admin_id"` - OpRole string `json:"op_role"` + OpRoleID uint `json:"op_role_id"` GuildID uint `json:"guild_id"` VoiceChannelID uint `json:"voice_channel_id"` Token string `json:"token"` @@ -51,6 +51,10 @@ func (c *Config) AdminStr() string { return strconv.Itoa(int(c.AdminID)) } +func (c *Config) OpRoleStr() string { + return strconv.Itoa(int(c.OpRoleID)) +} + func handle(err error) { if err != nil { log.Fatal(err) @@ -110,8 +110,12 @@ func (w *Wav) Start(ch chan<- []byte) { select { case <-w.Done: return + default: + } + select { + case <-w.Done: + return case ch <- b: - } } |
