package discord import ( "context" "fmt" "sync" "time" "gitea.dwysokinski.me/twhelp/dcbot/internal/domain" "github.com/bwmarrin/discordgo" "go.uber.org/zap" ) const ( executeMonitorsJobTimeout = 2 * time.Minute colorGreen = 0x00ff00 colorRed = 0xff0000 ) type executeMonitorsJob struct { s *discordgo.Session svc MonitorService logger *zap.Logger } func (e *executeMonitorsJob) Run() { ctx, cancel := context.WithTimeout(context.Background(), executeMonitorsJobTimeout) defer cancel() start := time.Now() notifications, err := e.svc.Execute(ctx) if err != nil { e.logger.Error("something went wrong while executing monitors", zap.Error(err)) return } var wg sync.WaitGroup for ch, embeds := range ennoblementNotificationsToEmbeds(notifications) { wg.Add(1) go func(ch string, embeds []*discordgo.MessageEmbed) { defer wg.Done() for _, embed := range embeds { _, _ = e.s.ChannelMessageSendEmbed(ch, embed) } }(ch, embeds) } wg.Wait() e.logger.Info( "notifications sent", zap.Int("notifications", len(notifications)), zap.Duration("duration", time.Since(start)), ) } func ennoblementNotificationsToEmbeds(ns []domain.EnnoblementNotification) map[string][]*discordgo.MessageEmbed { m := make(map[string]map[domain.EnnoblementNotificationType][]*discordgo.MessageEmbed) timestamp := formatTimestamp(time.Now()) for _, n := range ns { if _, ok := m[n.ChannelID]; !ok { m[n.ChannelID] = make(map[domain.EnnoblementNotificationType][]*discordgo.MessageEmbed) } embeds := m[n.ChannelID][n.Type] str := buildEnnoblementNotificationDescription(n) if l := len(embeds); l == 0 || len(embeds[l-1].Description)+len(str) > embedDescriptionCharLimit { embeds = append(embeds, &discordgo.MessageEmbed{ Color: ennoblementNotificationTypeToColor(n.Type), Type: discordgo.EmbedTypeRich, Timestamp: timestamp, }) } if len(embeds[len(embeds)-1].Description) > 0 { str = "\n" + str } embeds[len(embeds)-1].Description += str m[n.ChannelID][n.Type] = embeds } res := make(map[string][]*discordgo.MessageEmbed, len(m)) for ch, innerMap := range m { cnt := 0 for _, embeds := range innerMap { cnt += len(embeds) } res[ch] = make([]*discordgo.MessageEmbed, 0, cnt) for _, embeds := range innerMap { res[ch] = append(res[ch], embeds...) } } return res } func buildEnnoblementNotificationDescription(n domain.EnnoblementNotification) string { return fmt.Sprintf( "%s has taken %s (Old owner: %s)", nullPlayerMetaToMarkdown(n.Ennoblement.NewOwner), buildLink(n.Ennoblement.Village.FullName, n.Ennoblement.Village.ProfileURL), nullPlayerMetaToMarkdown(n.Ennoblement.OldOwner), ) } func nullPlayerMetaToMarkdown(p domain.NullPlayerMeta) string { if !p.Valid { return "Barbarians" } tag := p.Player.Tribe.Tribe.Tag if tag == "" { tag = "-" } return buildLink(p.Player.Name, p.Player.ProfileURL) + " [" + buildLink(tag, p.Player.Tribe.Tribe.ProfileURL) + "]" } func ennoblementNotificationTypeToColor(t domain.EnnoblementNotificationType) int { switch t { case domain.EnnoblementNotificationTypeGain: return colorGreen case domain.EnnoblementNotificationTypeLoss: fallthrough default: return colorRed } }