Skip to content

Commit 8418f0d

Browse files
authored
Merge pull request #16 from cake4everyone/mutli-server-announcements
Make bot ready to allow independent announcements in multiple servers
2 parents cb893eb + 9f40963 commit 8418f0d

27 files changed

+337
-214
lines changed

config.yaml

-9
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,6 @@ discord:
2020
name: Cake4Everybot
2121
credits: Cake4Everybot, developed by @Kesuaheli (Discord) and the ideas of the community ♥
2222

23-
announce:
24-
# The channels ID's to subscribe to
25-
# Its sort of a whitelist. Each channel in this list is allowed to send
26-
# announcement events through the bot
27-
youtube:
28-
- UC6sb0bkXREewXp2AkSOsOqg # Taomi
29-
twitch:
30-
- "404257324" # Taomi_
31-
3223
event:
3324
# Time (24h format) to trigger daily events like birthday check and advent calendar post
3425
morning_hour: 8

data/lang/de.yaml

-2
Original file line numberDiff line numberDiff line change
@@ -222,11 +222,9 @@ module:
222222
twitch:
223223
embed_category: Kategorie
224224
embed_footer: Twitch Glocke
225-
msg.nofification: "%s ist auf Twitch live gegangen!"
226225

227226
youtube:
228227
embed_footer: YouTube Glocke
229-
msg.new_vid: "%s hat ein neues Video hochgeladen"
230228

231229
twitch.command:
232230
generic:

data/lang/en.yaml

-2
Original file line numberDiff line numberDiff line change
@@ -222,11 +222,9 @@ module:
222222
twitch:
223223
embed_category: Category
224224
embed_footer: Twitch notification bell
225-
msg.nofification: "%s went live on Twitch!"
226225

227226
youtube:
228227
embed_footer: YouTube notification bell
229-
msg.new_vid: "%s just uploaded a new video"
230228

231229
twitch.command:
232230
generic:

database/announcements.go

+34-11
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ package database
44
//
55
// It can be obtained by GetAnnouncement for a given channel on a platform.
66
type Announcement struct {
7-
GuildID string
8-
ChannelID string
9-
MessageID string
10-
RoleID string
11-
Platform Platform
12-
PlatformID string
7+
GuildID string
8+
ChannelID string
9+
MessageID string
10+
RoleID string
11+
Platform Platform
12+
PlatformID string
13+
Notification string
1314
}
1415

1516
// Platform is the type of platform a Announcement can be made
@@ -50,34 +51,56 @@ func (p Platform) GoString() string {
5051
}
5152
}
5253

54+
// GetAllAnnouncementIDs returns all platform IDs for a given platform.
55+
//
56+
// If no result matches the given platform the returned error will be sql.ErrNoRows.
57+
// Other errors may exist.
58+
func GetAllAnnouncementIDs(platform Platform) (platformIDs []string, err error) {
59+
rows, err := Query("SELECT DISTINCT platform_id FROM announcements WHERE platform=?", platform)
60+
if err != nil {
61+
return nil, err
62+
}
63+
defer rows.Close()
64+
65+
platformIDs = make([]string, 0)
66+
for rows.Next() {
67+
var platformID string
68+
if err := rows.Scan(&platformID); err != nil {
69+
return nil, err
70+
}
71+
platformIDs = append(platformIDs, platformID)
72+
}
73+
return platformIDs, nil
74+
}
75+
5376
// GetAnnouncement reads all Discord announcement channels from the database for a given channel ID
5477
// on a platform.
5578
// A platform could be "twitch" or "youtube".
5679
//
5780
// If no result matches the given platform and channel ID the returned error will be sql.ErrNoRows.
5881
// Other errors may exist.
5982
func GetAnnouncement(platform Platform, platformID string) ([]*Announcement, error) {
60-
rows, err := Query("SELECT guild_id,channel_id,message_id,role_id FROM announcements WHERE platform=? AND platform_id=?", platform, platformID)
83+
rows, err := Query("SELECT guild_id,channel_id,message_id,role_id,notification FROM announcements WHERE platform=? AND platform_id=?", platform, platformID)
6184
if err != nil {
6285
return []*Announcement{}, err
6386
}
6487
defer rows.Close()
6588
announcements := make([]*Announcement, 0)
6689
for rows.Next() {
67-
var guildID, channelID, messageID, roleID string
90+
var guildID, channelID, messageID, roleID, notification string
6891
if err := rows.Scan(&guildID, &channelID, &messageID, &roleID); err != nil {
6992
return []*Announcement{}, err
7093
}
71-
announcements = append(announcements, &Announcement{guildID, channelID, messageID, roleID, platform, platformID})
94+
announcements = append(announcements, &Announcement{guildID, channelID, messageID, roleID, platform, platformID, notification})
7295
}
7396
return announcements, err
7497
}
7598

