diff --git a/endpoints.go b/endpoints.go index c0d5b7241..f5822da64 100644 --- a/endpoints.go +++ b/endpoints.go @@ -23,15 +23,16 @@ var ( EndpointSmActive = EndpointSm + "active.json" EndpointSmUpcoming = EndpointSm + "upcoming.json" - EndpointDiscord = "https://discord.com/" - EndpointAPI = EndpointDiscord + "api/v" + APIVersion + "/" - EndpointGuilds = EndpointAPI + "guilds/" - EndpointChannels = EndpointAPI + "channels/" - EndpointUsers = EndpointAPI + "users/" - EndpointGateway = EndpointAPI + "gateway" - EndpointGatewayBot = EndpointGateway + "/bot" - EndpointWebhooks = EndpointAPI + "webhooks/" - EndpointStickers = EndpointAPI + "stickers/" + EndpointDiscord = "https://discord.com/" + EndpointAPI = EndpointDiscord + "api/v" + APIVersion + "/" + EndpointGuilds = EndpointAPI + "guilds/" + EndpointChannels = EndpointAPI + "channels/" + EndpointUsers = EndpointAPI + "users/" + EndpointGateway = EndpointAPI + "gateway" + EndpointGatewayBot = EndpointGateway + "/bot" + EndpointWebhooks = EndpointAPI + "webhooks/" + EndpointStickers = EndpointAPI + "stickers/" + EndpointStageInstances = EndpointAPI + "stage-instances" EndpointCDN = "https://cdn.discordapp.com/" EndpointCDNAttachments = EndpointCDN + "attachments/" @@ -95,6 +96,7 @@ var ( EndpointGuildBanner = func(gID, hash string) string { return EndpointCDNBanners + gID + "/" + hash + ".png" } EndpointGuildStickers = func(gID string) string { return EndpointGuilds + gID + "/stickers" } EndpointGuildSticker = func(gID, sID string) string { return EndpointGuilds + gID + "/stickers/" + sID } + EndpointStageInstance = func(cID string) string { return EndpointStageInstances + "/" + cID } EndpointGuildScheduledEvents = func(gID string) string { return EndpointGuilds + gID + "/scheduled-events" } EndpointGuildScheduledEvent = func(gID, eID string) string { return EndpointGuilds + gID + "/scheduled-events/" + eID } EndpointGuildScheduledEventUsers = func(gID, eID string) string { return EndpointGuildScheduledEvent(gID, eID) + "/users" } diff --git a/eventhandlers.go b/eventhandlers.go index 07f5d2167..d0e382f8a 100644 --- a/eventhandlers.go +++ b/eventhandlers.go @@ -27,6 +27,9 @@ const ( guildRoleCreateEventType = "GUILD_ROLE_CREATE" guildRoleDeleteEventType = "GUILD_ROLE_DELETE" guildRoleUpdateEventType = "GUILD_ROLE_UPDATE" + guildStageInstanceCreateEventType = "STAGE_INSTANCE_CREATE" + guildStageInstanceUpdateEventType = "STAGE_INSTANCE_UPDATE" + guildStageInstanceDeleteEventType = "STAGE_INSTANCE_DELETE" guildScheduledEventCreateEventType = "GUILD_SCHEDULED_EVENT_CREATE" guildScheduledEventDeleteEventType = "GUILD_SCHEDULED_EVENT_DELETE" guildScheduledEventUpdateEventType = "GUILD_SCHEDULED_EVENT_UPDATE" @@ -452,6 +455,66 @@ func (eh guildRoleUpdateEventHandler) Handle(s *Session, i interface{}) { } } +// guildStageInstanceEventCreateHandler is an event handler for StageInstanceEventCreate events. +type guildStageInstanceEventCreateHandler func(*Session, *StageInstanceEventCreate) + +// Type returns the event type for StageInstanceEventCreate events. +func (eh guildStageInstanceEventCreateHandler) Type() string { + return guildStageInstanceCreateEventType +} + +// New returns a new instance of StageInstanceEventCreate. +func (eh guildStageInstanceEventCreateHandler) New() interface{} { + return &StageInstanceEventCreate{} +} + +// Handle is the handler for StageInstanceEventCreate events. +func (eh guildStageInstanceEventCreateHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*StageInstanceEventCreate); ok { + eh(s, t) + } +} + +// guildStageInstanceEventUpdateHandler is an event handler for StageInstanceEventUpdate events. +type guildStageInstanceEventUpdateHandler func(*Session, *StageInstanceEventUpdate) + +// Type returns the event type for StageInstanceEventUpdate events. +func (eh guildStageInstanceEventUpdateHandler) Type() string { + return guildStageInstanceCreateEventType +} + +// New returns a new instance of StageInstanceEventUpdate. +func (eh guildStageInstanceEventUpdateHandler) New() interface{} { + return &StageInstanceEventUpdate{} +} + +// Handle is the handler for StageInstanceEventUpdate events. +func (eh guildStageInstanceEventUpdateHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*StageInstanceEventUpdate); ok { + eh(s, t) + } +} + +// guildStageInstanceEventDeleteHandler is an event handler for StageInstanceEventDelete events. +type guildStageInstanceEventDeleteHandler func(*Session, *StageInstanceEventDelete) + +// Type returns the event type for StageInstanceEventDelete events. +func (eh guildStageInstanceEventDeleteHandler) Type() string { + return guildStageInstanceCreateEventType +} + +// New returns a new instance of StageInstanceEventDelete. +func (eh guildStageInstanceEventDeleteHandler) New() interface{} { + return &StageInstanceEventDelete{} +} + +// Handle is the handler for StageInstanceEventDelete events. +func (eh guildStageInstanceEventDeleteHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*StageInstanceEventDelete); ok { + eh(s, t) + } +} + // guildScheduledEventCreateEventHandler is an event handler for GuildScheduledEventCreate events. type guildScheduledEventCreateEventHandler func(*Session, *GuildScheduledEventCreate) diff --git a/events.go b/events.go index bb45d2510..5c567df14 100644 --- a/events.go +++ b/events.go @@ -199,6 +199,21 @@ type GuildIntegrationsUpdate struct { GuildID string `json:"guild_id"` } +// StageInstanceEventCreate is the data for a StageInstanceEventCreate event. +type StageInstanceEventCreate struct { + *StageInstance +} + +// StageInstanceEventUpdate is the data for a StageInstanceEventUpdate event. +type StageInstanceEventUpdate struct { + *StageInstance +} + +// StageInstanceEventDelete is the data for a StageInstanceEventDelete event. +type StageInstanceEventDelete struct { + *StageInstance +} + // GuildScheduledEventCreate is the data for a GuildScheduledEventCreate event. type GuildScheduledEventCreate struct { *GuildScheduledEvent diff --git a/examples/stage_instance/main.go b/examples/stage_instance/main.go new file mode 100644 index 000000000..db59dbb48 --- /dev/null +++ b/examples/stage_instance/main.go @@ -0,0 +1,61 @@ +package main + +import ( + "flag" + "fmt" + "log" + "time" + + "github.com/bwmarrin/discordgo" +) + +// Flags +var ( + GuildID = flag.String("guild", "", "Test guild ID") + StageChannelID = flag.String("stage", "", "Test stage channel ID") + BotToken = flag.String("token", "", "Bot token") +) + +func init() { flag.Parse() } + +// To be correctly used, the bot needs to be in a guild. +// All actions must be done on a stage channel event +func main() { + s, _ := discordgo.New("Bot " + *BotToken) + s.AddHandler(func(s *discordgo.Session, r *discordgo.Ready) { + fmt.Println("Bot is ready") + }) + + err := s.Open() + if err != nil { + log.Fatalf("Cannot open the session: %v", err) + } + defer s.Close() + + // Create a new Stage instance on the previous channel + si, err := s.StageInstanceCreate(&discordgo.StageInstanceParams{ + ChannelID: *StageChannelID, + Topic: "Amazing topic", + PrivacyLevel: discordgo.StageInstancePrivacyLevelGuildOnly, + SendStartNotification: true, + }) + if err != nil { + log.Fatalf("Cannot create stage instance: %v", err) + } + log.Printf("Stage Instance %s has been successfully created", si.Topic) + + // Edit the stage instance with a new Topic + si, err = s.StageInstanceEdit(*StageChannelID, &discordgo.StageInstanceParams{ + Topic: "New amazing topic", + }) + if err != nil { + log.Fatalf("Cannot edit stage instance: %v", err) + } + log.Printf("Stage Instance %s has been successfully edited", si.Topic) + + time.Sleep(5 * time.Second) + if err = s.StageInstanceDelete(*StageChannelID); err != nil { + log.Fatalf("Cannot delete stage instance: %v", err) + } + log.Printf("Stage Instance %s has been successfully deleted", si.Topic) +} diff --git a/restapi.go b/restapi.go index fcefcbbdd..96140aa2b 100644 --- a/restapi.go +++ b/restapi.go @@ -2874,6 +2874,56 @@ func (s *Session) FollowupMessageDelete(appID string, interaction *Interaction, return s.WebhookMessageDelete(appID, interaction.Token, messageID) } +// ------------------------------------------------------------------------------------------------ +// Functions specific to stage instances +// ------------------------------------------------------------------------------------------------ + +// StageInstanceCreate creates and returns a new Stage instance associated to a Stage channel. +// data : Parameters needed to create a stage instance. +// data : The data of the Stage instance to create +func (s *Session) StageInstanceCreate(data *StageInstanceParams) (si *StageInstance, err error) { + body, err := s.RequestWithBucketID("POST", EndpointStageInstances, data, EndpointStageInstances) + if err != nil { + return + } + + err = unmarshal(body, &si) + return +} + +// StageInstance will retrieve a Stage instance by ID of the Stage channel. +// channelID : The ID of the Stage channel +func (s *Session) StageInstance(channelID string) (si *StageInstance, err error) { + body, err := s.RequestWithBucketID("GET", EndpointStageInstance(channelID), nil, EndpointStageInstance(channelID)) + if err != nil { + return + } + + err = unmarshal(body, &si) + return +} + +// StageInstanceEdit will edit a Stage instance by ID of the Stage channel. +// channelID : The ID of the Stage channel +// data : The data to edit the Stage instance +func (s *Session) StageInstanceEdit(channelID string, data *StageInstanceParams) (si *StageInstance, err error) { + + body, err := s.RequestWithBucketID("PATCH", EndpointStageInstance(channelID), data, EndpointStageInstance(channelID)) + if err != nil { + return + } + + err = unmarshal(body, &si) + return +} + +// StageInstanceDelete will delete a Stage instance by ID of the Stage channel. +// channelID : The ID of the Stage channel +func (s *Session) StageInstanceDelete(channelID string) (err error) { + _, err = s.RequestWithBucketID("DELETE", EndpointStageInstance(channelID), nil, EndpointStageInstance(channelID)) + return +} + // ------------------------------------------------------------------------------------------------ // Functions specific to guilds scheduled events // ------------------------------------------------------------------------------------------------ diff --git a/structs.go b/structs.go index c2927b5bb..68005d445 100644 --- a/structs.go +++ b/structs.go @@ -260,6 +260,7 @@ const ( ChannelTypeGuildNewsThread ChannelType = 10 ChannelTypeGuildPublicThread ChannelType = 11 ChannelTypeGuildPrivateThread ChannelType = 12 + ChannelTypeGuildStageVoice ChannelType = 13 ) // A Channel holds all data related to an individual Discord channel. @@ -745,6 +746,9 @@ type Guild struct { // Permissions of our user Permissions int64 `json:"permissions,string"` + + // Stage instances in the guild + StageInstances []*StageInstance `json:"stage_instances"` } // A GuildPreview holds data related to a specific public Discord Guild, even if the user is not in the guild. @@ -1752,6 +1756,49 @@ type IdentifyProperties struct { ReferringDomain string `json:"$referring_domain"` } +// StageInstance holds information about a live stage. +// https://discord.com/developers/docs/resources/stage-instance#stage-instance-resource +type StageInstance struct { + // The id of this Stage instance + ID string `json:"id"` + // The guild id of the associated Stage channel + GuildID string `json:"guild_id"` + // The id of the associated Stage channel + ChannelID string `json:"channel_id"` + // The topic of the Stage instance (1-120 characters) + Topic string `json:"topic"` + // The privacy level of the Stage instance + // https://discord.com/developers/docs/resources/stage-instance#stage-instance-object-privacy-level + PrivacyLevel StageInstancePrivacyLevel `json:"privacy_level"` + // Whether or not Stage Discovery is disabled (deprecated) + DiscoverableDisabled bool `json:"discoverable_disabled"` + // The id of the scheduled event for this Stage instance + GuildScheduledEventID string `json:"guild_scheduled_event_id"` +} + +// StageInstanceParams represents the parameters needed to create or edit a stage instance +type StageInstanceParams struct { + // ChannelID represents the id of the Stage channel + ChannelID string `json:"channel_id,omitempty"` + // Topic of the Stage instance (1-120 characters) + Topic string `json:"topic,omitempty"` + // PrivacyLevel of the Stage instance (default GUILD_ONLY) + PrivacyLevel StageInstancePrivacyLevel `json:"privacy_level,omitempty"` + // SendStartNotification will notify @everyone that a Stage instance has started + SendStartNotification bool `json:"send_start_notification,omitempty"` +} + +// StageInstancePrivacyLevel represents the privacy level of a Stage instance +// https://discord.com/developers/docs/resources/stage-instance#stage-instance-object-privacy-level +type StageInstancePrivacyLevel int + +const ( + // StageInstancePrivacyLevelPublic The Stage instance is visible publicly. (deprecated) + StageInstancePrivacyLevelPublic StageInstancePrivacyLevel = 1 + // StageInstancePrivacyLevelGuildOnly The Stage instance is visible to only guild members. + StageInstancePrivacyLevelGuildOnly StageInstancePrivacyLevel = 2 +) + // Constants for the different bit offsets of text channel permissions const ( // Deprecated: PermissionReadMessages has been replaced with PermissionViewChannel for text and voice channels