aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/bwmarrin/discordgo/message.go
blob: eb2f49628dbf6844f5b11f2c49f73feaf5a8f5a4 (plain)
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
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains code related to the Message struct

package discordgo

import (
	"encoding/json"
	"io"
	"regexp"
	"strings"
	"time"
)

// MessageType is the type of Message
// https://discord.com/developers/docs/resources/channel#message-object-message-types
type MessageType int

// Block contains the valid known MessageType values
const (
	MessageTypeDefault                               MessageType = 0
	MessageTypeRecipientAdd                          MessageType = 1
	MessageTypeRecipientRemove                       MessageType = 2
	MessageTypeCall                                  MessageType = 3
	MessageTypeChannelNameChange                     MessageType = 4
	MessageTypeChannelIconChange                     MessageType = 5
	MessageTypeChannelPinnedMessage                  MessageType = 6
	MessageTypeGuildMemberJoin                       MessageType = 7
	MessageTypeUserPremiumGuildSubscription          MessageType = 8
	MessageTypeUserPremiumGuildSubscriptionTierOne   MessageType = 9
	MessageTypeUserPremiumGuildSubscriptionTierTwo   MessageType = 10
	MessageTypeUserPremiumGuildSubscriptionTierThree MessageType = 11
	MessageTypeChannelFollowAdd                      MessageType = 12
	MessageTypeGuildDiscoveryDisqualified            MessageType = 14
	MessageTypeGuildDiscoveryRequalified             MessageType = 15
	MessageTypeThreadCreated                         MessageType = 18
	MessageTypeReply                                 MessageType = 19
	MessageTypeChatInputCommand                      MessageType = 20
	MessageTypeThreadStarterMessage                  MessageType = 21
	MessageTypeContextMenuCommand                    MessageType = 23
)

// A Message stores all data related to a specific Discord message.
type Message struct {
	// The ID of the message.
	ID string `json:"id"`

	// The ID of the channel in which the message was sent.
	ChannelID string `json:"channel_id"`

	// The ID of the guild in which the message was sent.
	GuildID string `json:"guild_id,omitempty"`

	// The content of the message.
	Content string `json:"content"`

	// The time at which the messsage was sent.
	// CAUTION: this field may be removed in a
	// future API version; it is safer to calculate
	// the creation time via the ID.
	Timestamp time.Time `json:"timestamp"`

	// The time at which the last edit of the message
	// occurred, if it has been edited.
	EditedTimestamp *time.Time `json:"edited_timestamp"`

	// The roles mentioned in the message.
	MentionRoles []string `json:"mention_roles"`

	// Whether the message is text-to-speech.
	TTS bool `json:"tts"`

	// Whether the message mentions everyone.
	MentionEveryone bool `json:"mention_everyone"`

	// The author of the message. This is not guaranteed to be a
	// valid user (webhook-sent messages do not possess a full author).
	Author *User `json:"author"`

	// A list of attachments present in the message.
	Attachments []*MessageAttachment `json:"attachments"`

	// A list of components attached to the message.
	Components []MessageComponent `json:"-"`

	// A list of embeds present in the message.
	Embeds []*MessageEmbed `json:"embeds"`

	// A list of users mentioned in the message.
	Mentions []*User `json:"mentions"`

	// A list of reactions to the message.
	Reactions []*MessageReactions `json:"reactions"`

	// Whether the message is pinned or not.
	Pinned bool `json:"pinned"`

	// The type of the message.
	Type MessageType `json:"type"`

	// The webhook ID of the message, if it was generated by a webhook
	WebhookID string `json:"webhook_id"`

	// Member properties for this message's author,
	// contains only partial information
	Member *Member `json:"member"`

	// Channels specifically mentioned in this message
	// Not all channel mentions in a message will appear in mention_channels.
	// Only textual channels that are visible to everyone in a lurkable guild will ever be included.
	// Only crossposted messages (via Channel Following) currently include mention_channels at all.
	// If no mentions in the message meet these requirements, this field will not be sent.
	MentionChannels []*Channel `json:"mention_channels"`

	// Is sent with Rich Presence-related chat embeds
	Activity *MessageActivity `json:"activity"`

	// Is sent with Rich Presence-related chat embeds
	Application *MessageApplication `json:"application"`

	// MessageReference contains reference data sent with crossposted or reply messages.
	// This does not contain the reference *to* this message; this is for when *this* message references another.
	// To generate a reference to this message, use (*Message).Reference().
	MessageReference *MessageReference `json:"message_reference"`

	// The message associated with the message_reference
	// NOTE: This field is only returned for messages with a type of 19 (REPLY) or 21 (THREAD_STARTER_MESSAGE).
	// If the message is a reply but the referenced_message field is not present,
	// the backend did not attempt to fetch the message that was being replied to, so its state is unknown.
	// If the field exists but is null, the referenced message was deleted.
	ReferencedMessage *Message `json:"referenced_message"`

	// Is sent when the message is a response to an Interaction, without an existing message.
	// This means responses to message component interactions do not include this property,
	// instead including a MessageReference, as components exist on preexisting messages.
	Interaction *MessageInteraction `json:"interaction"`

	// The flags of the message, which describe extra features of a message.
	// This is a combination of bit masks; the presence of a certain permission can
	// be checked by performing a bitwise AND between this int and the flag.
	Flags MessageFlags `json:"flags"`

	// The thread that was started from this message, includes thread member object
	Thread *Channel `json:"thread,omitempty"`

	// An array of Sticker objects, if any were sent.
	StickerItems []*Sticker `json:"sticker_items"`
}