7699
// UpdateAnnouncementMessage updates the message id of a with newID.
77100
func (a *Announcement) UpdateAnnouncementMessage(newID string) error {
78-
_, err := Exec("UPDATE announcements SET message_id=? WHERE guild_id=? AND channel_id=? AND message_id=? AND role_id=? AND platform=? AND platform_id=?",
101+
_, err := Exec("UPDATE announcements SET message_id=? WHERE guild_id=? AND channel_id=? AND message_id=? AND role_id=? AND platform=? AND platform_id=? AND notification=?",
79102
newID,
80-
a.GuildID, a.ChannelID, a.MessageID, a.RoleID, a.Platform, a.PlatformID,
103+
a.GuildID, a.ChannelID, a.MessageID, a.RoleID, a.Platform, a.PlatformID, a.Notification,
81104
)
82105
a.MessageID = newID
83106
return err

database/giveaway.go

+22-16
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ type GiveawayEntry struct {
3636
Weight int
3737
// The day of last entry. Useful to check when only one ticket per day is allowed.
3838
LastEntry time.Time
39+
// The platform the giveaway is for
40+
Platform Platform
41+
// The platform identifier e.g. the channel id for the platform
42+
PlatformID string
3943
}
4044

4145
// ToEmbedField formats the giveaway entry to an discord message embed field.
@@ -59,22 +63,22 @@ func (e GiveawayEntry) ToEmbedField(s *discordgo.Session, totalTickets int) (f *
5963
// prefixed with prefix.
6064
//
6165
// If an error occours or it doesn't match prefix, an emtpy GiveawayEntry is returned instead.
62-
func GetGiveawayEntry(prefix, userID string) GiveawayEntry {
66+
func GetGiveawayEntry(prefix, userID string, platform Platform, platformID string) GiveawayEntry {
6367
var (
6468
weight int
6569
lastEntryID string
6670
)
67-
err := QueryRow("SELECT weight,last_entry_id FROM giveaway WHERE id=?", userID).Scan(&weight, &lastEntryID)
71+
err := QueryRow("SELECT weight,last_entry_id FROM giveaway WHERE id=? AND platform=? AND platform_id=?", userID, platform, platformID).Scan(&weight, &lastEntryID)
6872
if err == sql.ErrNoRows {
69-
return GiveawayEntry{UserID: userID, Weight: 0}
73+
return GiveawayEntry{UserID: userID, Weight: 0, Platform: platform, PlatformID: platformID}
7074
}
7175
if err != nil {
7276
log.Printf("Database failed to get giveaway entries for '%s': %v", userID, err)
7377
return GiveawayEntry{}
7478
}
7579

7680
if lastEntryID == "" {
77-
return GiveawayEntry{UserID: userID, Weight: weight}
81+
return GiveawayEntry{UserID: userID, Weight: weight, Platform: platform, PlatformID: platformID}
7882
}
7983

8084
dateValue, ok := strings.CutPrefix(lastEntryID, prefix+"-")
@@ -87,16 +91,16 @@ func GetGiveawayEntry(prefix, userID string) GiveawayEntry {
8791
log.Printf("could not convert last_entry_id '%s' to time: %v", lastEntryID, err)
8892
return GiveawayEntry{}
8993
}
90-
return GiveawayEntry{userID, weight, lastEntry}
94+
return GiveawayEntry{userID, weight, lastEntry, platform, platformID}
9195
}
9296

9397
// DeleteGiveawayEntry deletes the giveaway entry for the given user identifier from the database.
9498
//
9599
// If an error occours it will be returned. However if no datbase entry matched it returns err ==
96100
// nil, not err == sql.ErrNoRows. Because sql.ErrNoRows also results in the non-existence of the
97101
// requested row and therefore is treated as a successful call.
98-
func DeleteGiveawayEntry(userID string) error {
99-
_, err := Exec("DELETE FROM giveaway WHERE id=?", userID)
102+
func DeleteGiveawayEntry(userID string, platform Platform, platformID string) error {
103+
_, err := Exec("DELETE FROM giveaway WHERE id=? AND platform=? AND platform_id=?", userID, platform, platformID)
100104
if err == sql.ErrNoRows {
101105
return nil
102106
}
@@ -110,13 +114,13 @@ func DeleteGiveawayEntry(userID string) error {
110114
//
111115
// If there was no error the modified entry is returned. If there was an error, an emtpy
112116
// GiveawayEntry is returned instead.
113-
func AddGiveawayWeight(prefix, userID string, amount int) GiveawayEntry {
117+
func AddGiveawayWeight(prefix, userID string, amount int, platform Platform, platformID string) GiveawayEntry {
114118
var (
115119
weight int
116120
lastEntryID string
117121
new bool
118122
)
119-
err := QueryRow("SELECT weight,last_entry_id FROM giveaway WHERE id=?", userID).Scan(&weight, &lastEntryID)
123+
err := QueryRow("SELECT weight,last_entry_id FROM giveaway WHERE id=? AND platform=? AND platform_id=?", userID, platform, platformID).Scan(&weight, &lastEntryID)
120124
if err == sql.ErrNoRows {
121125
new = true
122126
} else if err != nil {
@@ -140,20 +144,22 @@ func AddGiveawayWeight(prefix, userID string, amount int) GiveawayEntry {
140144
log.Printf("Database failed to insert giveaway for '%s': %v", userID, err)
141145
return GiveawayEntry{}
142146
}
143-
return GiveawayEntry{userID, weight, lastEntry}
147+
return GiveawayEntry{userID, weight, lastEntry, platform, platformID}
144148
}
145149
_, err = Exec("UPDATE giveaway SET weight=?,last_entry_id=? WHERE id=?", weight, lastEntryID, userID)
146150
if err != nil {
147151
log.Printf("Database failed to update weight (new: %d) for '%s': %v", weight, userID, err)
148152
return GiveawayEntry{}
149153
}
150-
return GiveawayEntry{userID, weight, lastEntry}
154+
return GiveawayEntry{userID, weight, lastEntry, platform, platformID}
151155
}
152156

153157
// GetAllGiveawayEntries gets all giveaway entries that matches prefix.
154-
func GetAllGiveawayEntries(prefix string) []GiveawayEntry {
155-
rows, err := Query("SELECT id,weight,last_entry_id FROM giveaway")
156-
if err != nil {
158+
func GetAllGiveawayEntries(prefix string, platform Platform, platformID string) []GiveawayEntry {
159+
rows, err := Query("SELECT id,weight,last_entry_id FROM giveaway WHERE platform=? AND platform_id=?", platform, platformID)
160+
if err == sql.ErrNoRows {
161+
return []GiveawayEntry{}
162+
} else if err != nil {
157163
log.Printf("ERROR: could not get entries from database: %v", err)
158164
return []GiveawayEntry{}
159165
}
@@ -173,7 +179,7 @@ func GetAllGiveawayEntries(prefix string) []GiveawayEntry {
173179
}
174180

175181
if lastEntryID == "" {
176-
entries = append(entries, GiveawayEntry{UserID: userID, Weight: weight})
182+
entries = append(entries, GiveawayEntry{UserID: userID, Weight: weight, Platform: platform, PlatformID: platformID})
177183
continue
178184
}
179185

@@ -187,7 +193,7 @@ func GetAllGiveawayEntries(prefix string) []GiveawayEntry {
187193
log.Printf("ERROR: could not convert last_entry_id '%s' to time: %v", lastEntryID, err)
188194
continue
189195
}
190-
entries = append(entries, GiveawayEntry{userID, weight, lastEntry})
196+
entries = append(entries, GiveawayEntry{userID, weight, lastEntry, platform, platformID})
191197
}
192198
return entries
193199
}

event/event.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,6 @@ func AddListeners(dc *discordgo.Session, t *twitchgo.Session, webChan chan struc
5252
t.OnChannelMessage(twitch.MessageHandler)
5353

5454
addYouTubeListeners(dc)
55-
addTwitchListeners(dc, t)
55+
addTwitchListeners(dc, t, webChan)
5656
addScheduledTriggers(dc, t, webChan)
5757
}

event/scheduledTriggers.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,23 @@ import (
2828
)
2929

3030
func addScheduledTriggers(dc *discordgo.Session, t *twitchgo.Session, webChan chan struct{}) {
31-
go scheduleFunction(dc, t, 0, 0,
31+
go scheduleFunction(dc, t, 0, 0, webChan,
3232
adventcalendar.Midnight,
3333
)
3434

35-
go scheduleFunction(dc, t, viper.GetInt("event.morning_hour"), viper.GetInt("event.morning_minute"),
35+
go scheduleFunction(dc, t, viper.GetInt("event.morning_hour"), viper.GetInt("event.morning_minute"), webChan,
3636
birthday.Check,
3737
adventcalendar.Post,
3838
)
3939

4040
go refreshYoutube(webChan)
4141
}
4242

43-
func scheduleFunction(dc *discordgo.Session, t *twitchgo.Session, hour, min int, callbacks ...interface{}) {
43+
func scheduleFunction(dc *discordgo.Session, t *twitchgo.Session, hour, min int, webChan chan struct{}, callbacks ...interface{}) {
4444
if len(callbacks) == 0 {
4545
return
4646
}
47+
<-webChan
4748
log.Printf("scheduled %d function(s) for %2d:%02d!", len(callbacks), hour, min)
4849
time.Sleep(time.Second * 5)
4950
for {

event/twitch/announce.go

+15-9
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,22 @@ import (
1717

1818
// HandleChannelUpdate is the event handler for the "channel.update" event from twitch.
1919
func HandleChannelUpdate(s *discordgo.Session, t *twitchgo.Session, e *webTwitch.ChannelUpdateEvent) {
20-
HandleStreamAnnouncementChange(s, t, e.BroadcasterUserID, e.Title, "")
20+
HandleStreamAnnouncementChange(s, t, e.BroadcasterUserID, e.Title, false)
2121
}
2222

2323
// HandleStreamOnline is the event handler for the "stream.online" event from twitch.
2424
func HandleStreamOnline(s *discordgo.Session, t *twitchgo.Session, e *webTwitch.StreamOnlineEvent) {
25-
HandleStreamAnnouncementChange(s, t, e.BroadcasterUserID, "", lang.GetDefault("module.twitch.msg.nofification"))
25+
HandleStreamAnnouncementChange(s, t, e.BroadcasterUserID, "", true)
2626
}
2727

2828
// HandleStreamOffline is the event handler for the "stream.offline" event from twitch.
2929
func HandleStreamOffline(s *discordgo.Session, t *twitchgo.Session, e *webTwitch.StreamOfflineEvent) {
30-
HandleStreamAnnouncementChange(s, t, e.BroadcasterUserID, "", "")
30+
HandleStreamAnnouncementChange(s, t, e.BroadcasterUserID, "", false)
3131
}
3232

3333
// HandleStreamAnnouncementChange is a general event handler for twitch events, that should update
3434
// the discord announcement embed.
35-
func HandleStreamAnnouncementChange(s *discordgo.Session, t *twitchgo.Session, platformID, title, notification string) {
35+
func HandleStreamAnnouncementChange(s *discordgo.Session, t *twitchgo.Session, platformID, title string, sendNotification bool) {
3636
announcements, err := database.GetAnnouncement(database.AnnouncementPlatformTwitch, platformID)
3737
if err == sql.ErrNoRows {
3838
return
@@ -42,7 +42,7 @@ func HandleStreamAnnouncementChange(s *discordgo.Session, t *twitchgo.Session, p
4242
}
4343

4444
for _, announcement := range announcements {
45-
err = updateAnnouncementMessage(s, t, announcement, title, notification)
45+
err = updateAnnouncementMessage(s, t, announcement, title, sendNotification)
4646
if err != nil {
4747
log.Printf("Error: %v", err)
4848
}
@@ -72,7 +72,7 @@ func newAnnouncementMessage(s *discordgo.Session, announcement *database.Announc
7272
return msg, announcement.UpdateAnnouncementMessage(msg.ID)
7373
}
7474

75-
func updateAnnouncementMessage(s *discordgo.Session, t *twitchgo.Session, announcement *database.Announcement, title, notification string) error {
75+
func updateAnnouncementMessage(s *discordgo.Session, t *twitchgo.Session, announcement *database.Announcement, title string, sendNotification bool) error {
7676
msg, err := getAnnouncementMessage(s, announcement)
7777
if err != nil {
7878
return fmt.Errorf("get announcement in channel '%s': %v", announcement, err)
@@ -114,11 +114,17 @@ func updateAnnouncementMessage(s *discordgo.Session, t *twitchgo.Session, announ
114114
setOfflineEmbed(embed, user)
115115
}
116116

117-
if notification != "" {
117+
if sendNotification {
118+
notificationContent := announcement.Notification
119+
if announcement.Notification == "" {
120+
notificationContent = user.DisplayName
121+
} else if strings.Contains(announcement.Notification, "%s") {
122+
notificationContent = fmt.Sprintf(announcement.Notification, user.DisplayName)
123+
}
118124
if announcement.RoleID != "" {
119-
notification += fmt.Sprintf("\n<@&%s>", announcement.RoleID)
125+
notificationContent += (&discordgo.Role{ID: announcement.RoleID}).Mention()
120126
}
121-
msgNotification, err := s.ChannelMessageSend(announcement.ChannelID, fmt.Sprintf(notification, user.DisplayName))
127+
msgNotification, err := s.ChannelMessageSend(announcement.ChannelID, notificationContent)
122128
if err != nil {
123129
return fmt.Errorf("send notification: %v", err)
124130
}

event/twitch/messageHandler.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const tp string = "twitch.command."
3333
// MessageHandler handles new messages from the twitch chat(s). It will be called on every new
3434
// message.
3535
func MessageHandler(t *twitchgo.Session, channel string, user *twitchgo.IRCUser, message, msgID string, tags twitchgo.IRCMessageTags) {
36-
log.Printf("<%s@%s> %s", user.Nickname, channel, message)
36+
//log.Printf("<%s@%s> %s", user.Nickname, channel, message)
3737
}
3838

3939
// HandleCmdJoin is the handler for a command in a twitch chat. This handler buys a giveaway ticket
@@ -56,7 +56,7 @@ func HandleCmdJoin(t *twitchgo.Session, channel string, user *twitchgo.IRCUser,
5656
t.SendMessagef(channel, lang.GetDefault(tp+"msg.won"), user.Nickname)
5757
return
5858
}
59-
entry := database.GetGiveawayEntry("tw11", user.Nickname)
59+
entry := database.GetGiveawayEntry("tw11", user.Nickname, database.AnnouncementPlatformTwitch, channel) // FIXME: use channel ID instead of name
6060
if entry.UserID == "" {
6161
log.Printf("Error getting database giveaway entry: %v", err)
6262
t.SendMessage(channel, lang.GetDefault("twitch.command.generic.error"))
@@ -118,7 +118,7 @@ func HandleCmdJoin(t *twitchgo.Session, channel string, user *twitchgo.IRCUser,
118118
t.SendMessagef(channel, lang.GetDefault(tp+"msg.too_few_points"), user.Nickname, sePoints.Points, joinCost-sePoints.Points, joinCost)
119119
return
120120
}
121-
entry = database.AddGiveawayWeight("tw11", user.Nickname, 1)
121+
entry = database.AddGiveawayWeight("tw11", user.Nickname, 1, database.AnnouncementPlatformTwitch, channel) // FIXME: use channel ID instead of name
122122
if entry.UserID == "" {
123123
log.Printf("Error getting database giveaway entry: %v", err)
124124
t.SendMessage(channel, lang.GetDefault("twitch.command.generic.error"))
@@ -176,7 +176,7 @@ func HandleCmdTickets(t *twitchgo.Session, channel string, source *twitchgo.IRCU
176176
return
177177
}
178178

179-
entry := database.GetGiveawayEntry("tw11", userID)
179+
entry := database.GetGiveawayEntry("tw11", userID, database.AnnouncementPlatformTwitch, channel) // FIXME: use channel ID instead of name
180180
if entry.Weight >= 10 {
181181
if source.Nickname == userID {
182182
t.SendMessagef(channel, lang.GetDefault(tp+"msg.max_tickets"), source.Nickname)
@@ -270,15 +270,15 @@ func HandleCmdDraw(t *twitchgo.Session, channel string, user *twitchgo.IRCUser,
270270
return
271271
}
272272

273-
winner, totalTickets := database.DrawGiveawayWinner(database.GetAllGiveawayEntries("tw11"))
273+
winner, totalTickets := database.DrawGiveawayWinner(database.GetAllGiveawayEntries("tw11", database.AnnouncementPlatformTwitch, channel)) // FIXME: use channel ID instead of name
274274
if totalTickets == 0 {
275275
t.SendMessagef(channel, lang.GetDefault(tp+"msg.no_entries"), user.Nickname)
276276
return
277277
}
278278

279279
t.SendMessagef(channel, lang.GetDefault(tp+"msg.winner"), winner.UserID, prize.Name, winner.Weight, float64(winner.Weight*100)/float64(totalTickets))
280280

281-
err = database.DeleteGiveawayEntry(winner.UserID)
281+
err = database.DeleteGiveawayEntry(winner.UserID, database.AnnouncementPlatformTwitch, channel) // FIXME: use channel ID instead of name
282282
if err != nil {
283283
log.Printf("Error deleting database giveaway entry: %v", err)
284284
t.SendMessage(channel, lang.GetDefault("twitch.command.generic.error"))

0 commit comments

Comments
 (0)