From 8b5673434a08e9c4ae810810847836c16e3321f0 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Sun, 28 Jul 2019 23:10:24 +0300 Subject: [PATCH] Add song player handler --- changelog.md | 8 +++ go.mod | 2 +- modules/discord/discord.go | 57 +++++++++++++++++++ modules/discord/polkahandler.go | 92 +++++++++++++++++++++++++++++++ modules/discord/subdayhandlers.go | 3 +- modules/settings/settings.go | 34 +++++++----- songs/.gitkeep | 0 7 files changed, 179 insertions(+), 17 deletions(-) create mode 100644 modules/discord/polkahandler.go create mode 100644 songs/.gitkeep diff --git a/changelog.md b/changelog.md index 1da0e85..756a7c5 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ # Changelog +## 4.1.0 - 2019-07-28 +### Added +- Song player handler + +## 4.0.2 - 2019-07-28 +### Changed +- Info about 7th subday + ## 4.0.1 - 2019-07-22 ### Changed - Twitch chat library version from v1 to v2 diff --git a/go.mod b/go.mod index ca01272..8372b61 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ require ( github.com/gempir/go-twitch-irc/v2 v2.2.0 github.com/pkg/errors v0.8.1 github.com/stretchr/testify v1.3.0 // indirect - go.uber.org/atomic v1.3.2 // indirect + go.uber.org/atomic v1.3.2 go.uber.org/dig v1.7.0 // indirect go.uber.org/fx v1.9.0 go.uber.org/goleak v0.10.0 // indirect diff --git a/modules/discord/discord.go b/modules/discord/discord.go index 37a40e0..11e47c6 100644 --- a/modules/discord/discord.go +++ b/modules/discord/discord.go @@ -1,12 +1,17 @@ package discord import ( + "encoding/binary" "fmt" + "io" "log" + "os" "galched-bot/modules/settings" "galched-bot/modules/subday" + "go.uber.org/atomic" + "github.com/bwmarrin/discordgo" "github.com/pkg/errors" ) @@ -31,6 +36,16 @@ func New(s *settings.Settings, subday *subday.Subday) (*Discord, error) { processor.AddHandler(subdayHandler) } + polka, err := loadSong(s.PolkaPath) + if err != nil { + return nil, errors.Wrap(err, "cannot read polka song") + } + processor.AddHandler(&polkaHandler{ + polka: polka, + voiceChannel: s.DiscordVoiceChannel, + lock: atomic.NewBool(false), + }) + log.Printf("discord: added %d message handlers", len(processor.handlers)) if len(processor.handlers) > 0 { for i := range processor.handlers { @@ -45,6 +60,48 @@ func New(s *settings.Settings, subday *subday.Subday) (*Discord, error) { }, nil } +func loadSong(path string) ([][]byte, error) { + file, err := os.Open(path) + if err != nil { + return nil, errors.Wrap(err, "error opening dca file") + } + + var ( + opuslen int16 + buffer = make([][]byte, 0) + ) + + for { + // Read opus frame length from dca file. + err = binary.Read(file, binary.LittleEndian, &opuslen) + + // If this is the end of the file, just return. + if err == io.EOF || err == io.ErrUnexpectedEOF { + err := file.Close() + if err != nil { + return nil, err + } + return buffer, nil + } + + if err != nil { + return nil, errors.Wrap(err, "error reading from dca file") + } + + // Read encoded pcm from dca file. + InBuf := make([]byte, opuslen) + err = binary.Read(file, binary.LittleEndian, &InBuf) + + // Should not be any end of file errors + if err != nil { + return nil, errors.Wrap(err, "error reading from dca file") + } + + // Append encoded pcm data to the buffer. + buffer = append(buffer, InBuf) + } +} + func LogMessage(m *discordgo.MessageCreate) { log.Printf("discord: msg [%s]: %s", m.Author.Username, m.Content) } diff --git a/modules/discord/polkahandler.go b/modules/discord/polkahandler.go new file mode 100644 index 0000000..5f3dc3d --- /dev/null +++ b/modules/discord/polkahandler.go @@ -0,0 +1,92 @@ +package discord + +import ( + "log" + "time" + + "github.com/bwmarrin/discordgo" + "go.uber.org/atomic" +) + +type polkaHandler struct { + polka [][]byte + voiceChannel string + lock *atomic.Bool +} + +func (h *polkaHandler) Signature() string { + return "!song" +} + +func (h *polkaHandler) Description() string { + return "сыграть гимн галчед (только для избранных)" +} + +func (h *polkaHandler) IsValid(msg string) bool { + return msg == "!song" +} + +func (h *polkaHandler) Handle(s *discordgo.Session, m *discordgo.MessageCreate) { + if m.Author.Username != "YoMedved" && m.Author.Username != "Rummy_Quamox" && m.Author.Username != "Lidiya_owl" { + log.Printf("discord: unathorized polka message from %s", m.Author.Username) + return + } + + // Find the channel that the message came from. + c, err := s.State.Channel(m.ChannelID) + if err != nil { + // Could not find channel. + return + } + + // Find the guild for that channel. + g, err := s.State.Guild(c.GuildID) + if err != nil { + // Could not find guild. + return + } + + // Look for the message sender in that guild's current voice states. + LogMessage(m) + if h.lock.CAS(false, true) { + defer h.lock.Store(false) + err = h.playSound(s, g.ID, h.voiceChannel) + if err != nil { + log.Println("discord: error playing sound:", err) + } + time.Sleep(10 * time.Second) + return + } +} + +// playSound plays the current buffer to the provided channel. +func (h *polkaHandler) playSound(s *discordgo.Session, guildID, channelID string) (err error) { + + // Join the provided voice channel. + vc, err := s.ChannelVoiceJoin(guildID, channelID, false, true) + if err != nil { + return err + } + + // Sleep for a specified amount of time before playing the sound + time.Sleep(250 * time.Millisecond) + + // Start speaking. + vc.Speaking(true) + + // Send the buffer data. + for _, buff := range h.polka { + vc.OpusSend <- buff + } + + // Stop speaking + vc.Speaking(false) + + // Sleep for a specified amount of time before ending. + time.Sleep(250 * time.Millisecond) + + // Disconnect from the provided voice channel. + vc.Disconnect() + + return nil +} diff --git a/modules/discord/subdayhandlers.go b/modules/discord/subdayhandlers.go index 94bb89d..477cece 100644 --- a/modules/discord/subdayhandlers.go +++ b/modules/discord/subdayhandlers.go @@ -153,7 +153,8 @@ func (h *SubdayHistoryHandler) Handle(s *discordgo.Session, m *discordgo.Message "**22.12.18**: _True Crime: Streets of LA_ -> _Serious Sam 3_ -> _Kholat_\n" + "**26.01.19**: _Disney’s Aladdin_ -> _~~Gothic~~_ -> _Scrapland_ -> _Donut County_\n" + "**24.02.19**: _Tetris 99_ -> _~~Bully~~_ -> _~~GTA: Vice City~~_\n" + - "**02.06.19**: _Spec Ops: The Line_ -> _Escape from Tarkov_\n" + "**02.06.19**: _Spec Ops: The Line_ -> _Escape from Tarkov_\n" + + "**28.07.19**: _Crypt of the Necrodancer_ -> _My Friend Pedro_ -> _Ape Out_\n" SendMessage(s, m, message) } diff --git a/modules/settings/settings.go b/modules/settings/settings.go index 922afc8..b6bfa6c 100644 --- a/modules/settings/settings.go +++ b/modules/settings/settings.go @@ -6,7 +6,7 @@ import ( ) const ( - version = "4.0.1" + version = "4.1.0" twitchUser = "galchedbot" twitchIRCRoom = "galched" discordTokenPath = "./tokens/.discordtoken" @@ -22,13 +22,15 @@ const ( type ( Settings struct { - Version string - DiscordToken string - TwitchUser string - TwitchIRCRoom string - TwitchToken string - SubdayDataPath string - PermittedRoles []string + Version string + DiscordToken string + TwitchUser string + TwitchIRCRoom string + TwitchToken string + SubdayDataPath string + PermittedRoles []string + PolkaPath string + DiscordVoiceChannel string } ) @@ -43,12 +45,14 @@ func New() (*Settings, error) { } return &Settings{ - Version: version, - DiscordToken: string(discordToken), - TwitchToken: string(twitchToken), - TwitchUser: twitchUser, - TwitchIRCRoom: twitchIRCRoom, - SubdayDataPath: subdayDataPath, - PermittedRoles: []string{subRole1, subRole2, galchedRole, smorcRole}, + Version: version, + DiscordToken: string(discordToken), + TwitchToken: string(twitchToken), + TwitchUser: twitchUser, + TwitchIRCRoom: twitchIRCRoom, + SubdayDataPath: subdayDataPath, + PolkaPath: "songs/polka.dca", + DiscordVoiceChannel: "301793085522706432", + PermittedRoles: []string{subRole1, subRole2, galchedRole, smorcRole}, }, nil } diff --git a/songs/.gitkeep b/songs/.gitkeep new file mode 100644 index 0000000..e69de29