diff --git a/discord_test.go b/discord_test.go index 720397b..df52397 100644 --- a/discord_test.go +++ b/discord_test.go @@ -15,11 +15,12 @@ var ( dg *Session // Stores a global discordgo user session dgBot *Session // Stores a global discordgo bot session - envOAuth2Token = os.Getenv("DG_OAUTH2_TOKEN") // Token to use when authenticating using OAuth2 token - envBotToken = os.Getenv("DGB_TOKEN") // Token to use when authenticating the bot account - envGuild = os.Getenv("DG_GUILD") // Guild ID to use for tests - envChannel = os.Getenv("DG_CHANNEL") // Channel ID to use for tests - envAdmin = os.Getenv("DG_ADMIN") // User ID of admin user to use for tests + envOAuth2Token = os.Getenv("DG_OAUTH2_TOKEN") // Token to use when authenticating using OAuth2 token + envBotToken = os.Getenv("DGB_TOKEN") // Token to use when authenticating the bot account + envGuild = os.Getenv("DG_GUILD") // Guild ID to use for tests + envChannel = os.Getenv("DG_CHANNEL") // Channel ID to use for tests + envVoiceChannel = os.Getenv("DG_VOICE_CHANNEL") // Channel ID to use for tests + envAdmin = os.Getenv("DG_ADMIN") // User ID of admin user to use for tests ) func TestMain(m *testing.M) { @@ -183,3 +184,119 @@ func TestRemoveHandler(t *testing.T) { t.Fatalf("testHandler was not called once.") } } + +func TestScheduledEvents(t *testing.T) { + if dgBot == nil { + t.Skip("Skipping, dgBot not set.") + } + + beginAt := time.Now().Add(1 * time.Hour) + endAt := time.Now().Add(2 * time.Hour) + event, err := dgBot.GuildScheduledEventCreate(envGuild, &GuildScheduledEventParams{ + Name: "Test Event", + PrivacyLevel: GuildScheduledEventPrivacyLevelGuildOnly, + ScheduledStartTime: &beginAt, + ScheduledEndTime: &endAt, + Description: "Awesome Test Event created on livestream", + EntityType: GuildScheduledEventEntityTypeExternal, + EntityMetadata: &GuildScheduledEventEntityMetadata{ + Location: "https://discord.com", + }, + }) + defer dgBot.GuildScheduledEventDelete(envGuild, event.ID) + + if err != nil || event.Name != "Test Event" { + t.Fatal(err) + } + + events, err := dgBot.GuildScheduledEvents(envGuild, true) + if err != nil { + t.Fatal(err) + } + + var foundEvent *GuildScheduledEvent + for _, e := range events { + if e.ID == event.ID { + foundEvent = e + break + } + } + if foundEvent.Name != event.Name { + t.Fatal("err on GuildScheduledEvents endpoint. Missing Scheduled Event") + } + + getEvent, err := dgBot.GuildScheduledEvent(envGuild, event.ID, true) + if err != nil { + t.Fatal(err) + } + if getEvent.Name != event.Name { + t.Fatal("err on GuildScheduledEvent endpoint. Mismatched Scheduled Event") + } + + eventUpdated, err := dgBot.GuildScheduledEventEdit(envGuild, event.ID, &GuildScheduledEventParams{Name: "Test Event Updated"}) + if err != nil { + t.Fatal(err) + } + + if eventUpdated.Name != "Test Event Updated" { + t.Fatal("err on GuildScheduledEventUpdate endpoint. Scheduled Event Name mismatch") + } + + // Usage of 1 and 1 is just the pseudo data with the purpose to run all branches in the function without crashes. + // see https://github.com/bwmarrin/discordgo/pull/1032#discussion_r815438303 for more details. + users, err := dgBot.GuildScheduledEventUsers(envGuild, event.ID, 1, true, "1", "1") + if err != nil { + t.Fatal(err) + } + if len(users) != 0 { + t.Fatal("err on GuildScheduledEventUsers. Mismatch of event maybe occured") + } + + err = dgBot.GuildScheduledEventDelete(envGuild, event.ID) + if err != nil { + t.Fatal(err) + } +} + +func TestComplexScheduledEvents(t *testing.T) { + if dgBot == nil { + t.Skip("Skipping, dgBot not set.") + } + + beginAt := time.Now().Add(1 * time.Hour) + endAt := time.Now().Add(2 * time.Hour) + event, err := dgBot.GuildScheduledEventCreate(envGuild, &GuildScheduledEventParams{ + Name: "Test Voice Event", + PrivacyLevel: GuildScheduledEventPrivacyLevelGuildOnly, + ScheduledStartTime: &beginAt, + ScheduledEndTime: &endAt, + Description: "Test event on voice channel", + EntityType: GuildScheduledEventEntityTypeVoice, + ChannelID: envVoiceChannel, + }) + if err != nil || event.Name != "Test Voice Event" { + t.Fatal(err) + } + defer dgBot.GuildScheduledEventDelete(envGuild, event.ID) + + _, err = dgBot.GuildScheduledEventEdit(envGuild, event.ID, &GuildScheduledEventParams{ + EntityType: GuildScheduledEventEntityTypeExternal, + EntityMetadata: &GuildScheduledEventEntityMetadata{ + Location: "https://discord.com", + }, + }) + + if err != nil { + t.Fatal("err on GuildScheduledEventEdit. Change of entity type to external failed") + } + + _, err = dgBot.GuildScheduledEventEdit(envGuild, event.ID, &GuildScheduledEventParams{ + ChannelID: envVoiceChannel, + EntityType: GuildScheduledEventEntityTypeVoice, + EntityMetadata: nil, + }) + + if err != nil { + t.Fatal("err on GuildScheduledEventEdit. Change of entity type to voice failed") + } +} diff --git a/endpoints.go b/endpoints.go index c51916c..e00512d 100644 --- a/endpoints.go +++ b/endpoints.go @@ -60,38 +60,41 @@ var ( EndpointUserChannels = func(uID string) string { return EndpointUsers + uID + "/channels" } EndpointUserConnections = func(uID string) string { return EndpointUsers + uID + "/connections" } - EndpointGuild = func(gID string) string { return EndpointGuilds + gID } - EndpointGuildThreads = func(gID string) string { return EndpointGuild(gID) + "/threads" } - EndpointGuildActiveThreads = func(gID string) string { return EndpointGuildThreads(gID) + "/active" } - EndpointGuildPreview = func(gID string) string { return EndpointGuilds + gID + "/preview" } - EndpointGuildChannels = func(gID string) string { return EndpointGuilds + gID + "/channels" } - EndpointGuildMembers = func(gID string) string { return EndpointGuilds + gID + "/members" } - EndpointGuildMember = func(gID, uID string) string { return EndpointGuilds + gID + "/members/" + uID } - EndpointGuildMemberRole = func(gID, uID, rID string) string { return EndpointGuilds + gID + "/members/" + uID + "/roles/" + rID } - EndpointGuildBans = func(gID string) string { return EndpointGuilds + gID + "/bans" } - EndpointGuildBan = func(gID, uID string) string { return EndpointGuilds + gID + "/bans/" + uID } - EndpointGuildIntegrations = func(gID string) string { return EndpointGuilds + gID + "/integrations" } - EndpointGuildIntegration = func(gID, iID string) string { return EndpointGuilds + gID + "/integrations/" + iID } - EndpointGuildRoles = func(gID string) string { return EndpointGuilds + gID + "/roles" } - EndpointGuildRole = func(gID, rID string) string { return EndpointGuilds + gID + "/roles/" + rID } - EndpointGuildInvites = func(gID string) string { return EndpointGuilds + gID + "/invites" } - EndpointGuildWidget = func(gID string) string { return EndpointGuilds + gID + "/widget" } - EndpointGuildEmbed = EndpointGuildWidget - EndpointGuildPrune = func(gID string) string { return EndpointGuilds + gID + "/prune" } - EndpointGuildIcon = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".png" } - EndpointGuildIconAnimated = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".gif" } - EndpointGuildSplash = func(gID, hash string) string { return EndpointCDNSplashes + gID + "/" + hash + ".png" } - EndpointGuildWebhooks = func(gID string) string { return EndpointGuilds + gID + "/webhooks" } - EndpointGuildAuditLogs = func(gID string) string { return EndpointGuilds + gID + "/audit-logs" } - EndpointGuildEmojis = func(gID string) string { return EndpointGuilds + gID + "/emojis" } - EndpointGuildEmoji = func(gID, eID string) string { return EndpointGuilds + gID + "/emojis/" + eID } - 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 } - EndpointGuildTemplate = func(tID string) string { return EndpointGuilds + "/templates/" + tID } - EndpointGuildTemplates = func(gID string) string { return EndpointGuilds + gID + "/templates" } - EndpointGuildTemplateSync = func(gID, tID string) string { return EndpointGuilds + gID + "/templates/" + tID } - EndpointGuildMemberAvatar = func(gId, uID, aID string) string { + EndpointGuild = func(gID string) string { return EndpointGuilds + gID } + EndpointGuildThreads = func(gID string) string { return EndpointGuild(gID) + "/threads" } + EndpointGuildActiveThreads = func(gID string) string { return EndpointGuildThreads(gID) + "/active" } + EndpointGuildPreview = func(gID string) string { return EndpointGuilds + gID + "/preview" } + EndpointGuildChannels = func(gID string) string { return EndpointGuilds + gID + "/channels" } + EndpointGuildMembers = func(gID string) string { return EndpointGuilds + gID + "/members" } + EndpointGuildMember = func(gID, uID string) string { return EndpointGuilds + gID + "/members/" + uID } + EndpointGuildMemberRole = func(gID, uID, rID string) string { return EndpointGuilds + gID + "/members/" + uID + "/roles/" + rID } + EndpointGuildBans = func(gID string) string { return EndpointGuilds + gID + "/bans" } + EndpointGuildBan = func(gID, uID string) string { return EndpointGuilds + gID + "/bans/" + uID } + EndpointGuildIntegrations = func(gID string) string { return EndpointGuilds + gID + "/integrations" } + EndpointGuildIntegration = func(gID, iID string) string { return EndpointGuilds + gID + "/integrations/" + iID } + EndpointGuildRoles = func(gID string) string { return EndpointGuilds + gID + "/roles" } + EndpointGuildRole = func(gID, rID string) string { return EndpointGuilds + gID + "/roles/" + rID } + EndpointGuildInvites = func(gID string) string { return EndpointGuilds + gID + "/invites" } + EndpointGuildWidget = func(gID string) string { return EndpointGuilds + gID + "/widget" } + EndpointGuildEmbed = EndpointGuildWidget + EndpointGuildPrune = func(gID string) string { return EndpointGuilds + gID + "/prune" } + EndpointGuildIcon = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".png" } + EndpointGuildIconAnimated = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".gif" } + EndpointGuildSplash = func(gID, hash string) string { return EndpointCDNSplashes + gID + "/" + hash + ".png" } + EndpointGuildWebhooks = func(gID string) string { return EndpointGuilds + gID + "/webhooks" } + EndpointGuildAuditLogs = func(gID string) string { return EndpointGuilds + gID + "/audit-logs" } + EndpointGuildEmojis = func(gID string) string { return EndpointGuilds + gID + "/emojis" } + EndpointGuildEmoji = func(gID, eID string) string { return EndpointGuilds + gID + "/emojis/" + eID } + 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 } + 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" } + EndpointGuildTemplate = func(tID string) string { return EndpointGuilds + "/templates/" + tID } + EndpointGuildTemplates = func(gID string) string { return EndpointGuilds + gID + "/templates" } + EndpointGuildTemplateSync = func(gID, tID string) string { return EndpointGuilds + gID + "/templates/" + tID } + EndpointGuildMemberAvatar = func(gId, uID, aID string) string { return EndpointCDNGuilds + gId + "/users/" + uID + "/avatars/" + aID + ".png" } EndpointGuildMemberAvatarAnimated = func(gId, uID, aID string) string { diff --git a/eventhandlers.go b/eventhandlers.go index 63b7304..18d6248 100644 --- a/eventhandlers.go +++ b/eventhandlers.go @@ -7,59 +7,62 @@ package discordgo // Event type values are used to match the events returned by Discord. // EventTypes surrounded by __ are synthetic and are internal to DiscordGo. const ( - channelCreateEventType = "CHANNEL_CREATE" - channelDeleteEventType = "CHANNEL_DELETE" - channelPinsUpdateEventType = "CHANNEL_PINS_UPDATE" - channelUpdateEventType = "CHANNEL_UPDATE" - connectEventType = "__CONNECT__" - disconnectEventType = "__DISCONNECT__" - eventEventType = "__EVENT__" - guildBanAddEventType = "GUILD_BAN_ADD" - guildBanRemoveEventType = "GUILD_BAN_REMOVE" - guildCreateEventType = "GUILD_CREATE" - guildDeleteEventType = "GUILD_DELETE" - guildEmojisUpdateEventType = "GUILD_EMOJIS_UPDATE" - guildIntegrationsUpdateEventType = "GUILD_INTEGRATIONS_UPDATE" - guildMemberAddEventType = "GUILD_MEMBER_ADD" - guildMemberRemoveEventType = "GUILD_MEMBER_REMOVE" - guildMemberUpdateEventType = "GUILD_MEMBER_UPDATE" - guildMembersChunkEventType = "GUILD_MEMBERS_CHUNK" - guildRoleCreateEventType = "GUILD_ROLE_CREATE" - guildRoleDeleteEventType = "GUILD_ROLE_DELETE" - guildRoleUpdateEventType = "GUILD_ROLE_UPDATE" - guildUpdateEventType = "GUILD_UPDATE" - interactionCreateEventType = "INTERACTION_CREATE" - inviteCreateEventType = "INVITE_CREATE" - inviteDeleteEventType = "INVITE_DELETE" - messageAckEventType = "MESSAGE_ACK" - messageCreateEventType = "MESSAGE_CREATE" - messageDeleteEventType = "MESSAGE_DELETE" - messageDeleteBulkEventType = "MESSAGE_DELETE_BULK" - messageReactionAddEventType = "MESSAGE_REACTION_ADD" - messageReactionRemoveEventType = "MESSAGE_REACTION_REMOVE" - messageReactionRemoveAllEventType = "MESSAGE_REACTION_REMOVE_ALL" - messageUpdateEventType = "MESSAGE_UPDATE" - presenceUpdateEventType = "PRESENCE_UPDATE" - presencesReplaceEventType = "PRESENCES_REPLACE" - rateLimitEventType = "__RATE_LIMIT__" - readyEventType = "READY" - relationshipAddEventType = "RELATIONSHIP_ADD" - relationshipRemoveEventType = "RELATIONSHIP_REMOVE" - resumedEventType = "RESUMED" - threadCreateEventType = "THREAD_CREATE" - threadDeleteEventType = "THREAD_DELETE" - threadListSyncEventType = "THREAD_LIST_SYNC" - threadMemberUpdateEventType = "THREAD_MEMBER_UPDATE" - threadMembersUpdateEventType = "THREAD_MEMBERS_UPDATE" - threadUpdateEventType = "THREAD_UPDATE" - typingStartEventType = "TYPING_START" - userGuildSettingsUpdateEventType = "USER_GUILD_SETTINGS_UPDATE" - userNoteUpdateEventType = "USER_NOTE_UPDATE" - userSettingsUpdateEventType = "USER_SETTINGS_UPDATE" - userUpdateEventType = "USER_UPDATE" - voiceServerUpdateEventType = "VOICE_SERVER_UPDATE" - voiceStateUpdateEventType = "VOICE_STATE_UPDATE" - webhooksUpdateEventType = "WEBHOOKS_UPDATE" + channelCreateEventType = "CHANNEL_CREATE" + channelDeleteEventType = "CHANNEL_DELETE" + channelPinsUpdateEventType = "CHANNEL_PINS_UPDATE" + channelUpdateEventType = "CHANNEL_UPDATE" + connectEventType = "__CONNECT__" + disconnectEventType = "__DISCONNECT__" + eventEventType = "__EVENT__" + guildBanAddEventType = "GUILD_BAN_ADD" + guildBanRemoveEventType = "GUILD_BAN_REMOVE" + guildCreateEventType = "GUILD_CREATE" + guildDeleteEventType = "GUILD_DELETE" + guildEmojisUpdateEventType = "GUILD_EMOJIS_UPDATE" + guildIntegrationsUpdateEventType = "GUILD_INTEGRATIONS_UPDATE" + guildMemberAddEventType = "GUILD_MEMBER_ADD" + guildMemberRemoveEventType = "GUILD_MEMBER_REMOVE" + guildMemberUpdateEventType = "GUILD_MEMBER_UPDATE" + guildMembersChunkEventType = "GUILD_MEMBERS_CHUNK" + guildRoleCreateEventType = "GUILD_ROLE_CREATE" + guildRoleDeleteEventType = "GUILD_ROLE_DELETE" + guildRoleUpdateEventType = "GUILD_ROLE_UPDATE" + guildUpdateEventType = "GUILD_UPDATE" + guildScheduledEventCreateEventType = "GUILD_SCHEDULED_EVENT_CREATE" + guildScheduledEventUpdateEventType = "GUILD_SCHEDULED_EVENT_UPDATE" + guildScheduledEventDeleteEventType = "GUILD_SCHEDULED_EVENT_DELETE" + interactionCreateEventType = "INTERACTION_CREATE" + inviteCreateEventType = "INVITE_CREATE" + inviteDeleteEventType = "INVITE_DELETE" + messageAckEventType = "MESSAGE_ACK" + messageCreateEventType = "MESSAGE_CREATE" + messageDeleteEventType = "MESSAGE_DELETE" + messageDeleteBulkEventType = "MESSAGE_DELETE_BULK" + messageReactionAddEventType = "MESSAGE_REACTION_ADD" + messageReactionRemoveEventType = "MESSAGE_REACTION_REMOVE" + messageReactionRemoveAllEventType = "MESSAGE_REACTION_REMOVE_ALL" + messageUpdateEventType = "MESSAGE_UPDATE" + presenceUpdateEventType = "PRESENCE_UPDATE" + presencesReplaceEventType = "PRESENCES_REPLACE" + rateLimitEventType = "__RATE_LIMIT__" + readyEventType = "READY" + relationshipAddEventType = "RELATIONSHIP_ADD" + relationshipRemoveEventType = "RELATIONSHIP_REMOVE" + resumedEventType = "RESUMED" + threadCreateEventType = "THREAD_CREATE" + threadDeleteEventType = "THREAD_DELETE" + threadListSyncEventType = "THREAD_LIST_SYNC" + threadMemberUpdateEventType = "THREAD_MEMBER_UPDATE" + threadMembersUpdateEventType = "THREAD_MEMBERS_UPDATE" + threadUpdateEventType = "THREAD_UPDATE" + typingStartEventType = "TYPING_START" + userGuildSettingsUpdateEventType = "USER_GUILD_SETTINGS_UPDATE" + userNoteUpdateEventType = "USER_NOTE_UPDATE" + userSettingsUpdateEventType = "USER_SETTINGS_UPDATE" + userUpdateEventType = "USER_UPDATE" + voiceServerUpdateEventType = "VOICE_SERVER_UPDATE" + voiceStateUpdateEventType = "VOICE_STATE_UPDATE" + webhooksUpdateEventType = "WEBHOOKS_UPDATE" ) // channelCreateEventHandler is an event handler for ChannelCreate events. @@ -307,6 +310,66 @@ func (eh guildIntegrationsUpdateEventHandler) Handle(s *Session, i interface{}) } } +// guildScheduledEventCreateEventHandler is an event handler for GuildScheduledEventCreate events. +type guildScheduledEventCreateEventHandler func(*Session, *GuildScheduledEventCreate) + +// Type returns the event type for GuildScheduledEventCreate events. +func (eh guildScheduledEventCreateEventHandler) Type() string { + return guildScheduledEventCreateEventType +} + +// New returns a new instance of GuildScheduledEventCreate. +func (eh guildScheduledEventCreateEventHandler) New() interface{} { + return &GuildScheduledEventCreate{} +} + +// Handle is the handler for GuildScheduledEventCreate events. +func (eh guildScheduledEventCreateEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*GuildScheduledEventCreate); ok { + eh(s, t) + } +} + +// guildScheduledEventUpdateEventHandler is an event handler for GuildScheduledEventUpdate events. +type guildScheduledEventUpdateEventHandler func(*Session, *GuildScheduledEventUpdate) + +// Type returns the event type for GuildScheduledEventUpdate events. +func (eh guildScheduledEventUpdateEventHandler) Type() string { + return guildScheduledEventUpdateEventType +} + +// New returns a new instance of GuildScheduledEventUpdate. +func (eh guildScheduledEventUpdateEventHandler) New() interface{} { + return &GuildScheduledEventUpdate{} +} + +// Handle is the handler for GuildScheduledEventUpdate events. +func (eh guildScheduledEventUpdateEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*GuildScheduledEventUpdate); ok { + eh(s, t) + } +} + +// guildScheduledEventDeleteEventHandler is an event handler for GuildScheduledEventDelete events. +type guildScheduledEventDeleteEventHandler func(*Session, *GuildScheduledEventDelete) + +// Type returns the event type for GuildScheduledEventDelete events. +func (eh guildScheduledEventDeleteEventHandler) Type() string { + return guildScheduledEventDeleteEventType +} + +// New returns a new instance of GuildScheduledEventDelete. +func (eh guildScheduledEventDeleteEventHandler) New() interface{} { + return &GuildScheduledEventDelete{} +} + +// Handle is the handler for GuildScheduledEventDelete events. +func (eh guildScheduledEventDeleteEventHandler) Handle(s *Session, i interface{}) { + if t, ok := i.(*GuildScheduledEventDelete); ok { + eh(s, t) + } +} + // guildMemberAddEventHandler is an event handler for GuildMemberAdd events. type guildMemberAddEventHandler func(*Session, *GuildMemberAdd) @@ -1132,6 +1195,12 @@ func handlerForInterface(handler interface{}) EventHandler { return guildEmojisUpdateEventHandler(v) case func(*Session, *GuildIntegrationsUpdate): return guildIntegrationsUpdateEventHandler(v) + case func(*Session, *GuildScheduledEventCreate): + return guildScheduledEventCreateEventHandler(v) + case func(*Session, *GuildScheduledEventUpdate): + return guildScheduledEventUpdateEventHandler(v) + case func(*Session, *GuildScheduledEventDelete): + return guildScheduledEventDeleteEventHandler(v) case func(*Session, *GuildMemberAdd): return guildMemberAddEventHandler(v) case func(*Session, *GuildMemberRemove): @@ -1228,6 +1297,9 @@ func init() { registerInterfaceProvider(guildDeleteEventHandler(nil)) registerInterfaceProvider(guildEmojisUpdateEventHandler(nil)) registerInterfaceProvider(guildIntegrationsUpdateEventHandler(nil)) + registerInterfaceProvider(guildScheduledEventCreateEventHandler(nil)) + registerInterfaceProvider(guildScheduledEventUpdateEventHandler(nil)) + registerInterfaceProvider(guildScheduledEventDeleteEventHandler(nil)) registerInterfaceProvider(guildMemberAddEventHandler(nil)) registerInterfaceProvider(guildMemberRemoveEventHandler(nil)) registerInterfaceProvider(guildMemberUpdateEventHandler(nil)) diff --git a/events.go b/events.go index dd217d9..cc5d711 100644 --- a/events.go +++ b/events.go @@ -199,6 +199,21 @@ type GuildIntegrationsUpdate struct { GuildID string `json:"guild_id"` } +// GuildScheduledEventCreate is the data for a GuildScheduledEventCreate event. +type GuildScheduledEventCreate struct { + *GuildScheduledEvent +} + +// GuildScheduledEventUpdate is the data for a GuildScheduledEventUpdate event. +type GuildScheduledEventUpdate struct { + *GuildScheduledEvent +} + +// GuildScheduledEventDelete is the data for a GuildScheduledEventDelete event. +type GuildScheduledEventDelete struct { + *GuildScheduledEvent +} + // MessageAck is the data for a MessageAck event. type MessageAck struct { MessageID string `json:"message_id"` diff --git a/examples/scheduled_events/main.go b/examples/scheduled_events/main.go new file mode 100644 index 0000000..218265f --- /dev/null +++ b/examples/scheduled_events/main.go @@ -0,0 +1,84 @@ +package main + +import ( + "flag" + "fmt" + "log" + "os" + "os/signal" + "time" + + "github.com/bwmarrin/discordgo" +) + +// Flags +var ( + GuildID = flag.String("guild", "", "Test guild ID") + VoiceChannelID = flag.String("voice", "", "Test voice channel ID") + BotToken = flag.String("token", "", "Bot token") +) + +func init() { flag.Parse() } + +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() + + event := createAmazingEvent(s) + transformEventToExternalEvent(s, event) + + stop := make(chan os.Signal, 1) + signal.Notify(stop, os.Interrupt) + <-stop + log.Println("Graceful shutdown") + +} + +// Create a new event on guild +func createAmazingEvent(s *discordgo.Session) *discordgo.GuildScheduledEvent { + // Define the starting time (must be in future) + startingTime := time.Now().Add(1 * time.Hour) + // Define the ending time (must be after starting time) + endingTime := startingTime.Add(30 * time.Minute) + // Create the event + scheduledEvent, err := s.GuildScheduledEventCreate(*GuildID, &discordgo.GuildScheduledEventParams{ + Name: "Amazing Event", + Description: "This event will start in 1 hour and last 30 minutes", + ScheduledStartTime: &startingTime, + ScheduledEndTime: &endingTime, + EntityType: discordgo.GuildScheduledEventEntityTypeVoice, + ChannelID: *VoiceChannelID, + PrivacyLevel: discordgo.GuildScheduledEventPrivacyLevelGuildOnly, + }) + if err != nil { + log.Printf("Error creating scheduled event: %v", err) + return nil + } + + fmt.Println("Created scheduled event:", scheduledEvent.Name) + return scheduledEvent +} + +func transformEventToExternalEvent(s *discordgo.Session, event *discordgo.GuildScheduledEvent) { + scheduledEvent, err := s.GuildScheduledEventEdit(*GuildID, event.ID, &discordgo.GuildScheduledEventParams{ + Name: "Amazing Event @ Discord Website", + EntityType: discordgo.GuildScheduledEventEntityTypeExternal, + EntityMetadata: &discordgo.GuildScheduledEventEntityMetadata{ + Location: "https://discord.com", + }, + }) + if err != nil { + log.Printf("Error during transformation of scheduled voice event into external event: %v", err) + return + } + + fmt.Println("Created scheduled event:", scheduledEvent.Name) +} diff --git a/examples/threads/main.go b/examples/threads/main.go index 101a257..9b61791 100644 --- a/examples/threads/main.go +++ b/examples/threads/main.go @@ -21,8 +21,9 @@ const timeout time.Duration = time.Second * 10 var games map[string]time.Time = make(map[string]time.Time) +func init() { flag.Parse() } + func main() { - flag.Parse() s, _ := discordgo.New("Bot " + *BotToken) s.AddHandler(func(s *discordgo.Session, r *discordgo.Ready) { fmt.Println("Bot is ready") diff --git a/restapi.go b/restapi.go index 0f2a161..41796fe 100644 --- a/restapi.go +++ b/restapi.go @@ -2757,3 +2757,115 @@ func (s *Session) FollowupMessageEdit(appID string, interaction *Interaction, me func (s *Session) FollowupMessageDelete(appID string, interaction *Interaction, messageID string) error { return s.WebhookMessageDelete(appID, interaction.Token, messageID) } + +// ------------------------------------------------------------------------------------------------ +// Functions specific to guilds scheduled events +// ------------------------------------------------------------------------------------------------ + +// GuildScheduledEvents returns an array of GuildScheduledEvent for a guild +// guildID : The ID of a Guild +// userCount : Whether to include the user count in the response +func (s *Session) GuildScheduledEvents(guildID string, userCount bool) (st []*GuildScheduledEvent, err error) { + uri := EndpointGuildScheduledEvents(guildID) + if userCount { + uri += "?with_user_count=true" + } + + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildScheduledEvents(guildID)) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildScheduledEvent returns a specific GuildScheduledEvent in a guild +// guildID : The ID of a Guild +// eventID : The ID of the event +// userCount : Whether to include the user count in the response +func (s *Session) GuildScheduledEvent(guildID, eventID string, userCount bool) (st *GuildScheduledEvent, err error) { + uri := EndpointGuildScheduledEvent(guildID, eventID) + if userCount { + uri += "?with_user_count=true" + } + + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildScheduledEvent(guildID, eventID)) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildScheduledEventCreate creates a GuildScheduledEvent for a guild and returns it +// guildID : The ID of a Guild +// eventID : The ID of the event +func (s *Session) GuildScheduledEventCreate(guildID string, event *GuildScheduledEventParams) (st *GuildScheduledEvent, err error) { + body, err := s.RequestWithBucketID("POST", EndpointGuildScheduledEvents(guildID), event, EndpointGuildScheduledEvents(guildID)) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildScheduledEventEdit updates a specific event for a guild and returns it. +// guildID : The ID of a Guild +// eventID : The ID of the event +func (s *Session) GuildScheduledEventEdit(guildID, eventID string, event *GuildScheduledEventParams) (st *GuildScheduledEvent, err error) { + body, err := s.RequestWithBucketID("PATCH", EndpointGuildScheduledEvent(guildID, eventID), event, EndpointGuildScheduledEvent(guildID, eventID)) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} + +// GuildScheduledEventDelete deletes a specific GuildScheduledEvent in a guild +// guildID : The ID of a Guild +// eventID : The ID of the event +func (s *Session) GuildScheduledEventDelete(guildID, eventID string) (err error) { + _, err = s.RequestWithBucketID("DELETE", EndpointGuildScheduledEvent(guildID, eventID), nil, EndpointGuildScheduledEvent(guildID, eventID)) + return +} + +// GuildScheduledEventUsers returns an array of GuildScheduledEventUser for a particular event in a guild +// guildID : The ID of a Guild +// eventID : The ID of the event +// limit : The maximum number of users to return (Max 100) +// withMember : Whether to include the member object in the response +// beforeID : If is not empty all returned users entries will be before the given ID +// afterID : If is not empty all returned users entries will be after the given ID +func (s *Session) GuildScheduledEventUsers(guildID, eventID string, limit int, withMember bool, beforeID, afterID string) (st []*GuildScheduledEventUser, err error) { + uri := EndpointGuildScheduledEventUsers(guildID, eventID) + + queryParams := url.Values{} + if withMember { + queryParams.Set("with_member", "true") + } + if limit > 0 { + queryParams.Set("limit", strconv.Itoa(limit)) + } + if beforeID != "" { + queryParams.Set("before", beforeID) + } + if afterID != "" { + queryParams.Set("after", afterID) + } + + if len(queryParams) > 0 { + uri += "?" + queryParams.Encode() + } + + body, err := s.RequestWithBucketID("GET", uri, nil, EndpointGuildScheduledEventUsers(guildID, eventID)) + if err != nil { + return + } + + err = unmarshal(body, &st) + return +} diff --git a/structs.go b/structs.go index 02adaa1..516dcf0 100644 --- a/structs.go +++ b/structs.go @@ -767,6 +767,149 @@ type GuildPreview struct { Description string `json:"description"` } +// GuildScheduledEvent is a representation of a scheduled event in a guild. Only for retrieval of the data. +// https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event +type GuildScheduledEvent struct { + // The ID of the scheduled event + ID string `json:"id"` + // The guild id which the scheduled event belongs to + GuildID string `json:"guild_id"` + // The channel id in which the scheduled event will be hosted, or null if scheduled entity type is EXTERNAL + ChannelID string `json:"channel_id"` + // The id of the user that created the scheduled event + CreatorID string `json:"creator_id"` + // The name of the scheduled event (1-100 characters) + Name string `json:"name"` + // The description of the scheduled event (1-1000 characters) + Description string `json:"description"` + // The time the scheduled event will start + ScheduledStartTime time.Time `json:"scheduled_start_time"` + // The time the scheduled event will end, required only when entity_type is EXTERNAL + ScheduledEndTime *time.Time `json:"scheduled_end_time"` + // The privacy level of the scheduled event + PrivacyLevel GuildScheduledEventPrivacyLevel `json:"privacy_level"` + // The status of the scheduled event + Status GuildScheduledEventStatus `json:"status"` + // Type of the entity where event would be hosted + // See field requirements + // https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-field-requirements-by-entity-type + EntityType GuildScheduledEventEntityType `json:"entity_type"` + // The id of an entity associated with a guild scheduled event + EntityID string `json:"entity_id"` + // Additional metadata for the guild scheduled event + EntityMetadata GuildScheduledEventEntityMetadata `json:"entity_metadata"` + // The user that created the scheduled event + Creator *User `json:"creator"` + // The number of users subscribed to the scheduled event + UserCount int `json:"user_count"` + // The cover image hash of the scheduled event + // see https://discord.com/developers/docs/reference#image-formatting for more + // information about image formatting + Image string `json:"image"` +} + +// GuildScheduledEventParams are the parameters allowed for creating or updating a scheduled event +// https://discord.com/developers/docs/resources/guild-scheduled-event#create-guild-scheduled-event +type GuildScheduledEventParams struct { + // The channel id in which the scheduled event will be hosted, or null if scheduled entity type is EXTERNAL + ChannelID string `json:"channel_id,omitempty"` + // The name of the scheduled event (1-100 characters) + Name string `json:"name,omitempty"` + // The description of the scheduled event (1-1000 characters) + Description string `json:"description,omitempty"` + // The time the scheduled event will start + ScheduledStartTime *time.Time `json:"scheduled_start_time,omitempty"` + // The time the scheduled event will end, required only when entity_type is EXTERNAL + ScheduledEndTime *time.Time `json:"scheduled_end_time,omitempty"` + // The privacy level of the scheduled event + PrivacyLevel GuildScheduledEventPrivacyLevel `json:"privacy_level,omitempty"` + // The status of the scheduled event + Status GuildScheduledEventStatus `json:"status,omitempty"` + // Type of the entity where event would be hosted + // See field requirements + // https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-field-requirements-by-entity-type + EntityType GuildScheduledEventEntityType `json:"entity_type,omitempty"` + // Additional metadata for the guild scheduled event + EntityMetadata *GuildScheduledEventEntityMetadata `json:"entity_metadata,omitempty"` + // The cover image hash of the scheduled event + // see https://discord.com/developers/docs/reference#image-formatting for more + // information about image formatting + Image string `json:"image,omitempty"` +} + +// MarshalJSON is a helper function to marshal GuildScheduledEventParams +func (p GuildScheduledEventParams) MarshalJSON() ([]byte, error) { + type guildScheduledEventParams GuildScheduledEventParams + + if p.EntityType == GuildScheduledEventEntityTypeExternal && p.ChannelID == "" { + return json.Marshal(struct { + guildScheduledEventParams + ChannelID json.RawMessage `json:"channel_id"` + }{ + guildScheduledEventParams: guildScheduledEventParams(p), + ChannelID: json.RawMessage("null"), + }) + } + + return json.Marshal(guildScheduledEventParams(p)) +} + +// GuildScheduledEventEntityMetadata holds additional metadata for guild scheduled event. +type GuildScheduledEventEntityMetadata struct { + // location of the event (1-100 characters) + // required for events with 'entity_type': EXTERNAL + Location string `json:"location"` +} + +// GuildScheduledEventPrivacyLevel is the privacy level of a scheduled event. +// https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-privacy-level +type GuildScheduledEventPrivacyLevel int + +const ( + // GuildScheduledEventPrivacyLevelGuildOnly makes the scheduled + // event is only accessible to guild members + GuildScheduledEventPrivacyLevelGuildOnly GuildScheduledEventPrivacyLevel = 2 +) + +// GuildScheduledEventStatus is the status of a scheduled event +// Valid Guild Scheduled Event Status Transitions : +// SCHEDULED --> ACTIVE --> COMPLETED +// SCHEDULED --> CANCELED +// https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-status +type GuildScheduledEventStatus int + +const ( + // GuildScheduledEventStatusScheduled represents the current event is in scheduled state + GuildScheduledEventStatusScheduled = 1 + // GuildScheduledEventStatusActive represents the current event is in active state + GuildScheduledEventStatusActive = 2 + // GuildScheduledEventStatusCompleted represents the current event is in completed state + GuildScheduledEventStatusCompleted = 3 + // GuildScheduledEventStatusCanceled represents the current event is in canceled state + GuildScheduledEventStatusCanceled = 4 +) + +// GuildScheduledEventEntityType is the type of entity associated with a guild scheduled event. +// https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-types +type GuildScheduledEventEntityType int + +const ( + // GuildScheduledEventEntityTypeStageInstance represents a stage channel + GuildScheduledEventEntityTypeStageInstance = 1 + // GuildScheduledEventEntityTypeVoice represents a voice channel + GuildScheduledEventEntityTypeVoice = 2 + // GuildScheduledEventEntityTypeExternal represents an external event + GuildScheduledEventEntityTypeExternal = 3 +) + +// GuildScheduledEventUser is a user subscribed to a scheduled event. +// https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-user-object +type GuildScheduledEventUser struct { + GuildScheduledEventID string `json:"guild_scheduled_event_id"` + User *User `json:"user"` + Member *Member `json:"member"` +} + // A GuildTemplate represents type GuildTemplate struct { // The unique code for the guild template @@ -1876,7 +2019,8 @@ const ( IntentDirectMessages | IntentDirectMessageReactions | IntentDirectMessageTyping | - IntentGuildScheduledEvents + IntentGuildScheduledEvents | + IntentsGuildScheduledEvents IntentsAll = IntentsAllWithoutPrivileged | IntentGuildMembers |