diff --git a/api/scheduled_events.go b/api/scheduled_events.go new file mode 100644 index 0000000..b4c0d9c --- /dev/null +++ b/api/scheduled_events.go @@ -0,0 +1,162 @@ +package api + +import ( + "github.com/diamondburned/arikawa/v3/discord" + "github.com/diamondburned/arikawa/v3/utils/httputil" + "github.com/diamondburned/arikawa/v3/utils/json/option" +) + +// CreateScheduledEventData is the structure for creating a scheduled event. +// +// https://discord.com/developers/docs/resources/guild-scheduled-event#create-guild-scheduled-event-json-params +type CreateScheduledEventData struct { + // ChannelID is the channel id of the scheduled event. + ChannelID discord.ChannelID `json:"channel_id"` + // EntityMetadata is the entity metadata of the scheduled event. + EntityMetadata *discord.EntityMetadata `json:"entity_metadata"` + // Name is the name of the scheduled event. + Name string `json:"name"` + // PrivacyLevel is the privacy level of the scheduled event. + PrivacyLevel discord.ScheduledEventPrivacyLevel `json:"privacy_level"` + // StartTime is when the scheduled event begins. + StartTime discord.Timestamp `json:"scheduled_start_time"` + // EndTime is when the scheduled event ends, if it does. + EndTime *discord.Timestamp `json:"scheduled_end_time,omitempty"` + // Description is the description of the schduled event. + Description string `json:"description"` + // EntityType is the entity type of the scheduled event. + EntityType discord.EntityType `json:"entity_type"` + // Image is the cover image of the scheduled event. + Image Image `json:"image"` +} + +// EditScheduledEventData is the structure for modifying a scheduled event. +// +// https://discord.com/developers/docs/resources/guild-scheduled-event#modify-guild-scheduled-event-json-params +type EditScheduledEventData struct { + // ChannelID is the new channel id of the scheduled event. + ChannelID discord.ChannelID `json:"channel_id,omitempty"` + // EntityMetadata is the new entity metadata of the scheduled event. + EntityMetadata *discord.EntityMetadata `json:"entity_metadata,omitempty"` + // Name is the new name of the scheduled event. + Name option.NullableString `json:"name,omitempty"` + // PrivacyLevel is the new privacy level of the scheduled event. + PrivacyLevel discord.ScheduledEventPrivacyLevel `json:"privacy_level,omitempty"` + // StartTime is the new starting time for when the scheduled event begins. + StartTime *discord.Timestamp `json:"scheduled_start_time,omitempty"` + // EndTime is the new time of which the scheduled event ends + EndTime *discord.Timestamp `json:"scheduled_end_time,omitempty"` + // Description is the new description of the scheduled event. + Description option.NullableString `json:"description,omitempty"` + // EntityType is the new entity type of the scheduled event. + EntityType discord.EntityType `json:"entity_type,omitempty"` + // Status is the new event status of the scheduled event. + Status discord.EventStatus `json:"status,omitempty"` + // Image is the new image of the scheduled event. + Image *Image `json:"image,omitempty"` +} + +// GuildScheduledEventUser represents a user interested in a scheduled event. +// +// https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-user-object +type GuildScheduledEventUser struct { + // EventID is the id of the scheduled event. + EventID discord.EventID `json:"guild_scheduled_event_id"` + // User is the user object of the user. + User discord.User `json:"user"` + // Member is the member object of the user. + Member *discord.Member `json:"member"` +} + +// ListScheduledEventUsers returns a list of users currently in a scheduled event. +// +// https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event-users +func (c *Client) ListScheduledEventUsers( + guildID discord.GuildID, eventID discord.EventID, limit option.NullableInt, + withMember bool, before, after discord.UserID) ([]GuildScheduledEventUser, error) { + var eventUsers []GuildScheduledEventUser + var params struct { + Limit option.NullableInt `schema:"limit,omitempty"` + WithMember bool `schema:"with_member,omitempty"` + Before discord.UserID `schema:"before,omitempty"` + After discord.UserID `schema:"after,omitempty"` + } + params.Limit = limit + params.WithMember = withMember + params.Before = before + params.After = after + + return eventUsers, c.RequestJSON( + &eventUsers, "GET", EndpointGuilds+guildID.String()+"/scheduled-events/"+eventID.String()+"/users", + httputil.WithSchema(c, params), + ) + +} + +// ListScheduledEvents lists the scheduled events in a guild. +// +// https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event-users +func (c *Client) ListScheduledEvents(guildID discord.GuildID, withUserCount bool) ([]discord.GuildScheduledEvent, error) { + var scheduledEvents []discord.GuildScheduledEvent + var params struct { + WithUserCount bool `schema:"with_user_count"` + } + params.WithUserCount = withUserCount + return scheduledEvents, c.RequestJSON( + &scheduledEvents, "GET", EndpointGuilds+guildID.String()+"/scheduled-events", + httputil.WithSchema(c, params), + ) +} + +// CreateScheduledEvent creates a new scheduled event. +// +// https://discord.com/developers/docs/resources/guild-scheduled-event#create-guild-scheduled-event +func (c *Client) CreateScheduledEvent(guildID discord.GuildID, reason AuditLogReason, + data CreateScheduledEventData) (*discord.GuildScheduledEvent, error) { + var scheduledEvent *discord.GuildScheduledEvent + return scheduledEvent, c.RequestJSON( + &scheduledEvent, "POST", + EndpointGuilds+guildID.String()+"/scheduled-events", + httputil.WithJSONBody(data), + httputil.WithHeaders(reason.Header()), + ) +} + +// EditScheduledEvent modifies the attributes of a scheduled event. +// +// https://discord.com/developers/docs/resources/guild-scheduled-event#modify-guild-scheduled-event +func (c *Client) EditScheduledEvent(guildID discord.GuildID, eventID discord.EventID, reason AuditLogReason, + data EditScheduledEventData) (*discord.GuildScheduledEvent, error) { + var modifiedEvent *discord.GuildScheduledEvent + return modifiedEvent, c.RequestJSON( + &modifiedEvent, + "PATCH", EndpointGuilds+guildID.String()+"/scheduled-events/"+eventID.String(), + httputil.WithHeaders(reason.Header()), + httputil.WithJSONBody(data), + ) +} + +// DeleteScheduledEvent deletes a scheduled event. +// +// https://discord.com/developers/docs/resources/guild-scheduled-event#delete-guild-scheduled-event +func (c *Client) DeleteScheduledEvent(guildID discord.GuildID, eventID discord.EventID) error { + return c.FastRequest( + "DELETE", EndpointGuilds+guildID.String()+"/scheduled-events/"+eventID.String(), + ) +} + +// ScheduledEvent retrieves the information on the scheduled event +// +// https://discord.com/developers/docs/resources/guild-scheduled-event#get-guild-scheduled-event +func (c *Client) ScheduledEvent(guildID discord.GuildID, eventID discord.EventID, withUserCount bool) (*discord.GuildScheduledEvent, error) { + var params struct { + WithUserCount bool `schema:"with_user_count"` + } + params.WithUserCount = withUserCount + var event *discord.GuildScheduledEvent + return event, c.RequestJSON( + &event, "GET", EndpointGuilds+guildID.String()+"/scheduled-events/"+eventID.String(), + httputil.WithSchema(c, params), + ) + +} diff --git a/discord/permission.go b/discord/permission.go index 4275859..39914b8 100644 --- a/discord/permission.go +++ b/discord/permission.go @@ -73,7 +73,8 @@ const ( // Allows for requesting to speak in stage channels. (This permission is // under active development and may be changed or removed.) PermissionRequestToSpeak - _ + // Allows for creating, editing, and deleting scheduled events. + PermissionManageEvents // Allows for deleting and archiving threads, and viewing all private // threads PermissionManageThreads @@ -139,7 +140,8 @@ const ( PermissionManageEmojisAndStickers | PermissionManageNicknames | PermissionChangeNickname | - PermissionViewAuditLog + PermissionViewAuditLog | + PermissionManageEvents ) func (p Permissions) Has(perm Permissions) bool { diff --git a/discord/scheduled_events.go b/discord/scheduled_events.go new file mode 100644 index 0000000..bd293df --- /dev/null +++ b/discord/scheduled_events.go @@ -0,0 +1,80 @@ +package discord + +// EventStatus describes the different statuses GuildScheduledEvent can be. +// +// https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-status +type EventStatus int + +const ( + ScheduledEvent EventStatus = iota + 1 + ActiveEvent + CompletedEvent + CancelledEvent +) + +// EntityType describes the different types GuildScheduledEvent can be. +type EntityType int + +const ( + StageInstanceEntity EntityType = iota + 1 + VoiceEntity + ExternalEntity +) + +// ScheduledEventPrivacy describes the privacy levels of GuildScheduledEvent. +// +// https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-privacy-level +type ScheduledEventPrivacyLevel int + +const ( + // GuildOnly requires the scheduled event to be only accessible to guild members. + GuildOnly ScheduledEventPrivacyLevel = iota + 2 +) + +// GuildScheduledEvent describes the scheduled event structure. +// +// https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-structure +type GuildScheduledEvent struct { + // ID is the id of the scheduled event. + ID EventID `json:"id"` + // GuildID is the guild id of where the scheduled event belongs to. + GuildID GuildID `json:"guild_id"` + // ChannelID is the channel id in which the scheduled event will be + // hosted at, this may be NullChannelID if the EntityType is set + // to ExternalEntity. + ChannelID ChannelID `json:"channel_id"` + // CreatorID is the user id of who created the scheduled event. + CreatorID UserID `json:"creator_id"` + // Name is the name of the scheduled event. + Name string `json:"name"` + // Description is the description of the scheduled event. + Description string `json:"description"` + // StartTime is when the scheduled event will start at. + StartTime Timestamp `json:"scheduled_start_time"` + // EndTime is when the scheduled event will end at, if it does. + EndTime Timestamp `json:"scheduled_end_time"` + // PrivacyLevel is the privacy level of the scheduled event. + PrivacyLevel ScheduledEventPrivacyLevel `json:"privacy_level"` + // Status is the status of the scheduled event. + Status EventStatus `json:"status"` + // EntityType describes the type of scheduled event. + EntityType EntityType `json:"entity_type"` + // EntityID is the id of an entity associated with a scheduled event. + EntityID EntityID `json:"entity_id"` + // EntityMetadata is additional metadata for the scheduled event. + EntityMetadata *EntityMetadata `json:"entity_metadata"` + // Creator is the the user responsible for creating the scheduled event. This field + // will only be present if CreatorID is + Creator *User `json:"creator"` + // UserCount is the number of users subscribed to the scheduled event. + UserCount int `json:"user_count"` + // Image is the cover image hash of the scheduled event. + Image Hash `json:"image,omitempty"` +} + +// EntityMetadata is the entity metadata of GuildScheduledEvent. +type EntityMetadata struct { + // Location describes where the event takes place at. This is not + // optional when GuildScheduled#EntityType is set as ExternalEntity. + Location string `json:"location,omitempty"` +} diff --git a/discord/snowflake.go b/discord/snowflake.go index beef083..c11335b 100644 --- a/discord/snowflake.go +++ b/discord/snowflake.go @@ -15,7 +15,7 @@ func DurationSinceEpoch(t time.Time) time.Duration { return time.Duration(t.UnixNano()) - Epoch } -//go:generate go run ../utils/cmd/gensnowflake -o snowflake_types.go AppID AttachmentID AuditLogEntryID ChannelID CommandID EmojiID GuildID IntegrationID InteractionID MessageID RoleID StageID StickerID StickerPackID TeamID UserID WebhookID +//go:generate go run ../utils/cmd/gensnowflake -o snowflake_types.go AppID AttachmentID AuditLogEntryID ChannelID CommandID EmojiID GuildID IntegrationID InteractionID MessageID RoleID StageID StickerID StickerPackID TeamID UserID WebhookID EventID EntityID // Mention generates the mention syntax for this channel ID. func (s ChannelID) Mention() string { return "<#" + s.String() + ">" } diff --git a/discord/snowflake_types.go b/discord/snowflake_types.go index 4714527..de95710 100644 --- a/discord/snowflake_types.go +++ b/discord/snowflake_types.go @@ -411,3 +411,51 @@ func (s WebhookID) Time() time.Time { return Snowflake(s).Time() } func (s WebhookID) Worker() uint8 { return Snowflake(s).Worker() } func (s WebhookID) PID() uint8 { return Snowflake(s).PID() } func (s WebhookID) Increment() uint16 { return Snowflake(s).Increment() } + +// EventID is the snowflake type for a EventID. +type EventID Snowflake + +// NullEventID gets encoded into a null. This is used for optional and nullable snowflake fields. +const NullEventID = EventID(NullSnowflake) + +func (s EventID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() } +func (s *EventID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) } + +// String returns the ID, or nothing if the snowflake isn't valid. +func (s EventID) String() string { return Snowflake(s).String() } + +// IsValid returns whether or not the snowflake is valid. +func (s EventID) IsValid() bool { return Snowflake(s).IsValid() } + +// IsNull returns whether or not the snowflake is null. This method is rarely +// ever useful; most people should use IsValid instead. +func (s EventID) IsNull() bool { return Snowflake(s).IsNull() } + +func (s EventID) Time() time.Time { return Snowflake(s).Time() } +func (s EventID) Worker() uint8 { return Snowflake(s).Worker() } +func (s EventID) PID() uint8 { return Snowflake(s).PID() } +func (s EventID) Increment() uint16 { return Snowflake(s).Increment() } + +// EntityID is the snowflake type for a EntityID. +type EntityID Snowflake + +// NullEntityID gets encoded into a null. This is used for optional and nullable snowflake fields. +const NullEntityID = EntityID(NullSnowflake) + +func (s EntityID) MarshalJSON() ([]byte, error) { return Snowflake(s).MarshalJSON() } +func (s *EntityID) UnmarshalJSON(v []byte) error { return (*Snowflake)(s).UnmarshalJSON(v) } + +// String returns the ID, or nothing if the snowflake isn't valid. +func (s EntityID) String() string { return Snowflake(s).String() } + +// IsValid returns whether or not the snowflake is valid. +func (s EntityID) IsValid() bool { return Snowflake(s).IsValid() } + +// IsNull returns whether or not the snowflake is null. This method is rarely +// ever useful; most people should use IsValid instead. +func (s EntityID) IsNull() bool { return Snowflake(s).IsNull() } + +func (s EntityID) Time() time.Time { return Snowflake(s).Time() } +func (s EntityID) Worker() uint8 { return Snowflake(s).Worker() } +func (s EntityID) PID() uint8 { return Snowflake(s).PID() } +func (s EntityID) Increment() uint16 { return Snowflake(s).Increment() } diff --git a/gateway/event_methods.go b/gateway/event_methods.go index e8e98fd..c3f66fa 100644 --- a/gateway/event_methods.go +++ b/gateway/event_methods.go @@ -69,6 +69,11 @@ func init() { func() ws.Event { return new(RelationshipRemoveEvent) }, func() ws.Event { return new(ReadyEvent) }, func() ws.Event { return new(ReadySupplementalEvent) }, + func() ws.Event { return new(GuildScheduledEventCreateEvent) }, + func() ws.Event { return new(GuildScheduledEventUpdateEvent) }, + func() ws.Event { return new(GuildScheduledEventDeleteEvent) }, + func() ws.Event { return new(GuildScheduledEventUserAddEvent) }, + func() ws.Event { return new(GuildScheduledEventUserRemoveEvent) }, func() ws.Event { return new(IdentifyCommand) }, ) } @@ -453,6 +458,46 @@ func (*ReadySupplementalEvent) Op() ws.OpCode { return dispatchOp } // EventType implements Event. func (*ReadySupplementalEvent) EventType() ws.EventType { return "READY_SUPPLEMENTAL" } +// Op implements Event. It always returns 0. +func (*GuildScheduledEventCreateEvent) Op() ws.OpCode { return dispatchOp } + +// EventType implements Event. +func (*GuildScheduledEventCreateEvent) EventType() ws.EventType { + return "GUILD_SCHEDULED_EVENT_CREATE" +} + +// Op implements Event. It always returns 0. +func (*GuildScheduledEventUpdateEvent) Op() ws.OpCode { return dispatchOp } + +// EventType implements Event. +func (*GuildScheduledEventUpdateEvent) EventType() ws.EventType { + return "GUILD_SCHEDULED_EVENT_UPDATE" +} + +// Op implements Event. It always returns 0. +func (*GuildScheduledEventDeleteEvent) Op() ws.OpCode { return dispatchOp } + +// EventType implements Event. +func (*GuildScheduledEventDeleteEvent) EventType() ws.EventType { + return "GUILD_SCHEDULED_EVENT_DELETE" +} + +// Op implements Event. It always returns 0. +func (*GuildScheduledEventUserAddEvent) Op() ws.OpCode { return dispatchOp } + +// EventType implements Event. +func (*GuildScheduledEventUserAddEvent) EventType() ws.EventType { + return "GUILD_SCHEDULED_EVENT_USER_ADD" +} + +// Op implements Event. It always returns 0. +func (*GuildScheduledEventUserRemoveEvent) Op() ws.OpCode { return dispatchOp } + +// EventType implements Event. +func (*GuildScheduledEventUserRemoveEvent) EventType() ws.EventType { + return "GUILD_SCHEDULED_EVENT_USER_REMOVE" +} + // Op implements Event. It always returns Op 2. func (*IdentifyCommand) Op() ws.OpCode { return 2 } diff --git a/gateway/events.go b/gateway/events.go index 7799ffa..c84da50 100644 --- a/gateway/events.go +++ b/gateway/events.go @@ -895,3 +895,48 @@ func ConvertSupplementalPresences(sps []SupplementalPresence) []discord.Presence } return presences } + +// GuildScheduledEventCreateEvent is a dispatch event. +// +// https://discord.com/developers/docs/topics/gateway#guild-scheduled-event-create +type GuildScheduledEventCreateEvent struct { + discord.GuildScheduledEvent +} + +// GuildScheduledEventUpdateEvent is a dispatch event. +// +// https://discord.com/developers/docs/topics/gateway#guild-scheduled-event-update +type GuildScheduledEventUpdateEvent struct { + discord.GuildScheduledEvent +} + +// GuildScheduledEventDeleteEvent is a dispatch event. +// +// https://discord.com/developers/docs/topics/gateway#guild-scheduled-event-delete +type GuildScheduledEventDeleteEvent struct { + discord.GuildScheduledEvent +} + +// GuildScheduledEventUserAddEvent is a dispatch event. +// +// https://discord.com/developers/docs/topics/gateway#guild-scheduled-event-user-add +type GuildScheduledEventUserAddEvent struct { + // EventID is the id of the scheduled event + EventID discord.EventID `json:"guild_scheduled_event_id"` + // UserID is the id of the user being added + UserID discord.UserID `json:"user_id"` + // GuildID is the id of where the scheduled event belongs + GuildID discord.GuildID `json:"guild_id"` +} + +// GuildScheduledEventUserRemoveEvent is a dispatch event. +// +// https://discord.com/developers/docs/topics/gateway#guild-scheduled-event-user-remove +type GuildScheduledEventUserRemoveEvent struct { + // EventID is the id of the scheduled event + EventID discord.EventID `json:"guild_scheduled_event_id"` + // UserID is the id of the user being removed + UserID discord.UserID `json:"user_id"` + // GuildID is the id of where the scheduled event belongs + GuildID discord.GuildID `json:"guild_id"` +} diff --git a/gateway/intents.go b/gateway/intents.go index 3d44749..ca4f895 100644 --- a/gateway/intents.go +++ b/gateway/intents.go @@ -25,6 +25,8 @@ const ( IntentDirectMessages IntentDirectMessageReactions IntentDirectMessageTyping + _ + IntentGuildScheduledEvents ) // PrivilegedIntents contains a list of privileged intents that Discord requires @@ -90,4 +92,10 @@ var EventIntents = map[ws.EventType]Intents{ "MESSAGE_REACTION_REMOVE_EMOJI": IntentGuildMessageReactions | IntentDirectMessageReactions, "TYPING_START": IntentGuildMessageTyping | IntentDirectMessageTyping, + + "GUILD_SCHEDULED_EVENT_CREATE": IntentGuildScheduledEvents, + "GUILD_SCHEDULED_EVENT_UPDATE": IntentGuildScheduledEvents, + "GUILD_SCHEDULED_EVENT_DELETE": IntentGuildScheduledEvents, + "GUILD_SCHEDULED_EVENT_USER_ADD": IntentGuildScheduledEvents, + "GUILD_SCHEDULED_EVENT_USER_REMOVE": IntentGuildScheduledEvents, }