diff --git a/api/channel.go b/api/channel.go index 853538d..4527785 100644 --- a/api/channel.go +++ b/api/channel.go @@ -8,80 +8,13 @@ import ( const EndpointChannels = Endpoint + "channels/" -type Channel struct { - ID discord.Snowflake `json:"id,string"` - Type ChannelType `json:"type"` - - // Fields below may not appear - - GuildID discord.Snowflake `json:"guild_id,string,omitempty"` - - Position int `json:"position,omitempty"` - Name string `json:"name,omitempty"` // 2-100 chars - Topic string `json:"topic,omitempty"` // 0-1024 chars - NSFW bool `json:"nsfw"` - - Icon discord.Hash `json:"icon,omitempty"` - - // Direct Messaging fields - DMOwnerID discord.Snowflake `json:"owner_id,omitempty"` - DMRecipients []User `json:"recipients,omitempty"` - - // AppID of the group DM creator if it's bot-created - AppID discord.Snowflake `json:"application_id,omitempty"` - - // ID of the category the channel is in, if any. - CategoryID discord.Snowflake `json:"parent_id,omitempty"` - - LastPinTime discord.Timestamp `json:"last_pin_timestamp,omitempty"` - - // Explicit permission overrides for members and roles. - Permissions []Overwrite `json:"permission_overwrites,omitempty"` - // ID of the last message, may not point to a valid one. - LastMessageID discord.Snowflake `json:"last_message_id,omitempty"` - - // Slow mode duration. Bots and people with "manage_messages" or - // "manage_channel" permissions are unaffected. - UserRateLimit discord.Seconds `json:"rate_limit_per_user,omitempty"` - - // Voice, so GuildVoice only - VoiceBitrate int `json:"bitrate,omitempty"` - VoiceUserLimit int `json:"user_limit,omitempty"` -} - -type ChannelType uint8 - -const ( - GuildText ChannelType = iota - DirectMessage - GuildVoice - GroupDM - GuildCategory - GuildNews - GuildStore -) - -type Overwrite struct { - ID discord.Snowflake `json:"id,omitempty"` - Type OverwriteType `json:"type"` - Allow uint64 `json:"allow"` - Deny uint64 `json:"deny"` -} - -type OverwriteType string - -const ( - OverwriteRole OverwriteType = "role" - OverwriteMember OverwriteType = "member" -) - type ChannelModifier struct { ChannelID discord.Snowflake `json:"id,omitempty"` // All types - Name string `json:"name,omitempty"` - Position json.OptionInt `json:"position,omitempty"` - Permissions []Overwrite `json:"permission_overwrites,omitempty"` + Name string `json:"name,omitempty"` + Position json.OptionInt `json:"position,omitempty"` + Permissions []discord.Overwrite `json:"permission_overwrites,omitempty"` // Text only Topic json.OptionString `json:"topic,omitempty"` @@ -100,8 +33,8 @@ type ChannelModifier struct { ParentID discord.Snowflake `json:"parent_id,omitempty"` } -func (c *Client) Channel(channelID discord.Snowflake) (*Channel, error) { - var channel *Channel +func (c *Client) Channel(channelID discord.Snowflake) (*discord.Channel, error) { + var channel *discord.Channel return channel, c.RequestJSON(&channel, "POST", EndpointChannels+channelID.String()) @@ -119,7 +52,7 @@ func (c *Client) DeleteChannel(channelID discord.Snowflake) error { } func (c *Client) EditChannelPermission(channelID discord.Snowflake, - overwrite Overwrite) error { + overwrite discord.Overwrite) error { url := EndpointChannels + channelID.String() + "/permissions/" + overwrite.ID.String() @@ -143,9 +76,9 @@ func (c *Client) Typing(channelID discord.Snowflake) error { } func (c *Client) PinnedMessages( - channelID discord.Snowflake) ([]Message, error) { + channelID discord.Snowflake) ([]discord.Message, error) { - var pinned []Message + var pinned []discord.Message return pinned, c.RequestJSON(&pinned, "GET", EndpointChannels+channelID.String()+"/pins") } diff --git a/api/emoji.go b/api/emoji.go index 7e13455..65abc09 100644 --- a/api/emoji.go +++ b/api/emoji.go @@ -1,25 +1,9 @@ package api import ( - "strings" - "git.sr.ht/~diamondburned/arikawa/discord" ) -type Emoji struct { - ID discord.Snowflake `json:"id"` // 0 for Unicode emojis - Name string `json:"name"` - - // These fields are optional - - RoleIDs []discord.Snowflake `json:"roles,omitempty"` - User User `json:"user,omitempty"` - - RequireColons bool `json:"require_colons,omitempty"` - Managed bool `json:"managed,omitempty"` - Animated bool `json:"animated,omitempty"` -} - // EmojiAPI is a special format that the API wants. type EmojiAPI = string @@ -31,28 +15,20 @@ func FormatEmojiAPI(id discord.Snowflake, name string) string { return id.String() + ":" + name } -// APIString returns a string usable for sending over to the API. -func (e Emoji) APIString() EmojiAPI { - if e.ID == 0 { - return e.Name // is unicode - } +func (c *Client) Emojis( + guildID discord.Snowflake) ([]discord.Emoji, error) { - return e.ID.String() + ":" + e.Name + var emjs []discord.Emoji + return emjs, c.RequestJSON(&emjs, "GET", + EndpointGuilds+guildID.String()+"/emojis") } -// String formats the string like how the client does. -func (e Emoji) String() string { - if e.ID == 0 { - return e.Name - } +func (c *Client) Emoji( + guildID, emojiID discord.Snowflake) (*discord.Emoji, error) { - var parts = []string{ - "", e.Name, e.ID.String(), - } - - if e.Animated { - parts[0] = "a" - } - - return "<" + strings.Join(parts, ":") + ">" + var emj *discord.Emoji + return emj, c.RequestJSON(&emj, "GET", + EndpointGuilds+guildID.String()+"/emojis/"+emojiID.String()) } + +// func (c *Client) CreateEmoji() diff --git a/api/errors.go b/api/errors.go deleted file mode 100644 index a662ed0..0000000 --- a/api/errors.go +++ /dev/null @@ -1,22 +0,0 @@ -package api - -import ( - "fmt" -) - -type ErrOverbound struct { - Count int - Max int - - Thing string -} - -var _ error = (*ErrOverbound)(nil) - -func (e ErrOverbound) Error() string { - if e.Thing == "" { - return fmt.Sprintf("Overbound error: %d > %d", e.Count, e.Max) - } - - return fmt.Sprintf(e.Thing+" overbound: %d > %d", e.Count, e.Max) -} diff --git a/api/guild.go b/api/guild.go index a02054d..9c21f33 100644 --- a/api/guild.go +++ b/api/guild.go @@ -1,7 +1,3 @@ package api const EndpointGuilds = Endpoint + "guilds/" - -type Guild struct{} - -type GuildMember struct{} diff --git a/api/invite.go b/api/invite.go index 0d32b73..998807f 100644 --- a/api/invite.go +++ b/api/invite.go @@ -1,13 +1,16 @@ package api -import "git.sr.ht/~diamondburned/arikawa/discord" +import ( + "git.sr.ht/~diamondburned/arikawa/discord" +) const EndpointInvites = Endpoint + "invites/" +// Still unsure what this is type MetaInvite struct { - Inviter User `json:"user"` - Uses uint `json:"uses"` - MaxUses uint `json:"max_uses"` + Inviter discord.User `json:"user"` + Uses uint `json:"uses"` + MaxUses uint `json:"max_uses"` MaxAge discord.Seconds `json:"max_age"` @@ -15,28 +18,7 @@ type MetaInvite struct { CreatedAt discord.Timestamp `json:"created_at"` } -type Invite struct { - Code string `json:"code"` - Channel Channel `json:"channel"` // partial - Guild *Guild `json:"guild,omitempty"` // partial - - ApproxMembers uint `json:"approximate_members_count,omitempty"` - - Target *User `json:"target_user,omitempty"` // partial - TargetType InviteUserType `json:"target_user_type,omitempty"` - - // Only available if Target is - ApproxPresences uint `json:"approximate_presence_count,omitempty"` -} - -type InviteUserType uint8 - -const ( - InviteNormalUser InviteUserType = iota - InviteUserStream -) - -func (c *Client) Invite(code string) (*Invite, error) { +func (c *Client) Invite(code string) (*discord.Invite, error) { var params struct { WithCounts bool `json:"with_counts,omitempty"` } @@ -44,13 +26,13 @@ func (c *Client) Invite(code string) (*Invite, error) { // Nothing says I can't! params.WithCounts = true - var inv *Invite + var inv *discord.Invite return inv, c.RequestJSON(&inv, "GET", EndpointInvites+code) } // Invites is only for guild channels. -func (c *Client) Invites(channelID discord.Snowflake) ([]Invite, error) { - var invs []Invite +func (c *Client) Invites(channelID discord.Snowflake) ([]discord.Invite, error) { + var invs []discord.Invite return invs, c.RequestJSON(&invs, "GET", EndpointChannels+channelID.String()+"/invites") } @@ -63,7 +45,7 @@ func (c *Client) Invites(channelID discord.Snowflake) ([]Invite, error) { // temporary membership. Unique, if true, tries not to reuse a similar invite, // useful for creating unique one time use invites. func (c *Client) CreateInvite(channelID discord.Snowflake, - maxAge discord.Seconds, maxUses uint, temp, unique bool) (*Invite, error) { + maxAge discord.Seconds, maxUses uint, temp, unique bool) (*discord.Invite, error) { var params struct { MaxAge uint `json:"max_age"` @@ -77,14 +59,14 @@ func (c *Client) CreateInvite(channelID discord.Snowflake, params.Temporary = temp params.Unique = unique - var inv *Invite + var inv *discord.Invite return inv, c.RequestJSON(&inv, "POST", EndpointChannels+channelID.String()+"/invites") } // DeleteInvite requires either MANAGE_CHANNELS on the target channel, or // MANAGE_GUILD to remove any invite in the guild. -func (c *Client) DeleteInvite(code string) (*Invite, error) { - var inv *Invite +func (c *Client) DeleteInvite(code string) (*discord.Invite, error) { + var inv *discord.Invite return inv, c.RequestJSON(&inv, "DELETE", EndpointInvites+code) } diff --git a/api/message.go b/api/message.go index 0ca608f..c02abaf 100644 --- a/api/message.go +++ b/api/message.go @@ -8,177 +8,30 @@ import ( "github.com/pkg/errors" ) -type Message struct { - ID discord.Snowflake `json:"id"` - Type MessageType `json:"type"` - ChannelID discord.Snowflake `json:"channel_id"` - GuildID discord.Snowflake `json:"guild_id,omitempty"` - - // The author object follows the structure of the user object, but is only - // a valid user in the case where the message is generated by a user or bot - // user. If the message is generated by a webhook, the author object - // corresponds to the webhook's id, username, and avatar. You can tell if a - // message is generated by a webhook by checking for the webhook_id on the - // message object. - Author User `json:"author"` - - // The member object exists in MESSAGE_CREATE and MESSAGE_UPDATE - // events from text-based guild channels. - Member *GuildMember `json:"member,omitempty"` - - Content string `json:"content"` - - Timestamp discord.Timestamp `json:"timestamp,omitempty"` - EditedTimestamp *discord.Timestamp `json:"edited_timestamp,omitempty"` - - TTS bool `json:"tts"` - Pinned bool `json:"pinned"` - - // The user objects in the mentions array will only have the partial - // member field present in MESSAGE_CREATE and MESSAGE_UPDATE events from - // text-based guild channels. - Mentions []GuildUser `json:"mentions"` - - MentionRoleIDs []discord.Snowflake `json:"mention_roles"` - MentionEveryone bool `json:"mention_everyone"` - - // Not all channel mentions in a message will appear in mention_channels. - MentionChannels []ChannelMention `json:"mention_channels,omitempty"` - - Attachments []Attachment `json:"attachments"` - Embeds []Embed `json:"embeds"` - - Reactions []Reaction `json:"reaction,omitempty"` - - // Used for validating a message was sent - Nonce string `json:"nonce,omitempty"` - - WebhookID discord.Snowflake `json:"webhook_id,omitempty"` - Activity *MessageActivity `json:"activity,omitempty"` - Application *MessageApplication `json:"application,omitempty"` - Reference *MessageReference `json:"message_reference,omitempty"` - Flags MessageFlags `json:"flags"` -} - -type MessageType uint8 - -const ( - DefaultMessage MessageType = iota - RecipientAddMessage - RecipientRemoveMessage - CallMessage - ChannelNameChangeMessage - ChannelIconChangeMessage - ChannelPinnedMessage - GuildMemberJoinMessage - NitroBoostMessage - NitroTier1Message - NitroTier2Message - NitroTier3Message - ChannelFollowAddMessage -) - -type MessageFlags uint8 - -const ( - CrosspostedMessage MessageFlags = 1 << iota - MessageIsCrosspost - SuppressEmbeds - SourceMessageDeleted - UrgentMessage -) - -type ChannelMention struct { - ChannelID discord.Snowflake `json:"id"` - GuildID discord.Snowflake `json:"guild_id"` - ChannelType ChannelType `json:"type"` - ChannelName string `json:"name"` -} - -type GuildUser struct { - User - Member *GuildMember `json:"member,omitempty"` -} - -// - -type MessageActivity struct { - Type MessageActivityType `json:"type"` - - // From a Rich Presence event - PartyID string `json:"party_id,omitempty"` -} - -type MessageActivityType uint8 - -const ( - JoinMessage MessageActivityType = iota + 1 - SpectateMessage - ListenMessage - JoinRequestMessage -) - -// - -type MessageApplication struct { - ID discord.Snowflake `json:"id"` - CoverID string `json:"cover_image,omitempty"` - Description string `json:"description"` - Icon string `json:"icon"` - Name string `json:"name"` -} - -// - -type MessageReference struct { - ChannelID discord.Snowflake `json:"channel_id"` - - // Field might not be provided - MessageID discord.Snowflake `json:"message_id,omitempty"` - GuildID discord.Snowflake `json:"guild_id,omitempty"` -} - -// - -type Attachment struct { - ID discord.Snowflake `json:"id"` - Filename string `json:"filename"` - Size uint64 `json:"size"` - - URL discord.URL `json:"url"` - Proxy discord.URL `json:"proxy_url"` - - // Only if Image - Height uint `json:"height,omitempty"` - Width uint `json:"width,omitempty"` -} - -// - -func (c *Client) Messages( - channelID discord.Snowflake, limit uint) ([]Message, error) { +func (c *Client) Messages(channelID discord.Snowflake, + limit uint) ([]discord.Message, error) { return c.messages(channelID, limit, nil) } -func (c *Client) MessagesAround( - channelID, around discord.Snowflake, limit uint) ([]Message, error) { +func (c *Client) MessagesAround(channelID, around discord.Snowflake, + limit uint) ([]discord.Message, error) { return c.messages(channelID, limit, map[string]interface{}{ "around": around, }) } -func (c *Client) MessagesBefore( - channelID, before discord.Snowflake, limit uint) ([]Message, error) { +func (c *Client) MessagesBefore(channelID, before discord.Snowflake, + limit uint) ([]discord.Message, error) { return c.messages(channelID, limit, map[string]interface{}{ "before": before, }) } -func (c *Client) MessagesAfter( - channelID, after discord.Snowflake, limit uint) ([]Message, error) { +func (c *Client) MessagesAfter(channelID, after discord.Snowflake, + limit uint) ([]discord.Message, error) { return c.messages(channelID, limit, map[string]interface{}{ "after": after, @@ -186,7 +39,7 @@ func (c *Client) MessagesAfter( } func (c *Client) messages(channelID discord.Snowflake, - limit uint, body map[string]interface{}) ([]Message, error) { + limit uint, body map[string]interface{}) ([]discord.Message, error) { if body == nil { body = map[string]interface{}{} @@ -201,21 +54,21 @@ func (c *Client) messages(channelID discord.Snowflake, body["limit"] = limit - var msgs []Message + var msgs []discord.Message return msgs, c.RequestJSON(&msgs, "GET", EndpointChannels+channelID.String(), httputil.WithJSONBody(c, body)) } func (c *Client) Message( - channelID, messageID discord.Snowflake) (*Message, error) { + channelID, messageID discord.Snowflake) (*discord.Message, error) { - var msg *Message + var msg *discord.Message return msg, c.RequestJSON(&msg, "GET", EndpointChannels+channelID.String()+"/messages/"+messageID.String()) } func (c *Client) SendMessage(channelID discord.Snowflake, - content string, embed *Embed) (*Message, error) { + content string, embed *discord.Embed) (*discord.Message, error) { return c.SendMessageComplex(channelID, SendMessageData{ Content: content, @@ -224,7 +77,7 @@ func (c *Client) SendMessage(channelID discord.Snowflake, } func (c *Client) SendMessageComplex(channelID discord.Snowflake, - data SendMessageData) (*Message, error) { + data SendMessageData) (*discord.Message, error) { if data.Embed != nil { if err := data.Embed.Validate(); err != nil { @@ -233,7 +86,7 @@ func (c *Client) SendMessageComplex(channelID discord.Snowflake, } var URL = EndpointChannels + channelID.String() - var msg *Message + var msg *discord.Message if len(data.Files) == 0 { // No files, no need for streaming @@ -256,21 +109,22 @@ func (c *Client) SendMessageComplex(channelID discord.Snowflake, } func (c *Client) EditMessage(channelID, messageID discord.Snowflake, - content string, embed *Embed, suppressEmbeds bool) (*Message, error) { + content string, embed *discord.Embed, suppressEmbeds bool, +) (*discord.Message, error) { var param struct { - Content string `json:"content,omitempty"` - Embed *Embed `json:"embed,omitempty"` - Flags MessageFlags `json:"flags,omitempty"` + Content string `json:"content,omitempty"` + Embed *discord.Embed `json:"embed,omitempty"` + Flags discord.MessageFlags `json:"flags,omitempty"` } param.Content = content param.Embed = embed if suppressEmbeds { - param.Flags = SuppressEmbeds + param.Flags = discord.SuppressEmbeds } - var msg *Message + var msg *discord.Message return msg, c.RequestJSON( &msg, "PATCH", EndpointChannels+channelID.String()+"/messages/"+messageID.String(), diff --git a/api/message_reaction.go b/api/message_reaction.go index 27824f7..a7ad806 100644 --- a/api/message_reaction.go +++ b/api/message_reaction.go @@ -5,12 +5,6 @@ import ( "git.sr.ht/~diamondburned/arikawa/httputil" ) -type Reaction struct { - Count int `json:"count"` - Me bool `json:"me"` // for current user - Emoji Emoji `json:"emoji"` -} - // React adds a reaction to the message. This requires READ_MESSAGE_HISTORY (and // additionally ADD_REACTIONS) to react. func (c *Client) React(chID, msgID discord.Snowflake, @@ -23,15 +17,16 @@ func (c *Client) React(chID, msgID discord.Snowflake, } func (c *Client) Reactions(chID, msgID discord.Snowflake, - limit uint, emoji EmojiAPI) ([]User, error) { + limit uint, emoji EmojiAPI) ([]discord.User, error) { return c.ReactionRange(chID, msgID, 0, 0, limit, emoji) } // ReactionRange get users before and after IDs. Before, after, and limit are // optional. -func (c *Client) ReactionRange(chID, msgID, before, after discord.Snowflake, - limit uint, emoji EmojiAPI) ([]User, error) { +func (c *Client) ReactionRange( + chID, msgID, before, after discord.Snowflake, + limit uint, emoji EmojiAPI) ([]discord.User, error) { if limit == 0 { limit = 25 @@ -48,7 +43,7 @@ func (c *Client) ReactionRange(chID, msgID, before, after discord.Snowflake, Limit uint `json:"limit"` } - var users []User + var users []discord.User var msgURL = EndpointChannels + chID.String() + "/messages/" + msgID.String() + "/reactions/" + emoji diff --git a/api/message_send.go b/api/message_send.go index c7291ab..4f8463f 100644 --- a/api/message_send.go +++ b/api/message_send.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" + "git.sr.ht/~diamondburned/arikawa/discord" "git.sr.ht/~diamondburned/arikawa/json" "github.com/pkg/errors" ) @@ -17,7 +18,8 @@ type SendMessageData struct { Content string `json:"content"` Nonce string `json:"nonce"` TTS bool `json:"tts"` - Embed *Embed `json:"embed"` + + Embed *discord.Embed `json:"embed"` Files []SendMessageFile `json:"-"` } @@ -53,7 +55,7 @@ func (data *SendMessageData) WriteMultipart(c json.Driver, w io.Writer) error { for i, file := range data.Files { h := textproto.MIMEHeader{} h.Set("Content-Disposition", fmt.Sprintf( - `form-data; name="file%s"; filename="%s"`, + `form-data; name="file%d"; filename="%s"`, i, quoteEscaper.Replace(file.Name), )) diff --git a/discord/channel.go b/discord/channel.go new file mode 100644 index 0000000..404bd50 --- /dev/null +++ b/discord/channel.go @@ -0,0 +1,68 @@ +package discord + +type Channel struct { + ID Snowflake `json:"id,string"` + Type ChannelType `json:"type"` + + // Fields below may not appear + + GuildID Snowflake `json:"guild_id,string,omitempty"` + + Position int `json:"position,omitempty"` + Name string `json:"name,omitempty"` // 2-100 chars + Topic string `json:"topic,omitempty"` // 0-1024 chars + NSFW bool `json:"nsfw"` + + Icon Hash `json:"icon,omitempty"` + + // Direct Messaging fields + DMOwnerID Snowflake `json:"owner_id,omitempty"` + DMRecipients []User `json:"recipients,omitempty"` + + // AppID of the group DM creator if it's bot-created + AppID Snowflake `json:"application_id,omitempty"` + + // ID of the category the channel is in, if any. + CategoryID Snowflake `json:"parent_id,omitempty"` + + LastPinTime Timestamp `json:"last_pin_timestamp,omitempty"` + + // Explicit permission overrides for members and roles. + Permissions []Overwrite `json:"permission_overwrites,omitempty"` + // ID of the last message, may not point to a valid one. + LastMessageID Snowflake `json:"last_message_id,omitempty"` + + // Slow mode duration. Bots and people with "manage_messages" or + // "manage_channel" permissions are unaffected. + UserRateLimit Seconds `json:"rate_limit_per_user,omitempty"` + + // Voice, so GuildVoice only + VoiceBitrate int `json:"bitrate,omitempty"` + VoiceUserLimit int `json:"user_limit,omitempty"` +} + +type ChannelType uint8 + +const ( + GuildText ChannelType = iota + DirectMessage + GuildVoice + GroupDM + GuildCategory + GuildNews + GuildStore +) + +type Overwrite struct { + ID Snowflake `json:"id,omitempty"` + Type OverwriteType `json:"type"` + Allow uint64 `json:"allow"` + Deny uint64 `json:"deny"` +} + +type OverwriteType string + +const ( + OverwriteRole OverwriteType = "role" + OverwriteMember OverwriteType = "member" +) diff --git a/discord/color.go b/discord/color.go deleted file mode 100644 index 4eb30a4..0000000 --- a/discord/color.go +++ /dev/null @@ -1,5 +0,0 @@ -package discord - -type Color uint - -const DefaultColor Color = 0x303030 diff --git a/discord/emoji.go b/discord/emoji.go new file mode 100644 index 0000000..6370864 --- /dev/null +++ b/discord/emoji.go @@ -0,0 +1,43 @@ +package discord + +import "strings" + +type Emoji struct { + ID Snowflake `json:"id"` // 0 for Unicode emojis + Name string `json:"name"` + + // These fields are optional + + RoleIDs []Snowflake `json:"roles,omitempty"` + User User `json:"user,omitempty"` + + RequireColons bool `json:"require_colons,omitempty"` + Managed bool `json:"managed,omitempty"` + Animated bool `json:"animated,omitempty"` +} + +// APIString returns a string usable for sending over to the API. +func (e Emoji) APIString() string { + if e.ID == 0 { + return e.Name // is unicode + } + + return e.ID.String() + ":" + e.Name +} + +// String formats the string like how the client does. +func (e Emoji) String() string { + if e.ID == 0 { + return e.Name + } + + var parts = []string{ + "", e.Name, e.ID.String(), + } + + if e.Animated { + parts[0] = "a" + } + + return "<" + strings.Join(parts, ":") + ">" +} diff --git a/discord/guild.go b/discord/guild.go new file mode 100644 index 0000000..dd13054 --- /dev/null +++ b/discord/guild.go @@ -0,0 +1,5 @@ +package discord + +type Guild struct{} + +type GuildMember struct{} diff --git a/discord/image.go b/discord/image.go deleted file mode 100644 index 82aa539..0000000 --- a/discord/image.go +++ /dev/null @@ -1,22 +0,0 @@ -package discord - -type Image string - -const ( - ImageBaseURL = "https://cdn.discordapp.com/" -) - -type ImageType uint8 - -const ( - CustomEmoji ImageType = iota - GuildIcon - GuildSplash - GuildBanner - DefaultUserAvatar - UserAvatar - ApplicationIcon - ApplicationAsset - AchievementIcon - TeamIcon -) diff --git a/discord/invite.go b/discord/invite.go new file mode 100644 index 0000000..8b0d21f --- /dev/null +++ b/discord/invite.go @@ -0,0 +1,22 @@ +package discord + +type Invite struct { + Code string `json:"code"` + Channel Channel `json:"channel"` // partial + Guild *Guild `json:"guild,omitempty"` // partial + + ApproxMembers uint `json:"approximate_members_count,omitempty"` + + Target *User `json:"target_user,omitempty"` // partial + TargetType InviteUserType `json:"target_user_type,omitempty"` + + // Only available if Target is + ApproxPresences uint `json:"approximate_presence_count,omitempty"` +} + +type InviteUserType uint8 + +const ( + InviteNormalUser InviteUserType = iota + InviteUserStream +) diff --git a/discord/message.go b/discord/message.go new file mode 100644 index 0000000..a53af3d --- /dev/null +++ b/discord/message.go @@ -0,0 +1,154 @@ +package discord + +type Message struct { + ID Snowflake `json:"id"` + Type MessageType `json:"type"` + ChannelID Snowflake `json:"channel_id"` + GuildID Snowflake `json:"guild_id,omitempty"` + + // The author object follows the structure of the user object, but is only + // a valid user in the case where the message is generated by a user or bot + // user. If the message is generated by a webhook, the author object + // corresponds to the webhook's id, username, and avatar. You can tell if a + // message is generated by a webhook by checking for the webhook_id on the + // message object. + Author User `json:"author"` + + // The member object exists in MESSAGE_CREATE and MESSAGE_UPDATE + // events from text-based guild channels. + Member *GuildMember `json:"member,omitempty"` + + Content string `json:"content"` + + Timestamp Timestamp `json:"timestamp,omitempty"` + EditedTimestamp *Timestamp `json:"edited_timestamp,omitempty"` + + TTS bool `json:"tts"` + Pinned bool `json:"pinned"` + + // The user objects in the mentions array will only have the partial + // member field present in MESSAGE_CREATE and MESSAGE_UPDATE events from + // text-based guild channels. + Mentions []GuildUser `json:"mentions"` + + MentionRoleIDs []Snowflake `json:"mention_roles"` + MentionEveryone bool `json:"mention_everyone"` + + // Not all channel mentions in a message will appear in mention_channels. + MentionChannels []ChannelMention `json:"mention_channels,omitempty"` + + Attachments []Attachment `json:"attachments"` + Embeds []Embed `json:"embeds"` + + Reactions []Reaction `json:"reaction,omitempty"` + + // Used for validating a message was sent + Nonce string `json:"nonce,omitempty"` + + WebhookID Snowflake `json:"webhook_id,omitempty"` + Activity *MessageActivity `json:"activity,omitempty"` + Application *MessageApplication `json:"application,omitempty"` + Reference *MessageReference `json:"message_reference,omitempty"` + Flags MessageFlags `json:"flags"` +} + +type MessageType uint8 + +const ( + DefaultMessage MessageType = iota + RecipientAddMessage + RecipientRemoveMessage + CallMessage + ChannelNameChangeMessage + ChannelIconChangeMessage + ChannelPinnedMessage + GuildMemberJoinMessage + NitroBoostMessage + NitroTier1Message + NitroTier2Message + NitroTier3Message + ChannelFollowAddMessage +) + +type MessageFlags uint8 + +const ( + CrosspostedMessage MessageFlags = 1 << iota + MessageIsCrosspost + SuppressEmbeds + SourceMessageDeleted + UrgentMessage +) + +type ChannelMention struct { + ChannelID Snowflake `json:"id"` + GuildID Snowflake `json:"guild_id"` + ChannelType ChannelType `json:"type"` + ChannelName string `json:"name"` +} + +type GuildUser struct { + User + Member *GuildMember `json:"member,omitempty"` +} + +// + +type MessageActivity struct { + Type MessageActivityType `json:"type"` + + // From a Rich Presence event + PartyID string `json:"party_id,omitempty"` +} + +type MessageActivityType uint8 + +const ( + JoinMessage MessageActivityType = iota + 1 + SpectateMessage + ListenMessage + JoinRequestMessage +) + +// + +type MessageApplication struct { + ID Snowflake `json:"id"` + CoverID string `json:"cover_image,omitempty"` + Description string `json:"description"` + Icon string `json:"icon"` + Name string `json:"name"` +} + +// + +type MessageReference struct { + ChannelID Snowflake `json:"channel_id"` + + // Field might not be provided + MessageID Snowflake `json:"message_id,omitempty"` + GuildID Snowflake `json:"guild_id,omitempty"` +} + +// + +type Attachment struct { + ID Snowflake `json:"id"` + Filename string `json:"filename"` + Size uint64 `json:"size"` + + URL URL `json:"url"` + Proxy URL `json:"proxy_url"` + + // Only if Image + Height uint `json:"height,omitempty"` + Width uint `json:"width,omitempty"` +} + +// + +type Reaction struct { + Count int `json:"count"` + Me bool `json:"me"` // for current user + Emoji Emoji `json:"emoji"` +} diff --git a/api/message_embed.go b/discord/message_embed.go similarity index 64% rename from api/message_embed.go rename to discord/message_embed.go index fafaf25..fd846b7 100644 --- a/api/message_embed.go +++ b/discord/message_embed.go @@ -1,20 +1,20 @@ -package api +package discord -import ( - "fmt" +import "fmt" - "git.sr.ht/~diamondburned/arikawa/discord" -) +type Color uint + +const DefaultColor Color = 0x303030 type Embed struct { Title string `json:"title,omitempty"` Type EmbedType `json:"type,omitempty"` Description string `json:"description,omitempty"` - URL discord.URL `json:"url,omitempty"` + URL URL `json:"url,omitempty"` - Timestamp discord.Timestamp `json:"timestamp,omitempty"` - Color discord.Color `json:"color,omitempty"` + Timestamp Timestamp `json:"timestamp,omitempty"` + Color Color `json:"color,omitempty"` Footer *EmbedFooter `json:"footer,omitempty"` Image *EmbedImage `json:"image,omitempty"` @@ -28,17 +28,34 @@ type Embed struct { func NewEmbed() *Embed { return &Embed{ Type: NormalEmbed, - Color: discord.DefaultColor, + Color: DefaultColor, } } +type ErrOverbound struct { + Count int + Max int + + Thing string +} + +var _ error = (*ErrOverbound)(nil) + +func (e ErrOverbound) Error() string { + if e.Thing == "" { + return fmt.Sprintf("Overbound error: %d > %d", e.Count, e.Max) + } + + return fmt.Sprintf(e.Thing+" overbound: %d > %d", e.Count, e.Max) +} + func (e *Embed) Validate() error { if e.Type == "" { e.Type = NormalEmbed } if e.Color == 0 { - e.Color = discord.DefaultColor + e.Color = DefaultColor } if len(e.Title) > 256 { @@ -104,39 +121,39 @@ const ( ) type EmbedFooter struct { - Text string `json:"text"` - Icon discord.URL `json:"icon_url,omitempty"` - ProxyIcon discord.URL `json:"proxy_icon_url,omitempty"` + Text string `json:"text"` + Icon URL `json:"icon_url,omitempty"` + ProxyIcon URL `json:"proxy_icon_url,omitempty"` } type EmbedImage struct { - URL discord.URL `json:"url"` - Proxy discord.URL `json:"proxy_url"` + URL URL `json:"url"` + Proxy URL `json:"proxy_url"` } type EmbedThumbnail struct { - URL discord.URL `json:"url,omitempty"` - Proxy discord.URL `json:"proxy_url,omitempty"` - Height uint `json:"height,omitempty"` - Width uint `json:"width,omitempty"` + URL URL `json:"url,omitempty"` + Proxy URL `json:"proxy_url,omitempty"` + Height uint `json:"height,omitempty"` + Width uint `json:"width,omitempty"` } type EmbedVideo struct { - URL discord.URL `json:"url"` - Height uint `json:"height"` - Width uint `json:"width"` + URL URL `json:"url"` + Height uint `json:"height"` + Width uint `json:"width"` } type EmbedProvider struct { - Name string `json:"name"` - URL discord.URL `json:"url"` + Name string `json:"name"` + URL URL `json:"url"` } type EmbedAuthor struct { - Name string `json:"name,omitempty"` - URL discord.URL `json:"url,omitempty"` - Icon discord.URL `json:"icon_url,omitempty"` - ProxyIcon discord.URL `json:"proxy_icon_url,omitempty"` + Name string `json:"name,omitempty"` + URL URL `json:"url,omitempty"` + Icon URL `json:"icon_url,omitempty"` + ProxyIcon URL `json:"proxy_icon_url,omitempty"` } type EmbedField struct { diff --git a/discord/seconds.go b/discord/seconds.go deleted file mode 100644 index c6a5b62..0000000 --- a/discord/seconds.go +++ /dev/null @@ -1,17 +0,0 @@ -package discord - -import "time" - -type Seconds uint - -func DurationToSeconds(dura time.Duration) Seconds { - return Seconds(dura.Seconds()) -} - -func (s Seconds) String() string { - return s.Duration().String() -} - -func (s Seconds) Duration() time.Duration { - return time.Duration(s) * time.Second -} diff --git a/discord/time.go b/discord/time.go index a9fb60f..3f463ac 100644 --- a/discord/time.go +++ b/discord/time.go @@ -27,3 +27,17 @@ func (t *Timestamp) UnmarshalJSON(v []byte) error { func (t Timestamp) MarshalJSON() ([]byte, error) { return []byte(`"` + time.Time(t).Format(TimestampFormat) + `"`), nil } + +type Seconds uint + +func DurationToSeconds(dura time.Duration) Seconds { + return Seconds(dura.Seconds()) +} + +func (s Seconds) String() string { + return s.Duration().String() +} + +func (s Seconds) Duration() time.Duration { + return time.Duration(s) * time.Second +} diff --git a/api/user.go b/discord/user.go similarity index 72% rename from api/user.go rename to discord/user.go index 7271ba4..bebe4a5 100644 --- a/api/user.go +++ b/discord/user.go @@ -1,12 +1,10 @@ -package api - -import "git.sr.ht/~diamondburned/arikawa/discord" +package discord type User struct { - UserID discord.Snowflake `json:"id"` - Username string `json:"username"` - Discriminator string `json:"discriminator"` - Avatar discord.Hash `json:"avatar"` + UserID Snowflake `json:"id"` + Username string `json:"username"` + Discriminator string `json:"discriminator"` + Avatar Hash `json:"avatar"` // These fields may be omitted diff --git a/go.mod b/go.mod index 2aa6b34..758b08e 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module git.sr.ht/~diamondburned/arikawa go 1.13 -require github.com/pkg/errors v0.8.1 +require ( + github.com/bwmarrin/discordgo v0.20.2 + github.com/pkg/errors v0.8.1 +) diff --git a/go.sum b/go.sum index f29ab35..da95d7e 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,8 @@ +github.com/bwmarrin/discordgo v0.20.2 h1:nA7jiTtqUA9lT93WL2jPjUp8ZTEInRujBdx1C9gkr20= +github.com/bwmarrin/discordgo v0.20.2/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= +github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= diff --git a/httputil/client.go b/httputil/client.go index d8e25b6..c8d73c9 100644 --- a/httputil/client.go +++ b/httputil/client.go @@ -56,6 +56,7 @@ func (c *Client) MeanwhileBody(bodyWriter func(io.Writer) error, func (c *Client) FastRequest( method, url string, opts ...RequestOption) error { + r, err := c.Request(method, url, opts...) if err != nil { return err