// UnmarshalJSON is a helper function to unmarshal the Message.
func (m *Message) UnmarshalJSON(data []byte) error {
	type message Message
	var v struct {
		message
		RawComponents []unmarshalableMessageComponent `json:"components"`
	}
	err := json.Unmarshal(data, &v)
	if err != nil {
		return err
	}
	*m = Message(v.message)
	m.Components = make([]MessageComponent, len(v.RawComponents))
	for i, v := range v.RawComponents {
		m.Components[i] = v.MessageComponent
	}
	return err
}

// GetCustomEmojis pulls out all the custom (Non-unicode) emojis from a message and returns a Slice of the Emoji struct.
func (m *Message) GetCustomEmojis() []*Emoji {
	var toReturn []*Emoji
	emojis := EmojiRegex.FindAllString(m.Content, -1)
	if len(emojis) < 1 {
		return toReturn
	}
	for _, em := range emojis {
		parts := strings.Split(em, ":")
		toReturn = append(toReturn, &Emoji{
			ID:       parts[2][:len(parts[2])-1],
			Name:     parts[1],
			Animated: strings.HasPrefix(em, "<a:"),
		})
	}
	return toReturn
}

// MessageFlags is the flags of "message" (see MessageFlags* consts)
// https://discord.com/developers/docs/resources/channel#message-object-message-flags
type MessageFlags int

// Valid MessageFlags values
const (
	// MessageFlagsCrossPosted This message has been published to subscribed channels (via Channel Following).
	MessageFlagsCrossPosted MessageFlags = 1 << 0
	// MessageFlagsIsCrossPosted this message originated from a message in another channel (via Channel Following).
	MessageFlagsIsCrossPosted MessageFlags = 1 << 1
	// MessageFlagsSupressEmbeds do not include any embeds when serializing this message.
	MessageFlagsSupressEmbeds MessageFlags = 1 << 2
	// MessageFlagsSourceMessageDeleted the source message for this crosspost has been deleted (via Channel Following).
	MessageFlagsSourceMessageDeleted MessageFlags = 1 << 3
	// MessageFlagsUrgent this message came from the urgent message system.
	MessageFlagsUrgent MessageFlags = 1 << 4
	// MessageFlagsHasThread this message has an associated thread, with the same id as the message.
	MessageFlagsHasThread MessageFlags = 1 << 5
	// MessageFlagsEphemeral this message is only visible to the user who invoked the Interaction.
	MessageFlagsEphemeral MessageFlags = 1 << 6
	// MessageFlagsLoading this message is an Interaction Response and the bot is "thinking".
	MessageFlagsLoading MessageFlags = 1 << 7
	// MessageFlagsFailedToMentionSomeRolesInThread this message failed to mention some roles and add their members to the thread.
	MessageFlagsFailedToMentionSomeRolesInThread MessageFlags = 1 << 8
)

