discord: Support guild Scheduled Events (#319)

* implement the gateway side of guild scheduled event
* Add proper punctuation to each docstring
* Fix UserAdd and UserRemove events
* Add MANAGE_EVENTS permission
* Implement the API-side of scheduled events
* Add ScheduledEvent method
* Cleanup
This commit is contained in:
ItsLychee 2022-04-03 00:36:45 -05:00 committed by GitHub
parent 05948ac1ec
commit 92897b4fc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 393 additions and 3 deletions

162
api/scheduled_events.go Normal file
View File

@ -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),
)
}

View File

@ -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 {

View File

@ -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"`
}

View File

@ -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() + ">" }

View File

@ -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() }

View File

@ -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 }

View File

@ -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"`
}

View File

@ -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,
}