github.com/diamondburned/arikawa/v2@v2.1.0/api/send.go (about) 1 package api 2 3 import ( 4 "mime/multipart" 5 6 "github.com/pkg/errors" 7 8 "github.com/diamondburned/arikawa/v2/discord" 9 "github.com/diamondburned/arikawa/v2/utils/json/option" 10 "github.com/diamondburned/arikawa/v2/utils/sendpart" 11 ) 12 13 const AttachmentSpoilerPrefix = "SPOILER_" 14 15 // AllowedMentions is a allowlist of mentions for a message. 16 // 17 // Allowlists 18 // 19 // Roles and Users are slices that act as allowlists for IDs that are allowed 20 // to be mentioned. For example, if only 1 ID is provided in Users, then only 21 // that ID will be parsed in the message. No other IDs will be. The same 22 // example also applies for roles. 23 // 24 // If Parse is an empty slice and both Users and Roles are empty slices, then no 25 // mentions will be parsed. 26 // 27 // Constraints 28 // 29 // If the Users slice is not empty, then Parse must not have AllowUserMention. 30 // Likewise, if the Roles slice is not empty, then Parse must not have 31 // AllowRoleMention. This is because everything provided in Parse will make 32 // Discord parse it completely, meaning they would be mutually exclusive with 33 // Roles and Users. 34 // 35 // https://discord.com/developers/docs/resources/channel#allowed-mentions-object 36 type AllowedMentions struct { 37 // Parse is an array of allowed mention types to parse from the content. 38 Parse []AllowedMentionType `json:"parse"` 39 // Roles is an array of role_ids to mention (Max size of 100). 40 Roles []discord.RoleID `json:"roles,omitempty"` 41 // Users is an array of user_ids to mention (Max size of 100). 42 Users []discord.UserID `json:"users,omitempty"` 43 // RepliedUser is used specifically for inline replies to specify, whether 44 // to mention the author of the message you are replying to or not. 45 RepliedUser option.Bool `json:"replied_user,omitempty"` 46 } 47 48 // AllowedMentionType is a constant that tells Discord what is allowed to parse 49 // from a message content. This can help prevent things such as an 50 // unintentional @everyone mention. 51 type AllowedMentionType string 52 53 // https://discord.com/developers/docs/resources/channel#allowed-mentions-object-allowed-mention-types 54 const ( 55 // AllowRoleMention makes Discord parse roles in the content. 56 AllowRoleMention AllowedMentionType = "roles" 57 // AllowUserMention makes Discord parse user mentions in the content. 58 AllowUserMention AllowedMentionType = "users" 59 // AllowEveryoneMention makes Discord parse @everyone mentions. 60 AllowEveryoneMention AllowedMentionType = "everyone" 61 ) 62 63 // Verify checks the AllowedMentions against constraints mentioned in 64 // AllowedMentions' documentation. This will be called on SendMessageComplex. 65 func (am AllowedMentions) Verify() error { 66 if len(am.Roles) > 100 { 67 return errors.Errorf("roles slice length %d is over 100", len(am.Roles)) 68 } 69 if len(am.Users) > 100 { 70 return errors.Errorf("users slice length %d is over 100", len(am.Users)) 71 } 72 73 for _, allowed := range am.Parse { 74 switch allowed { 75 case AllowRoleMention: 76 if len(am.Roles) > 0 { 77 return errors.New(`parse has AllowRoleMention and Roles slice is not empty`) 78 } 79 case AllowUserMention: 80 if len(am.Users) > 0 { 81 return errors.New(`parse has AllowUserMention and Users slice is not empty`) 82 } 83 } 84 } 85 86 return nil 87 } 88 89 // ErrEmptyMessage is returned if either a SendMessageData or an 90 // ExecuteWebhookData has both an empty Content and no Embed(s). 91 var ErrEmptyMessage = errors.New("message is empty") 92 93 // SendMessageData is the full structure to send a new message to Discord with. 94 type SendMessageData struct { 95 // Content are the message contents (up to 2000 characters). 96 Content string `json:"content,omitempty"` 97 // Nonce is a nonce that can be used for optimistic message sending. 98 Nonce string `json:"nonce,omitempty"` 99 100 // TTS is true if this is a TTS message. 101 TTS bool `json:"tts,omitempty"` 102 // Embed is embedded rich content. 103 Embed *discord.Embed `json:"embed,omitempty"` 104 105 // Files is the list of file attachments to be uploaded. To reference a file 106 // in an embed, use (sendpart.File).AttachmentURI(). 107 Files []sendpart.File `json:"-"` 108 109 // AllowedMentions are the allowed mentions for a message. 110 AllowedMentions *AllowedMentions `json:"allowed_mentions,omitempty"` 111 // Reference allows you to reference another message to create a reply. The 112 // referenced message must be from the same channel. 113 // 114 // Only MessageID is necessary. You may also include a channel_id and 115 // guild_id in the reference. However, they are not necessary, but will be 116 // validated if sent. 117 Reference *discord.MessageReference `json:"message_reference,omitempty"` 118 } 119 120 // NeedsMultipart returns true if the SendMessageData has files. 121 func (data SendMessageData) NeedsMultipart() bool { 122 return len(data.Files) > 0 123 } 124 125 func (data SendMessageData) WriteMultipart(body *multipart.Writer) error { 126 return sendpart.Write(body, data, data.Files) 127 } 128 129 // SendMessageComplex posts a message to a guild text or DM channel. If 130 // operating on a guild channel, this endpoint requires the SEND_MESSAGES 131 // permission to be present on the current user. If the tts field is set to 132 // true, the SEND_TTS_MESSAGES permission is required for the message to be 133 // spoken. Returns a message object. Fires a Message Create Gateway event. 134 // 135 // The maximum request size when sending a message is 8MB. 136 // 137 // This endpoint supports requests with Content-Types of both application/json 138 // and multipart/form-data. You must however use multipart/form-data when 139 // uploading files. Note that when sending multipart/form-data requests the 140 // embed field cannot be used, however you can pass a JSON encoded body as form 141 // value for payload_json, where additional request parameters such as embed 142 // can be set. 143 // 144 // Note that when sending application/json you must send at least one of 145 // content or embed, and when sending multipart/form-data, you must send at 146 // least one of content, embed or file. For a file attachment, the 147 // Content-Disposition subpart header MUST contain a filename parameter. 148 func (c *Client) SendMessageComplex( 149 channelID discord.ChannelID, data SendMessageData) (*discord.Message, error) { 150 151 if data.Content == "" && data.Embed == nil && len(data.Files) == 0 { 152 return nil, ErrEmptyMessage 153 } 154 155 if data.AllowedMentions != nil { 156 if err := data.AllowedMentions.Verify(); err != nil { 157 return nil, errors.Wrap(err, "allowedMentions error") 158 } 159 } 160 161 if data.Embed != nil { 162 if err := data.Embed.Validate(); err != nil { 163 return nil, errors.Wrap(err, "embed error") 164 } 165 } 166 167 var URL = EndpointChannels + channelID.String() + "/messages" 168 var msg *discord.Message 169 return msg, sendpart.POST(c.Client, data, &msg, URL) 170 }