// File stores info about files you e.g. send in messages.
type File struct {
	Name        string
	ContentType string
	Reader      io.Reader
}

// MessageSend stores all parameters you can send with ChannelMessageSendComplex.
type MessageSend struct {
	Content         string                  `json:"content,omitempty"`
	Embeds          []*MessageEmbed         `json:"embeds,omitempty"`
	TTS             bool                    `json:"tts"`
	Components      []MessageComponent      `json:"components"`
	Files           []*File                 `json:"-"`
	AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
	Reference       *MessageReference       `json:"message_reference,omitempty"`

	// TODO: Remove this when compatibility is not required.
	File *File `json:"-"`

	// TODO: Remove this when compatibility is not required.
	Embed *MessageEmbed `json:"-"`
}

// MessageEdit is used to chain parameters via ChannelMessageEditComplex, which
// is also where you should get the instance from.
type MessageEdit struct {
	Content         *string                 `json:"content,omitempty"`
	Components      []MessageComponent      `json:"components"`
	Embeds          []*MessageEmbed         `json:"embeds,omitempty"`
	AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`

	ID      string
	Channel string

	// TODO: Remove this when compatibility is not required.
	Embed *MessageEmbed `json:"-"`
}

// NewMessageEdit returns a MessageEdit struct, initialized
// with the Channel and ID.
func NewMessageEdit(channelID string, messageID string) *MessageEdit {
	return &MessageEdit{
		Channel: channelID,
		ID:      messageID,
	}
}

// SetContent is the same as setting the variable Content,
// except it doesn't take a pointer.
func (m *MessageEdit) SetContent(str string) *MessageEdit {
	m.Content = &str
	return m
}

// SetEmbed is a convenience function for setting the embed,
// so you can chain commands.
func (m *MessageEdit) SetEmbed(embed *MessageEmbed) *MessageEdit {
	m.Embeds = []*MessageEmbed{embed}
	return m
}

// SetEmbeds is a convenience function for setting the embeds,
// so you can chain commands.
func (m *MessageEdit) SetEmbeds(embeds []*MessageEmbed) *MessageEdit {
	m.Embeds = embeds
	return m
}

// AllowedMentionType describes the types of mentions used
// in the MessageAllowedMentions type.
type AllowedMentionType string

// The types of mentions used in MessageAllowedMentions.
const (
	AllowedMentionTypeRoles    AllowedMentionType = "roles"
	AllowedMentionTypeUsers    AllowedMentionType = "users"
	AllowedMentionTypeEveryone AllowedMentionType = "everyone"
)

// MessageAllowedMentions allows the user to specify which mentions
// Discord is allowed to parse in this message. This is useful when
// sending user input as a message, as it prevents unwanted mentions.
// If this type is used, all mentions must be explicitly whitelisted,
// either by putting an AllowedMentionType in the Parse slice
// (allowing all mentions of that type) or, in the case of roles and
// users, explicitly allowing those mentions on an ID-by-ID basis.
// For more information on this functionality, see:
// https://discordapp.com/developers/docs/resources/channel#allowed-mentions-object-allowed-mentions-reference
type MessageAllowedMentions struct {
	// The mention types that are allowed to be parsed in this message.
	// Please note that this is purposely **not** marked as omitempty,
	// so if a zero-value MessageAllowedMentions object is provided no
	// mentions will be allowed.
	Parse []AllowedMentionType `json:"parse"`

	// A list of role IDs to allow. This cannot be used when specifying
	// AllowedMentionTypeRoles in the Parse slice.
	Roles []string `json:"roles,omitempty"`

	// A list of user IDs to allow. This cannot be used when specifying
	// AllowedMentionTypeUsers in the Parse slice.
	Users []string `json:"users,omitempty"`
}

// A MessageAttachment stores data for message attachments.
type MessageAttachment struct {
	ID          string `json:"id"`
	URL         string `json:"url"`
	ProxyURL    string `json:"proxy_url"`
	Filename    string `json:"filename"`
	ContentType string `json:"content_type"`
	Width       int    `json:"width"`
	Height      int    `json:"height"`
	Size        int    `json:"size"`
	Ephemeral   bool   `json:"ephemeral"`
}

// MessageEmbedFooter is a part of a MessageEmbed struct.
type MessageEmbedFooter struct {
	Text         string `json:"text,omitempty"`
	IconURL      string `json:"icon_url,omitempty"`
	ProxyIconURL string `json:"proxy_icon_url,omitempty"`
}

// MessageEmbedImage is a part of a MessageEmbed struct.
type MessageEmbedImage struct {
	URL      string `json:"url,omitempty"`
	ProxyURL string `json:"proxy_url,omitempty"`
	Width    int    `json:"width,omitempty"`
	Height   int    `json:"height,omitempty"`
}

// MessageEmbedThumbnail is a part of a MessageEmbed struct.
type MessageEmbedThumbnail struct {
	URL      string `json:"url,omitempty"`
	ProxyURL string `json:"proxy_url,omitempty"`
	Width    int    `json:"width,omitempty"`
	Height   int    `json:"height,omitempty"`
}

// MessageEmbedVideo is a part of a MessageEmbed struct.
type MessageEmbedVideo struct {
	URL    string `json:"url,omitempty"`
	Width  int    `json:"width,omitempty"`
	Height int    `json:"height,omitempty"`
}

// MessageEmbedProvider is a part of a MessageEmbed struct.
type MessageEmbedProvider struct {
	URL  string `json:"url,omitempty"`
	Name string `json:"name,omitempty"`
}

// MessageEmbedAuthor is a part of a MessageEmbed struct.
type MessageEmbedAuthor struct {
	URL          string `json:"url,omitempty"`
	Name         string `json:"name,omitempty"`
	IconURL      string `json:"icon_url,omitempty"`
	ProxyIconURL string `json:"proxy_icon_url,omitempty"`
}

// MessageEmbedField is a part of a MessageEmbed struct.
type MessageEmbedField struct {
	Name   string `json:"name,omitempty"`
	Value  string `json:"value,omitempty"`
	Inline bool   `json:"inline,omitempty"`
}

// An MessageEmbed stores data for message embeds.
type MessageEmbed struct {
	URL         string                 `json:"url,omitempty"`
	Type        EmbedType              `json:"type,omitempty"`
	Title       string                 `json:"title,omitempty"`
	Description string                 `json:"description,omitempty"`
	Timestamp   string                 `json:"timestamp,omitempty"`
	Color       int                    `json:"color,omitempty"`
	Footer      *MessageEmbedFooter    `json:"footer,omitempty"`
	Image       *MessageEmbedImage     `json:"image,omitempty"`
	Thumbnail   *MessageEmbedThumbnail `json:"thumbnail,omitempty"`
	Video       *MessageEmbedVideo     `json:"video,omitempty"`
	Provider    *MessageEmbedProvider  `json:"provider,omitempty"`
	Author      *MessageEmbedAuthor    `json:"author,omitempty"`
	Fields      []*MessageEmbedField   `json:"fields,omitempty"`
}

// EmbedType is the type of embed
// https://discord.com/developers/docs/resources/channel#embed-object-embed-types
type EmbedType string

// Block of valid EmbedTypes
const (
	EmbedTypeRich    EmbedType = "rich"
	EmbedTypeImage   EmbedType = "image"
	EmbedTypeVideo   EmbedType = "video"
	EmbedTypeGifv    EmbedType = "gifv"
	EmbedTypeArticle EmbedType = "article"
	EmbedTypeLink    EmbedType = "link"
)

// MessageReactions holds a reactions object for a message.
type MessageReactions struct {
	Count int    `json:"count"`
	Me    bool   `json:"me"`
	Emoji *Emoji `json:"emoji"`
}

// MessageActivity is sent with Rich Presence-related chat embeds
type MessageActivity struct {
	Type    MessageActivityType `json:"type"`
	PartyID string              `json:"party_id"`
}

// MessageActivityType is the type of message activity
type MessageActivityType int

// Constants for the different types of Message Activity
const (
	MessageActivityTypeJoin        MessageActivityType = 1
	MessageActivityTypeSpectate    MessageActivityType = 2
	MessageActivityTypeListen      MessageActivityType = 3
	MessageActivityTypeJoinRequest MessageActivityType = 5
)

// MessageApplication is sent with Rich Presence-related chat embeds
type MessageApplication struct {
	ID          string `json:"id"`
	CoverImage  string `json:"cover_image"`
	Description string `json:"description"`
	Icon        string `json:"icon"`
	Name        string `json:"name"`
}

// MessageReference contains reference data sent with crossposted messages
type MessageReference struct {
	MessageID string `json:"message_id"`
	ChannelID string `json:"channel_id"`
	GuildID   string `json:"guild_id,omitempty"`
}

// Reference returns MessageReference of given message
func (m *Message) Reference() *MessageReference {
	return &MessageReference{
		GuildID:   m.GuildID,
		ChannelID: m.ChannelID,
		MessageID: m.ID,
	}
}

// ContentWithMentionsReplaced will replace all @<id> mentions with the
// username of the mention.
func (m *Message) ContentWithMentionsReplaced() (content string) {
	content = m.Content

	for _, user := range m.Mentions {
		content = strings.NewReplacer(
			"<@"+user.ID+">", "@"+user.Username,
			"<@!"+user.ID+">", "@"+user.Username,
		).Replace(content)
	}
	return
}

var patternChannels = regexp.MustCompile("<#[^>]*>")

// ContentWithMoreMentionsReplaced will replace all @<id> mentions with the
// username of the mention, but also role IDs and more.
func (m *Message) ContentWithMoreMentionsReplaced(s *Session) (content string, err error) {
	content = m.Content

	if !s.StateEnabled {
		content = m.ContentWithMentionsReplaced()
		return
	}

	channel, err := s.State.Channel(m.ChannelID)
	if err != nil {
		content = m.ContentWithMentionsReplaced()
		return
	}

	for _, user := range m.Mentions {
		nick := user.Username

		member, err := s.State.Member(channel.GuildID, user.ID)
		if err == nil && member.Nick != "" {
			nick = member.Nick
		}

		content = strings.NewReplacer(
			"<@"+user.ID+">", "@"+user.Username,
			"<@!"+user.ID+">", "@"+nick,
		).Replace(content)
	}
	for _, roleID := range m.MentionRoles {
		role, err := s.State.Role(channel.GuildID, roleID)
		if err != nil || !role.Mentionable {
			continue
		}

		content = strings.Replace(content, "<@&"+role.ID+">", "@"+role.Name, -1)
	}

	content = patternChannels.ReplaceAllStringFunc(content, func(mention string) string {
		channel, err := s.State.Channel(mention[2 : len(mention)-1])
		if err != nil || channel.Type == ChannelTypeGuildVoice {
			return mention
		}

		return "#" + channel.Name
	})
	return
}

// MessageInteraction contains information about the application command interaction which generated the message.
type MessageInteraction struct {
	ID   string          `json:"id"`
	Type InteractionType `json:"type"`
	Name string          `json:"name"`
	User *User           `json:"user"`

	// Member is only present when the interaction is from a guild.
	Member *Member `json:"member"`
}