1// Discordgo - Discord bindings for Go 2// Available at https://github.com/bwmarrin/discordgo 3 4// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved. 5// Use of this source code is governed by a BSD-style 6// license that can be found in the LICENSE file. 7 8// This file contains code related to the Message struct 9 10package discordgo 11 12import ( 13 "io" 14 "regexp" 15 "strings" 16) 17 18// MessageType is the type of Message 19// https://discord.com/developers/docs/resources/channel#message-object-message-types 20type MessageType int 21 22// Block contains the valid known MessageType values 23const ( 24 MessageTypeDefault MessageType = iota 25 MessageTypeRecipientAdd 26 MessageTypeRecipientRemove 27 MessageTypeCall 28 MessageTypeChannelNameChange 29 MessageTypeChannelIconChange 30 MessageTypeChannelPinnedMessage 31 MessageTypeGuildMemberJoin 32 MessageTypeUserPremiumGuildSubscription 33 MessageTypeUserPremiumGuildSubscriptionTierOne 34 MessageTypeUserPremiumGuildSubscriptionTierTwo 35 MessageTypeUserPremiumGuildSubscriptionTierThree 36 MessageTypeChannelFollowAdd 37 MessageTypeGuildDiscoveryDisqualified = iota + 1 38 MessageTypeGuildDiscoveryRequalified 39 MessageTypeReply = iota + 4 40 MessageTypeApplicationCommand 41) 42 43// A Message stores all data related to a specific Discord message. 44type Message struct { 45 // The ID of the message. 46 ID string `json:"id"` 47 48 // The ID of the channel in which the message was sent. 49 ChannelID string `json:"channel_id"` 50 51 // The ID of the guild in which the message was sent. 52 GuildID string `json:"guild_id,omitempty"` 53 54 // The content of the message. 55 Content string `json:"content"` 56 57 // The time at which the messsage was sent. 58 // CAUTION: this field may be removed in a 59 // future API version; it is safer to calculate 60 // the creation time via the ID. 61 Timestamp Timestamp `json:"timestamp"` 62 63 // The time at which the last edit of the message 64 // occurred, if it has been edited. 65 EditedTimestamp Timestamp `json:"edited_timestamp"` 66 67 // The roles mentioned in the message. 68 MentionRoles []string `json:"mention_roles"` 69 70 // Whether the message is text-to-speech. 71 TTS bool `json:"tts"` 72 73 // Whether the message mentions everyone. 74 MentionEveryone bool `json:"mention_everyone"` 75 76 // The author of the message. This is not guaranteed to be a 77 // valid user (webhook-sent messages do not possess a full author). 78 Author *User `json:"author"` 79 80 // A list of attachments present in the message. 81 Attachments []*MessageAttachment `json:"attachments"` 82 83 // A list of embeds present in the message. Multiple 84 // embeds can currently only be sent by webhooks. 85 Embeds []*MessageEmbed `json:"embeds"` 86 87 // A list of users mentioned in the message. 88 Mentions []*User `json:"mentions"` 89 90 // A list of reactions to the message. 91 Reactions []*MessageReactions `json:"reactions"` 92 93 // Whether the message is pinned or not. 94 Pinned bool `json:"pinned"` 95 96 // The type of the message. 97 Type MessageType `json:"type"` 98 99 // The webhook ID of the message, if it was generated by a webhook 100 WebhookID string `json:"webhook_id"` 101 102 // Member properties for this message's author, 103 // contains only partial information 104 Member *Member `json:"member"` 105 106 // Channels specifically mentioned in this message 107 // Not all channel mentions in a message will appear in mention_channels. 108 // Only textual channels that are visible to everyone in a lurkable guild will ever be included. 109 // Only crossposted messages (via Channel Following) currently include mention_channels at all. 110 // If no mentions in the message meet these requirements, this field will not be sent. 111 MentionChannels []*Channel `json:"mention_channels"` 112 113 // Is sent with Rich Presence-related chat embeds 114 Activity *MessageActivity `json:"activity"` 115 116 // Is sent with Rich Presence-related chat embeds 117 Application *MessageApplication `json:"application"` 118 119 // MessageReference contains reference data sent with crossposted messages 120 MessageReference *MessageReference `json:"message_reference"` 121 122 // The flags of the message, which describe extra features of a message. 123 // This is a combination of bit masks; the presence of a certain permission can 124 // be checked by performing a bitwise AND between this int and the flag. 125 Flags MessageFlags `json:"flags"` 126} 127 128// MessageFlags is the flags of "message" (see MessageFlags* consts) 129// https://discord.com/developers/docs/resources/channel#message-object-message-flags 130type MessageFlags int 131 132// Valid MessageFlags values 133const ( 134 MessageFlagsCrossPosted MessageFlags = 1 << iota 135 MessageFlagsIsCrossPosted 136 MessageFlagsSupressEmbeds 137 MessageFlagsSourceMessageDeleted 138 MessageFlagsUrgent 139) 140 141// File stores info about files you e.g. send in messages. 142type File struct { 143 Name string 144 ContentType string 145 Reader io.Reader 146} 147 148// MessageSend stores all parameters you can send with ChannelMessageSendComplex. 149type MessageSend struct { 150 Content string `json:"content,omitempty"` 151 Embed *MessageEmbed `json:"embed,omitempty"` 152 TTS bool `json:"tts"` 153 Files []*File `json:"-"` 154 AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` 155 Reference *MessageReference `json:"message_reference,omitempty"` 156 157 // TODO: Remove this when compatibility is not required. 158 File *File `json:"-"` 159} 160 161// MessageEdit is used to chain parameters via ChannelMessageEditComplex, which 162// is also where you should get the instance from. 163type MessageEdit struct { 164 Content *string `json:"content,omitempty"` 165 Embed *MessageEmbed `json:"embed,omitempty"` 166 AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"` 167 168 ID string 169 Channel string 170} 171 172// NewMessageEdit returns a MessageEdit struct, initialized 173// with the Channel and ID. 174func NewMessageEdit(channelID string, messageID string) *MessageEdit { 175 return &MessageEdit{ 176 Channel: channelID, 177 ID: messageID, 178 } 179} 180 181// SetContent is the same as setting the variable Content, 182// except it doesn't take a pointer. 183func (m *MessageEdit) SetContent(str string) *MessageEdit { 184 m.Content = &str 185 return m 186} 187 188// SetEmbed is a convenience function for setting the embed, 189// so you can chain commands. 190func (m *MessageEdit) SetEmbed(embed *MessageEmbed) *MessageEdit { 191 m.Embed = embed 192 return m 193} 194 195// AllowedMentionType describes the types of mentions used 196// in the MessageAllowedMentions type. 197type AllowedMentionType string 198 199// The types of mentions used in MessageAllowedMentions. 200const ( 201 AllowedMentionTypeRoles AllowedMentionType = "roles" 202 AllowedMentionTypeUsers AllowedMentionType = "users" 203 AllowedMentionTypeEveryone AllowedMentionType = "everyone" 204) 205 206// MessageAllowedMentions allows the user to specify which mentions 207// Discord is allowed to parse in this message. This is useful when 208// sending user input as a message, as it prevents unwanted mentions. 209// If this type is used, all mentions must be explicitly whitelisted, 210// either by putting an AllowedMentionType in the Parse slice 211// (allowing all mentions of that type) or, in the case of roles and 212// users, explicitly allowing those mentions on an ID-by-ID basis. 213// For more information on this functionality, see: 214// https://discordapp.com/developers/docs/resources/channel#allowed-mentions-object-allowed-mentions-reference 215type MessageAllowedMentions struct { 216 // The mention types that are allowed to be parsed in this message. 217 // Please note that this is purposely **not** marked as omitempty, 218 // so if a zero-value MessageAllowedMentions object is provided no 219 // mentions will be allowed. 220 Parse []AllowedMentionType `json:"parse"` 221 222 // A list of role IDs to allow. This cannot be used when specifying 223 // AllowedMentionTypeRoles in the Parse slice. 224 Roles []string `json:"roles,omitempty"` 225 226 // A list of user IDs to allow. This cannot be used when specifying 227 // AllowedMentionTypeUsers in the Parse slice. 228 Users []string `json:"users,omitempty"` 229} 230 231// A MessageAttachment stores data for message attachments. 232type MessageAttachment struct { 233 ID string `json:"id"` 234 URL string `json:"url"` 235 ProxyURL string `json:"proxy_url"` 236 Filename string `json:"filename"` 237 Width int `json:"width"` 238 Height int `json:"height"` 239 Size int `json:"size"` 240} 241 242// MessageEmbedFooter is a part of a MessageEmbed struct. 243type MessageEmbedFooter struct { 244 Text string `json:"text,omitempty"` 245 IconURL string `json:"icon_url,omitempty"` 246 ProxyIconURL string `json:"proxy_icon_url,omitempty"` 247} 248 249// MessageEmbedImage is a part of a MessageEmbed struct. 250type MessageEmbedImage struct { 251 URL string `json:"url,omitempty"` 252 ProxyURL string `json:"proxy_url,omitempty"` 253 Width int `json:"width,omitempty"` 254 Height int `json:"height,omitempty"` 255} 256 257// MessageEmbedThumbnail is a part of a MessageEmbed struct. 258type MessageEmbedThumbnail struct { 259 URL string `json:"url,omitempty"` 260 ProxyURL string `json:"proxy_url,omitempty"` 261 Width int `json:"width,omitempty"` 262 Height int `json:"height,omitempty"` 263} 264 265// MessageEmbedVideo is a part of a MessageEmbed struct. 266type MessageEmbedVideo struct { 267 URL string `json:"url,omitempty"` 268 Width int `json:"width,omitempty"` 269 Height int `json:"height,omitempty"` 270} 271 272// MessageEmbedProvider is a part of a MessageEmbed struct. 273type MessageEmbedProvider struct { 274 URL string `json:"url,omitempty"` 275 Name string `json:"name,omitempty"` 276} 277 278// MessageEmbedAuthor is a part of a MessageEmbed struct. 279type MessageEmbedAuthor struct { 280 URL string `json:"url,omitempty"` 281 Name string `json:"name,omitempty"` 282 IconURL string `json:"icon_url,omitempty"` 283 ProxyIconURL string `json:"proxy_icon_url,omitempty"` 284} 285 286// MessageEmbedField is a part of a MessageEmbed struct. 287type MessageEmbedField struct { 288 Name string `json:"name,omitempty"` 289 Value string `json:"value,omitempty"` 290 Inline bool `json:"inline,omitempty"` 291} 292 293// An MessageEmbed stores data for message embeds. 294type MessageEmbed struct { 295 URL string `json:"url,omitempty"` 296 Type EmbedType `json:"type,omitempty"` 297 Title string `json:"title,omitempty"` 298 Description string `json:"description,omitempty"` 299 Timestamp string `json:"timestamp,omitempty"` 300 Color int `json:"color,omitempty"` 301 Footer *MessageEmbedFooter `json:"footer,omitempty"` 302 Image *MessageEmbedImage `json:"image,omitempty"` 303 Thumbnail *MessageEmbedThumbnail `json:"thumbnail,omitempty"` 304 Video *MessageEmbedVideo `json:"video,omitempty"` 305 Provider *MessageEmbedProvider `json:"provider,omitempty"` 306 Author *MessageEmbedAuthor `json:"author,omitempty"` 307 Fields []*MessageEmbedField `json:"fields,omitempty"` 308} 309 310// EmbedType is the type of embed 311// https://discord.com/developers/docs/resources/channel#embed-object-embed-types 312type EmbedType string 313 314// Block of valid EmbedTypes 315const ( 316 EmbedTypeRich EmbedType = "rich" 317 EmbedTypeImage EmbedType = "image" 318 EmbedTypeVideo EmbedType = "video" 319 EmbedTypeGifv EmbedType = "gifv" 320 EmbedTypeArticle EmbedType = "article" 321 EmbedTypeLink EmbedType = "link" 322) 323 324// MessageReactions holds a reactions object for a message. 325type MessageReactions struct { 326 Count int `json:"count"` 327 Me bool `json:"me"` 328 Emoji *Emoji `json:"emoji"` 329} 330 331// MessageActivity is sent with Rich Presence-related chat embeds 332type MessageActivity struct { 333 Type MessageActivityType `json:"type"` 334 PartyID string `json:"party_id"` 335} 336 337// MessageActivityType is the type of message activity 338type MessageActivityType int 339 340// Constants for the different types of Message Activity 341const ( 342 MessageActivityTypeJoin MessageActivityType = iota + 1 343 MessageActivityTypeSpectate 344 MessageActivityTypeListen 345 MessageActivityTypeJoinRequest 346) 347 348// MessageFlag describes an extra feature of the message 349type MessageFlag int 350 351// Constants for the different bit offsets of Message Flags 352const ( 353 // This message has been published to subscribed channels (via Channel Following) 354 MessageFlagCrossposted MessageFlag = 1 << iota 355 // This message originated from a message in another channel (via Channel Following) 356 MessageFlagIsCrosspost 357 // Do not include any embeds when serializing this message 358 MessageFlagSuppressEmbeds 359) 360 361// MessageApplication is sent with Rich Presence-related chat embeds 362type MessageApplication struct { 363 ID string `json:"id"` 364 CoverImage string `json:"cover_image"` 365 Description string `json:"description"` 366 Icon string `json:"icon"` 367 Name string `json:"name"` 368} 369 370// MessageReference contains reference data sent with crossposted messages 371type MessageReference struct { 372 MessageID string `json:"message_id"` 373 ChannelID string `json:"channel_id"` 374 GuildID string `json:"guild_id,omitempty"` 375} 376 377// Reference returns MessageReference of given message 378func (m *Message) Reference() *MessageReference { 379 return &MessageReference{ 380 GuildID: m.GuildID, 381 ChannelID: m.ChannelID, 382 MessageID: m.ID, 383 } 384} 385 386// ContentWithMentionsReplaced will replace all @<id> mentions with the 387// username of the mention. 388func (m *Message) ContentWithMentionsReplaced() (content string) { 389 content = m.Content 390 391 for _, user := range m.Mentions { 392 content = strings.NewReplacer( 393 "<@"+user.ID+">", "@"+user.Username, 394 "<@!"+user.ID+">", "@"+user.Username, 395 ).Replace(content) 396 } 397 return 398} 399 400var patternChannels = regexp.MustCompile("<#[^>]*>") 401 402// ContentWithMoreMentionsReplaced will replace all @<id> mentions with the 403// username of the mention, but also role IDs and more. 404func (m *Message) ContentWithMoreMentionsReplaced(s *Session) (content string, err error) { 405 content = m.Content 406 407 if !s.StateEnabled { 408 content = m.ContentWithMentionsReplaced() 409 return 410 } 411 412 channel, err := s.State.Channel(m.ChannelID) 413 if err != nil { 414 content = m.ContentWithMentionsReplaced() 415 return 416 } 417 418 for _, user := range m.Mentions { 419 nick := user.Username 420 421 member, err := s.State.Member(channel.GuildID, user.ID) 422 if err == nil && member.Nick != "" { 423 nick = member.Nick 424 } 425 426 content = strings.NewReplacer( 427 "<@"+user.ID+">", "@"+user.Username, 428 "<@!"+user.ID+">", "@"+nick, 429 ).Replace(content) 430 } 431 for _, roleID := range m.MentionRoles { 432 role, err := s.State.Role(channel.GuildID, roleID) 433 if err != nil || !role.Mentionable { 434 continue 435 } 436 437 content = strings.Replace(content, "<@&"+role.ID+">", "@"+role.Name, -1) 438 } 439 440 content = patternChannels.ReplaceAllStringFunc(content, func(mention string) string { 441 channel, err := s.State.Channel(mention[2 : len(mention)-1]) 442 if err != nil || channel.Type == ChannelTypeGuildVoice { 443 return mention 444 } 445 446 return "#" + channel.Name 447 }) 448 return 449} 450