diff --git a/api/guild.go b/api/guild.go index b57def6..8c7ef33 100644 --- a/api/guild.go +++ b/api/guild.go @@ -35,9 +35,9 @@ func (c *Client) CreateGuild(data CreateGuildData) (*discord.Guild, error) { return g, c.RequestJSON(&g, "POST", Endpoint+"guilds", httputil.WithJSONBody(c, data)) } -func (c *Client) Guild(guildID discord.Snowflake) (*discord.Guild, error) { +func (c *Client) Guild(id discord.Snowflake) (*discord.Guild, error) { var g *discord.Guild - return g, c.RequestJSON(&g, "GET", EndpointGuilds+guildID.String()) + return g, c.RequestJSON(&g, "GET", EndpointGuilds+id.String()) } // Guilds returns all guilds, automatically paginating. Be careful, as this @@ -113,8 +113,8 @@ func (c *Client) GuildsRange(before, after discord.Snowflake, limit uint) ([]dis ) } -func (c *Client) LeaveGuild(guildID discord.Snowflake) error { - return c.FastRequest("DELETE", EndpointMe+"/guilds/"+guildID.String()) +func (c *Client) LeaveGuild(id discord.Snowflake) error { + return c.FastRequest("DELETE", EndpointMe+"/guilds/"+id.String()) } // https://discordapp.com/developers/docs/resources/guild#modify-guild-json-params @@ -143,19 +143,17 @@ type ModifyGuildData struct { PreferredLocale json.OptionString `json:"preferred_locale,omitempty"` } -func (c *Client) ModifyGuild( - guildID discord.Snowflake, data ModifyGuildData) (*discord.Guild, error) { - +func (c *Client) ModifyGuild(id discord.Snowflake, data ModifyGuildData) (*discord.Guild, error) { var g *discord.Guild return g, c.RequestJSON( &g, "PATCH", - EndpointGuilds+guildID.String(), + EndpointGuilds+id.String(), httputil.WithJSONBody(c, data), ) } -func (c *Client) DeleteGuild(guildID discord.Snowflake) error { - return c.FastRequest("DELETE", EndpointGuilds+guildID.String()) +func (c *Client) DeleteGuild(id discord.Snowflake) error { + return c.FastRequest("DELETE", EndpointGuilds+id.String()) } // GuildVoiceRegions is the same as /voice, but returns VIP ones as well if @@ -165,6 +163,38 @@ func (c *Client) VoiceRegionsGuild(guildID discord.Snowflake) ([]discord.VoiceRe return vrs, c.RequestJSON(&vrs, "GET", EndpointGuilds+guildID.String()+"/regions") } +// AuditLogData contains query parameters used for AuditLog. All fields are +// optional. +type AuditLogData struct { + // Filter the log for actions made by a user + UserID discord.Snowflake `json:"user_id,omitempty"` + // The type of audit log event + ActionType discord.AuditLogEvent `json:"action_type,omitempty"` + // Filter the log before a certain entry ID + Before discord.Snowflake `json:"before,omitempty"` + // How many entries are returned (default 50, minimum 1, maximum 100) + Limit uint `json:"limit"` +} + +// AuditLog returns an audit log object for the guild. Requires the +// VIEW_AUDIT_LOG permission. +func (c *Client) AuditLog(guildID discord.Snowflake, data AuditLogData) (*discord.AuditLog, error) { + switch { + case data.Limit == 0: + data.Limit = 50 + case data.Limit > 100: + data.Limit = 100 + } + + var audit *discord.AuditLog + + return audit, c.RequestJSON( + &audit, "GET", + EndpointGuilds+guildID.String()+"/audit-logs", + httputil.WithJSONBody(c, data), + ) +} + // Integrations requires MANAGE_GUILD. func (c *Client) Integrations(guildID discord.Snowflake) ([]discord.Integration, error) { var ints []discord.Integration diff --git a/discord/auditlog.go b/discord/auditlog.go new file mode 100644 index 0000000..9fe865a --- /dev/null +++ b/discord/auditlog.go @@ -0,0 +1,230 @@ +package discord + +import "github.com/diamondburned/arikawa/utils/json" + +type AuditLog struct { + // List of webhooks found in the audit log + Webhooks []Webhook `json:"webhooks"` + // List of users found in the audit log + Users []User `json:"users"` + // List of audit log entries + Entries []AuditLogEntries `json:"audit_log_entries"` + // List of partial integration objects, only ID, Name, Type, and Account + Integrations []Integration `json:"integrations"` +} + +type AuditLogEntries struct { + ID Snowflake `json:"id"` + UserID Snowflake `json:"user_id"` + TargetID string `json:"target_id,omitempty"` + + ActionType AuditLogEvent `json:"action_type"` + + Changes []AuditLogChange `json:"changes,omitempty"` + Options AuditEntryInfo `json:"options,omitempty"` + Reason string `json:"reason,omitempty"` +} + +type AuditLogEvent uint8 + +const ( + GuildUpdate AuditLogEvent = 1 + ChannelCreate AuditLogEvent = 10 + ChannelUpdate AuditLogEvent = 11 + ChannelDelete AuditLogEvent = 12 + ChannelOverwriteCreate AuditLogEvent = 13 + ChannelOverwriteUpdate AuditLogEvent = 14 + ChannelOverwriteDelete AuditLogEvent = 15 + MemberKick AuditLogEvent = 20 + MemberPrune AuditLogEvent = 21 + MemberBanAdd AuditLogEvent = 22 + MemberBanRemove AuditLogEvent = 23 + MemberUpdate AuditLogEvent = 24 + MemberRoleUpdate AuditLogEvent = 25 + MemberMove AuditLogEvent = 26 + MemberDisconnect AuditLogEvent = 27 + BotAdd AuditLogEvent = 28 + RoleCreate AuditLogEvent = 30 + RoleUpdate AuditLogEvent = 31 + RoleDelete AuditLogEvent = 32 + InviteCreate AuditLogEvent = 40 + InviteUpdate AuditLogEvent = 41 + InviteDelete AuditLogEvent = 42 + WebhookCreate AuditLogEvent = 50 + WebhookUpdate AuditLogEvent = 51 + WebhookDelete AuditLogEvent = 52 + EmojiCreate AuditLogEvent = 60 + EmojiUpdate AuditLogEvent = 61 + EmojiDelete AuditLogEvent = 62 + MessageDelete AuditLogEvent = 72 + MessageBulkDelete AuditLogEvent = 73 + MessagePin AuditLogEvent = 74 + MessageUnpin AuditLogEvent = 75 + IntegrationCreate AuditLogEvent = 80 + IntegrationUpdate AuditLogEvent = 81 + IntegrationDelete AuditLogEvent = 82 +) + +type AuditEntryInfo struct { + // MEMBER_PRUNE + DeleteMemberDays string `json:"delete_member_days,omitempty"` + // MEMBER_PRUNE + MembersRemoved string `json:"members_removed,omitempty"` + // MEMBER_MOVE & MESSAGE_PIN & MESSAGE_UNPIN & MESSAGE_DELETE + ChannelID Snowflake `json:"channel_id,omitempty"` + // MESSAGE_PIN & MESSAGE_UNPIN + MessageID Snowflake `json:"message_id,omitempty"` + // MESSAGE_DELETE & MESSAGE_BULK_DELETE & MEMBER_DISCONNECT & MEMBER_MOVE + Count string `json:"count,omitempty"` + // CHANNEL_OVERWRITE_CREATE & CHANNEL_OVERWRITE_UPDATE & CHANNEL_OVERWRITE_DELETE + ID Snowflake `json:"id,omitempty"` + // CHANNEL_OVERWRITE_CREATE & CHANNEL_OVERWRITE_UPDATE & CHANNEL_OVERWRITE_DELETE + Type ChannelOverwritten `json:"type,omitempty"` + // CHANNEL_OVERWRITE_CREATE & CHANNEL_OVERWRITE_UPDATE & CHANNEL_OVERWRITE_DELETE + RoleName string `json:"role_name,omitempty"` +} + +// ChannelOverwritten is the type of overwritten entity in +// (AuditEntryInfo).Type. +type ChannelOverwritten string + +const ( + MemberChannelOverwritten ChannelOverwritten = "member" + RoleChannelOverwritten ChannelOverwritten = "role" +) + +type AuditLogChange struct { + Key string `json:"key"` + NewValue *AuditLogChangeKey `json:"new_value,omitempty"` + OldValue *AuditLogChangeKey `json:"old_value,omitempty"` +} + +type AuditLogChangeKey struct { + // The ID of the changed entity - sometimes used in conjunction with other + // keys + ID Snowflake `json:"snowflake"` + // Type of entity created, either a ChannelType (int) or string. + Type json.AlwaysString `json:"type"` + + *AuditLogChangeGuild + *AuditLogChangeChannel + *AuditLogChangeRole + *AuditLogChangeInvite + *AuditLogChangeUser + *AuditLogChangeIntegration +} + +// AuditLogChangeGuild is the audit log key for Guild. +type AuditLogChangeGuild struct { + // Name changed + Name string `json:"name,omitempty"` + // Icon changed + IconHash string `json:"icon_hash,omitempty"` + // Invite splash page artwork changed + SplashHash string `json:"splash_hash,omitempty"` + // Owner changed + OwnerID Snowflake `json:"owner_id,omitempty"` + // Region changed + Region string `json:"region,omitempty"` + // AFK channel changed + AfkChannelID Snowflake `json:"afk_channel_id,omitempty"` + // AFK timeout duration changed + AFKTimeout Seconds `json:"afk_timeout,omitempty"` + // Two-factor auth requirement changed + MFA MFALevel `json:"mfa_level,omitempty"` + // Required verification level changed + Verification Verification `json:"verification_level,omitempty"` + // Change in whose messages are scanned and deleted for explicit content in + // the server + ExplicitFilter ExplicitFilter `json:"explicit_content_filter,omitempty"` + // Default message notification level changed + Notification Notification `json:"default_message_notifications,omitempty"` + // Guild invite vanity url changed + VanityURLCode string `json:"vanity_url_code,omitempty"` + // New role added, only ID and Name are available + RoleAdd []Role `json:"$add,omitempty"` + // Role removed, partial similar to RoleAdd + RoleRemove []Role `json:"$remove,omitempty"` + // Change in number of days after which inactive and role-unassigned members + // are kicked + PruneDeleteDays int `json:"prune_delete_days,omitempty"` + // Server widget enabled/disable + WidgetEnabled bool `json:"widget_enabled,omitempty"` + // Channel id of the server widget changed + WidgetChannelID Snowflake `json:"widget_channel_id,omitempty"` + // ID of the system channel changed + SystemChannelID Snowflake `json:"system_channel_id,omitempty"` +} + +// AuditLogChangeChannel is the audit log key for Channel. +type AuditLogChangeChannel struct { + // Text channel topic changed + Topic string `json:"topic,omitempty"` + // Voice channel bitrate changed + Bitrate uint `json:"bitrate,omitempty"` + // Permissions on a channel changed + Permissions []Overwrite `json:"permission_overwrites,omitempty"` + // Channel NSFW restriction changed + NSFW bool `json:"nsfw,omitempty"` + // Application ID of the added or removed webhook or bot + ApplicationID Snowflake `json:"application_id,omitempty"` + // Amount of seconds a user has to wait before sending another message + // changed + UserRateLimit Seconds `json:"rate_limit_per_user,omitempty"` +} + +// AuditLogChangeRole is the audit log key for Role. +type AuditLogChangeRole struct { + // Permissions for a role changed + Permissions Permissions `json:"permissions,omitempty"` + // Role color changed + Color Color `json:"color,omitempty"` + // Role is now displayed/no longer displayed separate from online users + Hoist bool `json:"hoist,omitempty"` + // Role is now mentionable/unmentionable + Mentionable bool `json:"mentionable,omitempty"` + // A permission on a text or voice channel was allowed for a role + Allow Permissions `json:"allow,omitempty"` + // A permission on a text or voice channel was denied for a role + Deny Permissions `json:"deny,omitempty"` +} + +// AuditLogChangeInvite is the audit log key for InviteMetadata. +type AuditLogChangeInvite struct { + // Invite code changed + Code string `json:"code,omitempty"` + // Channel for invite code changed + ChannelID Snowflake `json:"channel_id,omitempty"` + // Person who created invite code changed + InviterID Snowflake `json:"inviter_id,omitempty"` + // Change to max number of times invite code can be used + MaxUses int `json:"max_uses,omitempty"` + // Number of times invite code used changed + Uses int `json:"uses,omitempty"` + // How long invite code lasts changed + MaxAge Seconds `json:"max_age,omitempty"` + // Invite code is temporary/never expires + Temporary bool `json:"temporary,omitempty"` +} + +// AuditLogChangeUser is the audit log key for User. +type AuditLogChangeUser struct { + // User server deafened/undeafened + Deaf bool `json:"deaf,omitempty"` + // User server muted/unmuted + Mute bool `json:"mute,omitempty"` + // User nickname changed + Nick string `json:"nick,omitempty"` + // User avatar changed + Avatar Hash `json:"avatar_hash,omitempty"` +} + +// AuditLogChangeIntegration is the audit log key for Integration. +type AuditLogChangeIntegration struct { + // Integration emoticons enabled/disabled + EnableEmoticons bool `json:"enable_emoticons,omitempty"` + // Integration expiring subscriber behavior changed + ExpireBehavior int `json:"expire_behavior,omitempty"` + // Integration expire grace period changed + ExpireGracePeriod int `json:"expire_grace_period,omitempty"` +} diff --git a/discord/invite.go b/discord/invite.go index 8b0d21f..39aa977 100644 --- a/discord/invite.go +++ b/discord/invite.go @@ -20,3 +20,17 @@ const ( InviteNormalUser InviteUserType = iota InviteUserStream ) + +// Extra information about an invite, will extend the invite object. +type InviteMetadata struct { + // Number of times this invite has been used + Uses int `json:"uses"` + // Max number of times this invite can be used + MaxUses int `json:"max_uses"` + // Duration (in seconds) after which the invite expires + MaxAge Seconds `json:"max_age"` + // Whether this invite only grants temporary membership + Temporary bool `json:"temporary"` + // When this invite was created + CreatedAt Timestamp `json:"created_at"` +} diff --git a/utils/json/raw.go b/utils/json/raw.go index b04114f..f953294 100644 --- a/utils/json/raw.go +++ b/utils/json/raw.go @@ -1,5 +1,10 @@ package json +import ( + "bytes" + "strconv" +) + type Marshaler interface { MarshalJSON() ([]byte, error) } @@ -29,3 +34,17 @@ func (m *Raw) UnmarshalJSON(data []byte) error { func (m Raw) String() string { return string(m) } + +// AlwaysString would always unmarshal into a string, from any JSON type. Quotes +// will be stripped. +type AlwaysString string + +func (m *AlwaysString) UnmarshalJSON(data []byte) error { + data = bytes.Trim(data, `"`) + *m = AlwaysString(data) + return nil +} + +func (m AlwaysString) Int() (int, error) { + return strconv.Atoi(string(m)) +}