-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathstarboard.go
345 lines (302 loc) · 10.4 KB
/
starboard.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
package main
import (
"fmt"
"github.com/5HT2/taro-bot/bot"
"github.com/5HT2/taro-bot/cmd"
"github.com/5HT2/taro-bot/plugins"
"github.com/5HT2/taro-bot/util"
"github.com/diamondburned/arikawa/v3/discord"
"github.com/diamondburned/arikawa/v3/gateway"
"log"
"reflect"
"strconv"
"strings"
"time"
)
var (
stars3Emoji = "⭐"
stars5Emoji = "🌟"
stars6Emoji = "💫"
stars9Emoji = "✨"
)
func InitPlugin(_ *plugins.PluginInit) *plugins.Plugin {
return &plugins.Plugin{
Name: "Starboard",
Description: "Pin messages to a custom channel",
Version: "1.0.0",
Commands: []bot.CommandInfo{{
Fn: StarboardConfigCommand,
FnName: "StarboardConfigCommand",
Name: "configurestarboard",
Description: "Configure Starboard",
Aliases: []string{"starboardcfg", "cfgstarboard", "scfg"},
GuildOnly: true,
}},
Responses: []bot.ResponseInfo{},
Handlers: []bot.HandlerInfo{{
Fn: StarboardReactionHandler,
FnName: "StarboardReactionHandler",
FnType: reflect.TypeOf(func(*gateway.MessageReactionAddEvent) {}),
}},
}
}
func StarboardConfigCommand(c bot.Command) error {
err := cmd.HasPermission("channels", c)
if err == nil {
if arg, _ := cmd.ParseStringArg(c.Args, 1, true); err != nil {
return err
} else {
arg2, errParse := cmd.ParseChannelArg(c.Args, 2)
var err error = nil
bot.GuildContext(c.E.GuildID, func(g *bot.GuildConfig) (*bot.GuildConfig, string) {
switch arg {
case "regular":
if errParse != nil {
g.Starboard.Channel = 0
_, err = cmd.SendEmbed(c.E, "Starboard Channels", "⛔ Disabled regular starboard", bot.ErrorColor)
return g, "StarboardConfigCommand: disable regular starboard"
} else {
g.Starboard.Channel = arg2
_, err = cmd.SendEmbed(c.E, "Starboard Channels", "✅ Enabled regular starboard", bot.SuccessColor)
return g, "StarboardConfigCommand: enable regular starboard"
}
case "nsfw":
if errParse != nil {
g.Starboard.NsfwChannel = 0
_, err = cmd.SendEmbed(c.E, "Starboard Channels", "⛔ Disabled NSFW starboard", bot.ErrorColor)
return g, "StarboardConfigCommand: disable nsfw starboard"
} else {
g.Starboard.NsfwChannel = arg2
_, err = cmd.SendEmbed(c.E, "Starboard Channels", "✅ Enabled NSFW starboard", bot.SuccessColor)
return g, "StarboardConfigCommand: enable nsfw starboard"
}
case "threshold":
if arg3, errParse := cmd.ParseInt64Arg(c.Args, 2); errParse != nil {
err = errParse
} else {
if arg3 <= 0 {
arg3 = 1
}
g.Starboard.Threshold = arg3
_, err = cmd.SendEmbed(c.E, "Starboard Threshold", fmt.Sprintf("✅ Set threshold to: %v", arg3), bot.SuccessColor)
}
return g, "StarboardConfigCommand: set threshold"
case "list":
regularC := "✅ Regular Starboard (<#" + strconv.FormatInt(g.Starboard.Channel, 10) + ">)"
nsfwC := "✅ NSFW Starboard (<#" + strconv.FormatInt(g.Starboard.NsfwChannel, 10) + ">)"
if g.Starboard.Channel == 0 {
regularC = "⛔ Regular Starboard"
}
if g.Starboard.NsfwChannel == 0 {
nsfwC = "⛔ NSFW Starboard"
}
embed := discord.Embed{
Title: "Starboard Channels",
Description: regularC + "\n" + nsfwC,
Color: bot.DefaultColor,
}
_, err = cmd.SendCustomEmbed(c.E.ChannelID, embed)
return g, "StarboardConfigCommand: list starboard channels"
default:
_, err = cmd.SendEmbed(c.E,
"Configure Starboard",
"Available arguments are:\n- `list`\n- `threshold [threshold]`\n- `nsfw|regular [channel]`",
bot.DefaultColor)
return g, "StarboardConfigCommand: show help"
}
})
return err
}
} else {
return err
}
}
func StarboardReactionHandler(i interface{}) {
defer util.LogPanic()
e := i.(*gateway.MessageReactionAddEvent)
start := time.Now().UnixMilli()
bot.GuildContext(e.GuildID, func(g *bot.GuildConfig) (*bot.GuildConfig, string) {
if g.Starboard.Threshold == 0 {
g.Starboard.Threshold = 3
}
// Not starred by a guild member
if e.Member == nil {
log.Printf("Not a guild member\n")
return g, "StarboardReactionHandler: check guild member"
}
// Not a star
if e.Emoji.APIString().PathString() != util.EscapedStar {
log.Printf("Not a star emoji\n")
return g, "StarboardReactionHandler: check reaction emoji"
}
msg, err := bot.Client.Message(e.ChannelID, e.MessageID)
if err != nil {
return g, "StarboardReactionHandler: get reaction message"
}
channel, err := bot.Client.Channel(e.ChannelID)
if err != nil {
return g, "StarboardReactionHandler: get reaction channel"
}
var sMsg *bot.StarboardMessage = nil
newPost := true
cID := int64(channel.ID)
log.Printf("Checking channel for starboard message %s\n", cmd.CreateMessageLink(int64(e.GuildID), msg, false))
// If user reacts to a post in a starboard channel
if cID == g.Starboard.Channel || cID == g.Starboard.NsfwChannel {
for _, m := range g.Starboard.Messages {
if m.PostID == int64(msg.ID) {
sMsg = &m
newPost = false
break
}
}
} else { // else if a user reacts to a post in a regular channel
for _, m := range g.Starboard.Messages {
if m.ID == int64(msg.ID) {
sMsg = &m
newPost = false
break
}
}
// If starred before channel ID was added, and the reaction is from the origin channel, update the stored one
if !newPost && sMsg.CID == 0 {
sMsg.CID = int64(msg.ChannelID)
}
}
if newPost {
sMsg = &bot.StarboardMessage{
Author: int64(msg.Author.ID),
CID: int64(msg.ChannelID),
ID: int64(msg.ID),
PostID: 0,
IsNsfw: channel.NSFW,
Stars: make([]int64, 0),
}
}
// Channel to send starboard message to
cID = g.Starboard.Channel
if sMsg.IsNsfw == true {
cID = g.Starboard.NsfwChannel
}
// Channel hasn't been set
if cID == 0 {
log.Printf("Channel ID is 0\n")
return g, "StarboardReactionHandler: check cID"
}
// Get post channel and ensure it exists
postChannel, err := bot.Client.Channel(discord.ChannelID(cID))
if err != nil {
log.Printf("Couldn't get post channel\n")
return g, "StarboardReactionHandler: get post channel"
}
// When adding a new star, ensure star user is not the same as author
// And also check if they've already been added
sUserID := int64(e.Member.User.ID)
if sMsg.Author != sUserID && !util.SliceContains(sMsg.Stars, sUserID) {
sMsg.Stars = append(sMsg.Stars, sUserID)
}
log.Printf("sUserID: %v\nsMsg:%v\n", sUserID, sMsg)
// Update our reactions in case any are missing from the API
for _, reaction := range msg.Reactions {
if reaction.Emoji.APIString().PathString() == util.EscapedStar {
userReactions, err := bot.Client.Reactions(msg.ChannelID, msg.ID, reaction.Emoji.APIString(), 0)
if err != nil {
log.Printf("Failed to get userReactions: %s\n", err)
return g, "StarboardReactionHandler: update sMsg.Stars"
}
for _, userReaction := range userReactions {
sUserID = int64(userReaction.ID)
if sMsg.Author != sUserID && !util.SliceContains(sMsg.Stars, sUserID) {
sMsg.Stars = append(sMsg.Stars, sUserID)
}
}
break
}
}
stars := len(sMsg.Stars)
// Not enough stars in sMsg to make post
if int64(stars) < g.Starboard.Threshold {
log.Printf("Not enough stars: %v\n", sMsg.Stars)
return g, "StarboardReactionHandler: check notEnoughStars"
}
content := getEmoji(stars) + " **" + strconv.Itoa(stars) + "** <#" + strconv.FormatInt(sMsg.CID, 10) + ">"
// Attempt to get existing message, and make a new one if it isn't there
pMsg, err := bot.Client.Message(postChannel.ID, discord.MessageID(sMsg.PostID))
if err != nil {
log.Printf("Couldn't get pMsg %v\n", err)
// Construct new starboard post if it couldn't retrieve an existing one
// Try to find a URL in the message content
description := msg.Content
url := cmd.UrlRegex.MatchString(msg.Content)
// Set the embed image to the URL and try to find the first attached image in the message attachments
var image *discord.EmbedImage = nil
for _, attachment := range msg.Attachments {
if strings.HasPrefix(attachment.ContentType, "image/") {
image = &discord.EmbedImage{URL: attachment.URL}
url = false // Don't remove URL in embed if we found an image attachment (eg, twitter link + image attachment)
break
}
}
// If we found only a URL (no other text) in the message content, and the found URL has an image extension, and we didn't find an attached image
// Set the description to nothing and set the image to the found URL
if url && util.FileExtMatches(util.ImageExtensions, msg.Content) {
description = ""
image = &discord.EmbedImage{URL: msg.Content}
}
member, err := bot.Client.Member(e.GuildID, discord.UserID(sMsg.Author))
if err != nil {
log.Printf("Couldn't get member %v\n", err)
return g, "StarboardReactionHandler: get sMsg.Author"
}
field := discord.EmbedField{Name: "Source", Value: cmd.CreateMessageLink(int64(e.GuildID), msg, true)}
footer := discord.EmbedFooter{Text: strconv.FormatInt(sMsg.Author, 10)}
embed := discord.Embed{
Description: description,
Author: cmd.CreateEmbedAuthor(*member),
Fields: []discord.EmbedField{field},
Footer: &footer,
Timestamp: msg.Timestamp,
Color: bot.StarboardColor,
Image: image,
}
log.Printf("Embed image: %v\n", embed.Image)
msg, err = bot.Client.SendMessage(postChannel.ID, content, embed)
if err != nil {
log.Printf("Error sending starboard post: %v\n", err)
} else {
sMsg.PostID = int64(msg.ID)
}
} else {
// Edit the post if it exists
_, err = bot.Client.EditMessage(postChannel.ID, discord.MessageID(sMsg.PostID), content, pMsg.Embeds...)
if err != nil {
log.Printf("Error updating starboard post: %v\n", err)
}
}
// Now that we have updated the stars and starboard post ID, save it in the config
if newPost {
g.Starboard.Messages = append(g.Starboard.Messages, *sMsg)
} else {
for i, m := range g.Starboard.Messages {
if m.ID == sMsg.ID {
g.Starboard.Messages[i] = *sMsg
}
}
}
return g, "StarboardReactionHandler: update post"
})
log.Printf("Execute: %vms (StarboardReactionHandler)\n", time.Now().UnixMilli()-start)
}
func getEmoji(stars int) (emoji string) {
switch stars {
case 0, 1, 2, 3, 4:
emoji = stars3Emoji
case 5:
emoji = stars5Emoji
case 6, 7, 8:
emoji = stars6Emoji
default:
emoji = stars9Emoji
}
return emoji
}