package discord import ( "fmt" "time" "github.com/bwmarrin/discordgo" "github.com/ente-io/museum/pkg/repo" t "github.com/ente-io/museum/pkg/utils/time" log "github.com/sirupsen/logrus" "github.com/spf13/viper" ) // DiscordController is an devops aid. If Discord credentials are configured, // then it will send notifications to Discord channels on specified events. type DiscordController struct { MonaLisa *discordgo.Session ChaChing *discordgo.Session HostName string Environment string UserRepo *repo.UserRepository } func NewDiscordController(userRepo *repo.UserRepository, hostName string, environment string) *DiscordController { return &DiscordController{ MonaLisa: createBot("Mona Lisa", "discord.bot.mona-lisa.token"), ChaChing: createBot("Cha Ching", "discord.bot.cha-ching.token"), HostName: hostName, Environment: environment, UserRepo: userRepo, } } func createBot(name string, tokenConfigKey string) *discordgo.Session { silent := viper.GetBool("internal.silent") if silent { return nil } token := viper.GetString(tokenConfigKey) if token == "" { return nil } session, err := discordgo.New("Bot " + token) if err != nil { log.Warnf("Could not create Discord bot %s: %s", name, err) } return session } // The actual send func (c *DiscordController) sendMessage(bot *discordgo.Session, channel string, message string) { if bot == nil { log.Infof("Skipping sending Discord message: %s", message) return } _, err := bot.ChannelMessageSend(channel, message) if err != nil { log.Warnf("Could not send message {%s} to Discord channel {%s} due to error {%s}", message, channel, err) } } // Send a message related to server status or important events/errors. func (c *DiscordController) Notify(message string) { c.sendMessage(c.MonaLisa, viper.GetString("discord.bot.mona-lisa.channel"), message) } // Send a message related to subscriptions. func (c *DiscordController) NotifyNewSub(userID int64, paymentProvider string, amount string) { message := fmt.Sprintf("New subscriber via `%s`, after %s of signing up! 🫂 (%s)", paymentProvider, c.getTimeSinceSignUp(userID), amount) c.sendMessage(c.ChaChing, viper.GetString("discord.bot.cha-ching.channel"), message) } // Send a message related to subscriptions. func (c *DiscordController) NotifyBlackFridayUser(userID int64, amount string) { message := fmt.Sprintf("BlackFriday subscription purchased after %s of signing up! 🫂 (%s)", c.getTimeSinceSignUp(userID), amount) c.sendMessage(c.ChaChing, viper.GetString("discord.bot.cha-ching.channel"), message) } // Convenience wrappers over the primitive notify types. // // By keeping them separate we later allow them to be routed easily to different // Discord channels. func (c *DiscordController) NotifyStartup() { c.Notify(c.HostName + " has taken off 🚀") } func (c *DiscordController) NotifyShutdown() { c.Notify(c.HostName + " is down ☠️") } func (c *DiscordController) NotifyAdminAction(message string) { c.Notify(message) } func (c *DiscordController) NotifyAccountDelete(userID int64, paymentProvider string, productID string) { message := fmt.Sprintf("User on %s (%s) initiated delete after using us for %s", paymentProvider, productID, c.getTimeSinceSignUp(userID)) c.Notify(message) } func (c *DiscordController) NotifyPotentialAbuse(message string) { c.Notify(fmt.Sprintf("%s: %s", c.HostName, message)) } func (c *DiscordController) getTimeSinceSignUp(userID int64) string { timeSinceSignUp := "unknown time" user, err := c.UserRepo.GetUserByIDInternal(userID) if err != nil { log.Error(err) } else { since := time.Since(time.UnixMicro(user.CreationTime)) timeSinceSignUp = t.DaysOrHoursOrMinutes(since) } return timeSinceSignUp }