Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support of Stage Instance RestAPI and Events #1158

Merged
merged 15 commits into from Apr 14, 2022
Merged
20 changes: 11 additions & 9 deletions endpoints.go
Expand Up @@ -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/"
Expand Down Expand Up @@ -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" }
Expand Down
63 changes: 63 additions & 0 deletions eventhandlers.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions events.go
Expand Up @@ -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
Expand Down
61 changes: 61 additions & 0 deletions 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)
}
50 changes: 50 additions & 0 deletions restapi.go
Expand Up @@ -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) {
FedorLap2006 marked this conversation as resolved.
Show resolved Hide resolved
body, err := s.RequestWithBucketID("POST", EndpointStageInstances, data, EndpointStageInstances)
if err != nil {
return
}

err = unmarshal(body, &si)
return
42atomys marked this conversation as resolved.
Show resolved Hide resolved
}

// 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
42atomys marked this conversation as resolved.
Show resolved Hide resolved
}

// 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) {
FedorLap2006 marked this conversation as resolved.
Show resolved Hide resolved
FedorLap2006 marked this conversation as resolved.
Show resolved Hide resolved

body, err := s.RequestWithBucketID("PATCH", EndpointStageInstance(channelID), data, EndpointStageInstance(channelID))
if err != nil {
return
}

err = unmarshal(body, &si)
return
42atomys marked this conversation as resolved.
Show resolved Hide resolved
}

// 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
// ------------------------------------------------------------------------------------------------
Expand Down
47 changes: 47 additions & 0 deletions structs.go
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down