diff --git a/.env.example b/.env.example index 8922c3e..1886fa6 100644 --- a/.env.example +++ b/.env.example @@ -14,6 +14,9 @@ REST_ENABLED=true # Set REST API server to release mode (empty value will switch to debug mode) REST_GIN_RELEASE=true +# Hostname for REST API server, may optionally contain port e.g. "localhost:9000" +REST_HOSTNAME="localhost:9000" + # Audio frame duration (can be 20, 40, or 60 ms) # Everything above 20 will ruin sound quality DCA_FRAME_DURATION=20 diff --git a/cmd/main.go b/cmd/main.go index c0713bc..ee48cde 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -6,6 +6,7 @@ import ( "app/internal/manager" "app/internal/melodix" "app/internal/version" + "net" "os" "os/signal" "syscall" @@ -72,7 +73,7 @@ func main() { defer dg.Close() if config.RestEnabled { - startRestServer(config.RestGinRelease) + startRestServer(config.RestGinRelease, config.RestHostname) } slog.Infof("%v is now running. Press Ctrl+C to exit", version.AppName) @@ -106,7 +107,7 @@ func startBotInstances(session *discordgo.Session, guildID string) { botInstances[guildID].Melodix.Start(guildID) } -func startRestServer(isReleaseMode bool) { +func startRestServer(isReleaseMode bool, hostname string) { if isReleaseMode { gin.SetMode("release") } @@ -117,9 +118,21 @@ func startRestServer(isReleaseMode bool) { restAPI.Start(router) go func() { - port := "8080" // TODO: move out port number to .env file - slog.Infof("REST API server started on port %v\n", port) - if err := router.Run(":" + port); err != nil { + // parse hostname var - if it has port - use it or fallback to 8080 + host, port, err := net.SplitHostPort(hostname) + if err != nil { + // If there's an error, assume the entire input is the host (without port) + host = hostname + port = "8080" + } + + // If hostname is empty, set it to the default port (8080) + if host == "" { + host = "localhost" + } + + slog.Infof("REST API server started on %s:%s\n", host, port) + if err := router.Run(net.JoinHostPort(host, port)); err != nil { slog.Fatalf("Error starting REST API server: %v", err) } }() diff --git a/docker/.env b/docker/.env index 956e02c..c466476 100644 --- a/docker/.env +++ b/docker/.env @@ -29,6 +29,9 @@ REST_ENABLED=true # Set REST API server to release mode (empty value will switch to debug mode) REST_GIN_RELEASE=true +# Hostname for REST API server, may optionally contain port e.g. "localhost:9000" +REST_HOSTNAME=${HOST} + # Audio frame duration (can be 20, 40, or 60 ms) # Everything above 20 will ruin sound quality DCA_FRAME_DURATION=20 diff --git a/internal/config/config.go b/internal/config/config.go index 8136989..ac48ef8 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -16,6 +16,7 @@ type Config struct { DiscordBotToken string RestEnabled bool RestGinRelease bool + RestHostname string DcaFrameDuration int DcaBitrate int DcaPacketLoss int @@ -48,6 +49,7 @@ func NewConfig() (*Config, error) { DiscordBotToken: os.Getenv("DISCORD_BOT_TOKEN"), RestEnabled: getenvAsBool("REST_ENABLED"), RestGinRelease: getenvAsBool("REST_GIN_RELEASE"), + RestHostname: os.Getenv("REST_HOSTNAME"), DcaFrameDuration: getenvAsInt("DCA_FRAME_DURATION"), DcaBitrate: getenvAsInt("DCA_BITRATE"), DcaPacketLoss: getenvAsInt("DCA_PACKET_LOSS"), @@ -76,6 +78,7 @@ func (c *Config) String() string { "DiscordBotToken": c.DiscordBotToken, "RestEnabled": c.RestEnabled, "RestGinRelease": c.RestGinRelease, + "RestHostname": c.RestHostname, "DcaFrameDuration": c.DcaFrameDuration, "DcaBitrate": c.DcaBitrate, "DcaPacketLoss": c.DcaPacketLoss, @@ -110,6 +113,7 @@ func validateMandatoryConfig() error { // Extra overlay to ensure we have all necessary values even if they have default values (e.g. ffmpeg has own) // ignore: // - REST_GIN_RELEASE + // - REST_HOSTNAME // - DCA_FFMPEG_BINARY_PATH mandatoryKeys := []string{ diff --git a/internal/melodix/discord.go b/internal/melodix/discord.go index 5cc3a8d..eca31f3 100644 --- a/internal/melodix/discord.go +++ b/internal/melodix/discord.go @@ -493,9 +493,17 @@ func (d *Discord) handleHistoryCommand(s *discordgo.Session, m *discordgo.Messag func (d *Discord) handleAboutCommand(s *discordgo.Session, m *discordgo.MessageCreate) { d.changeAvatar(s) - content := version.AppName + " is a simple music bot that allows you to play music in voice channels on a Discord server." + config, err := config.NewConfig() + if err != nil { + slog.Fatalf("Error loading config: %v", err) + } + + avatarUrl := inferProtocolByPort(config.RestHostname, 443) + config.RestHostname + "/avatar/random?" + fmt.Sprint(time.Now().UnixNano()) + + title := getRandomAboutTitlePhrase() + content := getRandomAboutDescriptionPhrase() - embedStr := fmt.Sprintf("๐Ÿ“ป **About %v**\n\n%v", version.AppName, content) + embedStr := fmt.Sprintf("**%v**\n\n%v", title, content) embedMsg := embed.NewEmbed(). SetDescription(embedStr). @@ -503,7 +511,7 @@ func (d *Discord) handleAboutCommand(s *discordgo.Session, m *discordgo.MessageC AddField("```"+version.GoVersion+"```", "Go version"). AddField("```Created by Innokentiy Sokolov```", "[Linkedin](https://www.linkedin.com/in/keshon), [GitHub](https://github.com/keshon), [Homepage](https://keshon.ru)"). InlineAllFields(). - SetImage("https://melodix-bot.keshon.ru/avatar/random"). // TODO: move out to config .env file + SetImage(avatarUrl). SetColor(0x9f00d4).SetFooter(version.AppFullName + " <" + d.Player.GetCurrentStatus().String() + ">").MessageEmbed s.ChannelMessageSendEmbed(m.Message.ChannelID, embedMsg) diff --git a/internal/melodix/utility.go b/internal/melodix/utility.go index 1dfebad..aef97a6 100644 --- a/internal/melodix/utility.go +++ b/internal/melodix/utility.go @@ -5,6 +5,7 @@ import ( "fmt" "math" "math/rand" + "net" "net/http" "net/url" "os" @@ -257,3 +258,72 @@ func getRandomWaitPhrase() string { return phrases[index] } + +func getRandomAboutTitlePhrase() string { + phrases := []string{ + "Hello there!", + "Who do we have here?", + "Brace yourselves for Melodix!", + "Get ready to laugh and groove!", + "Peek behind the musical curtain!", + "Unleashing Melodix magic!", + "Prepare for some bot banter!", + "It's showtime with Melodix!", + "Allow me to introduce myself", + "Heeeey amigos!", + "Unleashing Melodix magic!", + "Did someone order beats?", + "Well, look who's curious!", + } + + index := rand.Intn(len(phrases)) + + return phrases[index] +} + +func getRandomAboutDescriptionPhrase() string { + phrases := []string{ + "๐ŸŽถ The Discord DJ That Won't Take Requests From Your In-Laws! ๐Ÿ”Š Crank up the tunes and drown out the chaos. No commercials, no cover chargesโ€”just pure, unfiltered beats. Because when life hands you a mic, you drop it with Melodix! ๐ŸŽค๐ŸŽ‰ #MelodixMadness #NoRequestsAllowed", + "๐ŸŽต Groovy Bot: Where Beats Meet Banter! ๐Ÿค– Tune in for the ultimate audio fiesta. Tracks that hit harder than Monday mornings and a vibe that won't quit. Request, rewind, and revel in the groove. Life's a party; let's make it legendary! ๐Ÿš€๐Ÿ•บ #GroovyBot #UnleashTheBeats", + "Melodix: Unleash the Epic Beats! ๐Ÿš€๐ŸŽต Your Discord, Your Soundtrackโ€”Elevate your server experience with the ultimate music companion. No boundaries, just epicness! Turn up the volume and let Melodix redefine your sonic adventure. ๐ŸŽง๐Ÿ”ฅ #EpicBeats #MelodixUnleashed", + "๐Ÿค– Welcome to the Groovy Bot Experience! ๐ŸŽถ Unleash the musical mayhem with a sprinkle of humor. I'm your DJ, serving beats hotter than a summer grill. ๐Ÿ”ฅ Request a jam, peek into your play history, and let's dance like nobody's watching. It's music with a side of laughter โ€“ because why not? Let the groove take the wheel! ๐Ÿ•บ๐ŸŽ‰ #BotLife #DanceTillYouDrop", + "๐ŸŽถ Melodix: Your Personal Discord DJ! ๐Ÿ”Š I spin tunes better than your grandma spins knitting yarn. No song requests? No problem! I play what I want, when I want. Get ready for a musical rollercoaster, minus the safety harness! ๐ŸŽข๐ŸŽค #MelodixMagic #GrandmaApproved", + "๐ŸŽต Melodix: The Bot with the Moves! ๐Ÿ•บ Break out your best dance moves because I'm dropping beats that even the neighbors can't resist. Turn up the volume, lock the door, and dance like nobody's watchingโ€”except me, of course! ๐Ÿ’ƒ๐ŸŽ‰ #DanceFloorOnDiscord #BeatDropper", + "Melodix: Where Music Meets Mischief! ๐Ÿค–๐ŸŽถ Your server's audio adventure begins here. I play music that hits harder than your morning alarm and cracks more jokes than your favorite stand-up comedian. Buckle up; it's gonna be a hilarious ride! ๐Ÿš€๐Ÿ˜‚ #MusicMischief #JokesterBot", + "๐Ÿค– Meet Melodix: The Discord DJ on a Comedy Tour! ๐ŸŽค Unleash the laughter and the beats with a bot that's funnier than your uncle's dad jokes. Request a track, sit back, and enjoy the show. Warning: I may cause uncontrollable fits of joy! ๐Ÿ˜†๐ŸŽต #ComedyTourBot #LaughOutLoud", + "๐ŸŽง Melodix: Beats that Hit Harder Than Life's Problems! ๐Ÿ’ฅ When reality knocks, I turn up the volume. Melodix delivers beats that punch harder than Monday mornings and leave you wondering why life isn't always this epic. Buckle up; it's time to conquer the airwaves! ๐Ÿš€๐ŸŽถ #EpicBeats #LifePuncher", + "๐Ÿ”Š Groovy Bot: Making Discord Groovy Again! ๐Ÿ•บ Shake off the stress, kick back, and let Groovy Bot do the heavy lifting. My beats are so groovy; even your grandma would break into the moonwalk. Get ready to rediscover your groove on Discord! ๐ŸŒ™๐Ÿ’ซ #GroovyAgain #DiscordDanceRevolution", + "๐Ÿš€ Melodix: Your Gateway to Musical Awesomeness! ๐ŸŒŸ I'm not just a bot; I'm your VIP pass to a sonic wonderland. No queues, no limitsโ€”just pure, unadulterated musical awesomeness. Fasten your seatbelts; the journey to epic sounds begins now! ๐ŸŽธ๐ŸŽ‰ #MusicalAwesomeness #VIPPass", + "๐ŸŽถ Melodix: More Than Just a Botโ€”It's a Vibe! ๐Ÿค–๐Ÿ•ถ๏ธ Elevate your server with vibes so cool, even penguins envy me. I'm not your average bot; I'm a mood-altering, vibe-creating, beat-dropping phenomenon. Prepare for a vibe check, Melodix style! ๐ŸŒŠ๐ŸŽต #VibeMaster #BotGoals", + "๐Ÿ”Š Step into Melodix's Audio Playground! ๐ŸŽ‰ Your ticket to the ultimate sonic adventure is here. With beats that rival a theme park ride and humor sharper than a stand-up special, Melodix is your all-access pass to the audio amusement park. Let the fun begin! ๐ŸŽข๐ŸŽค #AudioPlayground #RollercoasterBeats", + "๐ŸŽต Melodix: Where Discord Gets Its Groove On! ๐Ÿ’ƒ I'm not just a bot; I'm the rhythm that keeps your server dancing. My beats are so infectious; even the toughest critics tap their feet. Get ready to groove; Melodix is in the house! ๐Ÿ•บ๐ŸŽถ #DiscordGrooveMaster #BeatCommander", + "๐Ÿš€ Unleash Melodix: The Bot with a Sonic Punch! ๐Ÿ’ฅ Dive into a world where beats hit harder than a superhero landing. Melodix isn't just a bot; I'm a powerhouse of sonic awesomeness. Get ready for an audio experience that packs a punch! ๐ŸŽค๐Ÿ‘Š #SonicPowerhouse #BeatHero", + "๐Ÿ”Š Melodix: Your Server's Audio Magician! ๐ŸŽฉโœจ Watch as I turn ordinary moments into extraordinary memories with a wave of my musical wand. Beats appear, laughter ensues, and your server becomes the stage for an epic audio performance. Prepare to be enchanted! ๐ŸŽถ๐Ÿ”ฎ #AudioMagician #DiscordWizard", + "๐ŸŽง Melodix: Beats That Speak Louder Than Words! ๐Ÿ“ข When words fail, music speaks. I deliver beats so powerful; even a whisper could start a party. Say goodbye to silence; it's time to let the music do the talking. Turn it up; let's break the sound barrier! ๐Ÿš€๐ŸŽต #BeatsNotWords #MusicSpeaksVolumes", + "๐Ÿค– Melodix: The Bot That Takes the Stage! ๐ŸŽค Roll out the red carpet; Melodix is here to steal the show. My beats command attention, and my humor steals the spotlight. It's not just music; it's a performance. Get ready for a standing ovation! ๐Ÿ‘๐ŸŽถ #StageStealer #BotOnTheMic", + "๐ŸŽต Groovy Bot: Turning Discord into a Dance Floor! ๐Ÿ’ƒ I'm not just a bot; I'm the DJ that turns your server into a non-stop dance party. Groovy Bot's beats are so infectious; even the furniture wants to boogie. Get ready to dance like nobody's watching! ๐ŸŽ‰๐ŸŽถ #DancePartyBot #BoogieMaster", + "๐Ÿš€ Melodix: Your Sonic Co-Pilot on the Discord Journey! ๐ŸŽถ Buckle up; we're about to take off on a musical adventure. Melodix isn't just a bot; I'm your co-pilot navigating the airspace of epic beats. Fasten your seatbelts; the journey awaits! โœˆ๏ธ๐Ÿ”Š #SonicCoPilot #DiscordAdventure", + "๐Ÿ”Š Melodix: Bringing the Beats, Igniting the Vibes! ๐Ÿ”ฅ I'm not just a bot; I'm the ignition switch for a server-wide party. My beats are so fire; even the speakers need a cooldown. Prepare for a musical blaze that'll leave you in awe! ๐ŸŽต๐ŸŽ‰ #IgniteTheVibes #DiscordInferno", + "๐ŸŽถ Melodix: Turning Mundane into Musical! ๐ŸŒŸ Say goodbye to the ordinary; Melodix is here to transform the mundane into a symphony of epic proportions. My beats are the soundtrack to your server's extraordinary journey. Let's make every moment musical! ๐ŸŽค๐Ÿš€ #MusicalTransformation #EpicSymphony", + "๐Ÿค– Melodix: The Bot That Doesn't Miss a Beatโ€”Literally! ๐Ÿฅ Precision beats, flawless execution, and humor that lands every time. Melodix is the maestro of your server's audio orchestra. No missed beats, no dull momentsโ€”just pure musical perfection! ๐ŸŽถ๐Ÿ‘Œ #NoMissedBeats #AudioMaestro", + "๐ŸŽต Groovy Bot: Where Discord Finds Its Rhythm! ๐Ÿ•บ We're not just a bot; we're the rhythm that keeps your server in sync. Groovy Bot's beats are so contagious; even the skeptics catch the vibe. Get ready for a rhythmic revolution on Discord! ๐ŸŽถ๐Ÿ”„ #RhythmicRevolution #DiscordSyncMaster", + "๐Ÿš€ Melodix: Elevate Your Discord, Elevate Your Beats! ๐ŸŽง We're not just a bot; we're the elevator to the next level of sonic greatness. Melodix's beats are the soundtrack to your server's ascension. Get ready to elevate your vibes to new heights! ๐ŸŒŒ๐Ÿ”Š #ElevateYourBeats #DiscordAscent", + } + + index := rand.Intn(len(phrases)) + + return phrases[index] +} + +// inferProtocolByPort attempts to infer the protocol based on the availability of a specific port. +func inferProtocolByPort(hostname string, port int) string { + conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", hostname, port)) + if err != nil { + // Assuming it's not available, use HTTP + return "http://" + } + defer conn.Close() + + // The port is available, use HTTPS + return "https://" +}