Skip to content
This repository was archived by the owner on Mar 5, 2023. It is now read-only.

Commit e570648

Browse files
authored
Forum channels (bwmarrin#1246)
* feat: forums Initial implementation of forum channels REST API * fix: linter * feat: cosmetic changes Added periods in the end of all documentation comments * fix(ChannelFlagRequireTag): incorrect value Fix incorrect value of ChannelFlagRequireTag. * refactor(DefaultReaction): rename to ForumDefaultReaction Rename DefaultReaction to ForumDefaultReaction. * fix(Channel): use ForumDefaultReaction Use ForumDefaultReaction instead of DefaultReaction in DefaultReactionEmoji field. * fix: gofmt * feat: cosmetic changes * Change "GUILD_FORUM" to "forum" in comment to ForumTag * Fix spacing for documentation of embeds parameter in ForumThreadStartEmbeds * docs(ForumThreadStartComplex): align parameters Align documentation for parameters of ForumThreadStartComplex.
1 parent fea3d77 commit e570648

File tree

2 files changed

+176
-22
lines changed

2 files changed

+176
-22
lines changed

restapi.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2496,6 +2496,100 @@ func (s *Session) ThreadStart(channelID, name string, typ ChannelType, archiveDu
24962496
})
24972497
}
24982498

2499+
// ForumThreadStartComplex starts a new thread (creates a post) in a forum channel.
2500+
// channelID : Channel to create thread in.
2501+
// threadData : Parameters of the thread.
2502+
// messageData : Parameters of the starting message.
2503+
func (s *Session) ForumThreadStartComplex(channelID string, threadData *ThreadStart, messageData *MessageSend) (th *Channel, err error) {
2504+
endpoint := EndpointChannelThreads(channelID)
2505+
2506+
// TODO: Remove this when compatibility is not required.
2507+
if messageData.Embed != nil {
2508+
if messageData.Embeds == nil {
2509+
messageData.Embeds = []*MessageEmbed{messageData.Embed}
2510+
} else {
2511+
err = fmt.Errorf("cannot specify both Embed and Embeds")
2512+
return
2513+
}
2514+
}
2515+
2516+
for _, embed := range messageData.Embeds {
2517+
if embed.Type == "" {
2518+
embed.Type = "rich"
2519+
}
2520+
}
2521+
2522+
// TODO: Remove this when compatibility is not required.
2523+
files := messageData.Files
2524+
if messageData.File != nil {
2525+
if files == nil {
2526+
files = []*File{messageData.File}
2527+
} else {
2528+
err = fmt.Errorf("cannot specify both File and Files")
2529+
return
2530+
}
2531+
}
2532+
2533+
data := struct {
2534+
*ThreadStart
2535+
Message *MessageSend `json:"message"`
2536+
}{ThreadStart: threadData, Message: messageData}
2537+
2538+
var response []byte
2539+
if len(files) > 0 {
2540+
contentType, body, encodeErr := MultipartBodyWithJSON(data, files)
2541+
if encodeErr != nil {
2542+
return th, encodeErr
2543+
}
2544+
2545+
response, err = s.request("POST", endpoint, contentType, body, endpoint, 0)
2546+
} else {
2547+
response, err = s.RequestWithBucketID("POST", endpoint, data, endpoint)
2548+
}
2549+
if err != nil {
2550+
return
2551+
}
2552+
2553+
err = unmarshal(response, &th)
2554+
return
2555+
}
2556+
2557+
// ForumThreadStart starts a new thread (post) in a forum channel.
2558+
// channelID : Channel to create thread in.
2559+
// name : Name of the thread.
2560+
// archiveDuration : Auto archive duration.
2561+
// content : Content of the starting message.
2562+
func (s *Session) ForumThreadStart(channelID, name string, archiveDuration int, content string) (th *Channel, err error) {
2563+
return s.ForumThreadStartComplex(channelID, &ThreadStart{
2564+
Name: name,
2565+
AutoArchiveDuration: archiveDuration,
2566+
}, &MessageSend{Content: content})
2567+
}
2568+
2569+
// ForumThreadStartEmbed starts a new thread (post) in a forum channel.
2570+
// channelID : Channel to create thread in.
2571+
// name : Name of the thread.
2572+
// archiveDuration : Auto archive duration.
2573+
// embed : Embed data of the starting message.
2574+
func (s *Session) ForumThreadStartEmbed(channelID, name string, archiveDuration int, embed *MessageEmbed) (th *Channel, err error) {
2575+
return s.ForumThreadStartComplex(channelID, &ThreadStart{
2576+
Name: name,
2577+
AutoArchiveDuration: archiveDuration,
2578+
}, &MessageSend{Embeds: []*MessageEmbed{embed}})
2579+
}
2580+
2581+
// ForumThreadStartEmbeds starts a new thread (post) in a forum channel.
2582+
// channelID : Channel to create thread in.
2583+
// name : Name of the thread.
2584+
// archiveDuration : Auto archive duration.
2585+
// embeds : Embeds data of the starting message.
2586+
func (s *Session) ForumThreadStartEmbeds(channelID, name string, archiveDuration int, embeds []*MessageEmbed) (th *Channel, err error) {
2587+
return s.ForumThreadStartComplex(channelID, &ThreadStart{
2588+
Name: name,
2589+
AutoArchiveDuration: archiveDuration,
2590+
}, &MessageSend{Embeds: embeds})
2591+
}
2592+
24992593
// ThreadJoin adds current user to a thread
25002594
func (s *Session) ThreadJoin(id string) error {
25012595
endpoint := EndpointThreadMember(id, "@me")

structs.go

Lines changed: 82 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,20 @@ const (
254254
ChannelTypeGuildPublicThread ChannelType = 11
255255
ChannelTypeGuildPrivateThread ChannelType = 12
256256
ChannelTypeGuildStageVoice ChannelType = 13
257+
ChannelTypeGuildForum ChannelType = 15
258+
)
259+
260+
// ChannelFlags represent flags of a channel/thread.
261+
type ChannelFlags int
262+
263+
// Block containing known ChannelFlags values.
264+
const (
265+
// ChannelFlagPinned indicates whether the thread is pinned in the forum channel.
266+
// NOTE: forum threads only.
267+
ChannelFlagPinned ChannelFlags = 1 << 1
268+
// ChannelFlagRequireTag indicates whether a tag is required to be specified when creating a thread.
269+
// NOTE: forum channels only.
270+
ChannelFlagRequireTag ChannelFlags = 1 << 4
257271
)
258272

259273
// A Channel holds all data related to an individual Discord channel.
@@ -332,6 +346,18 @@ type Channel struct {
332346

333347
// All thread members. State channels only.
334348
Members []*ThreadMember `json:"-"`
349+
350+
// Channel flags.
351+
Flags ChannelFlags `json:"flags"`
352+
353+
// The set of tags that can be used in a forum channel.
354+
AvailableTags []ForumTag `json:"available_tags"`
355+
356+
// The IDs of the set of tags that have been applied to a thread in a forum channel.
357+
AppliedTags []string `json:"applied_tags"`
358+
359+
// Emoji to use as the default reaction to a forum post.
360+
DefaultReactionEmoji ForumDefaultReaction `json:"default_reaction_emoji"`
335361
}
336362

337363
// Mention returns a string which mentions the channel
@@ -355,13 +381,22 @@ type ChannelEdit struct {
355381
PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites,omitempty"`
356382
ParentID string `json:"parent_id,omitempty"`
357383
RateLimitPerUser *int `json:"rate_limit_per_user,omitempty"`
384+
Flags *ChannelFlags `json:"flags,omitempty"`
358385

359386
// NOTE: threads only
360387

361388
Archived *bool `json:"archived,omitempty"`
362389
AutoArchiveDuration int `json:"auto_archive_duration,omitempty"`
363390
Locked *bool `json:"locked,omitempty"`
364391
Invitable *bool `json:"invitable,omitempty"`
392+
393+
// NOTE: forum channels only
394+
395+
AvailableTags *[]ForumTag `json:"available_tags,omitempty"`
396+
DefaultReactionEmoji *ForumDefaultReaction `json:"default_reaction_emoji,omitempty"`
397+
398+
// NOTE: forum threads only
399+
AppliedTags *[]string `json:"applied_tags,omitempty"`
365400
}
366401

367402
// A ChannelFollow holds data returned after following a news channel
@@ -395,6 +430,9 @@ type ThreadStart struct {
395430
Type ChannelType `json:"type,omitempty"`
396431
Invitable bool `json:"invitable"`
397432
RateLimitPerUser int `json:"rate_limit_per_user,omitempty"`
433+
434+
// NOTE: forum threads only
435+
AppliedTags []string `json:"applied_tags,omitempty"`
398436
}
399437

400438
// ThreadMetadata contains a number of thread-specific channel fields that are not needed by other channel types.
@@ -438,6 +476,24 @@ type AddedThreadMember struct {
438476
Presence *Presence `json:"presence"`
439477
}
440478

479+
// ForumDefaultReaction specifies emoji to use as the default reaction to a forum post.
480+
// NOTE: Exactly one of EmojiID and EmojiName must be set.
481+
type ForumDefaultReaction struct {
482+
// The id of a guild's custom emoji.
483+
EmojiID string `json:"emoji_id,omitempty"`
484+
// The unicode character of the emoji.
485+
EmojiName string `json:"emoji_name,omitempty"`
486+
}
487+
488+
// ForumTag represents a tag that is able to be applied to a thread in a forum channel.
489+
type ForumTag struct {
490+
ID string `json:"id,omitempty"`
491+
Name string `json:"name"`
492+
Moderated bool `json:"moderated"`
493+
EmojiID string `json:"emoji_id,omitempty"`
494+
EmojiName string `json:"emoji_name,omitempty"`
495+
}
496+
441497
// Emoji struct holds data related to Emoji's
442498
type Emoji struct {
443499
ID string `json:"id"`
@@ -2074,6 +2130,7 @@ const (
20742130
ErrCodeUnknownGuildWelcomeScreen = 10069
20752131
ErrCodeUnknownGuildScheduledEvent = 10070
20762132
ErrCodeUnknownGuildScheduledEventUser = 10071
2133+
ErrUnknownTag = 10087
20772134

20782135
ErrCodeBotsCannotUseEndpoint = 20001
20792136
ErrCodeOnlyBotsCanUseEndpoint = 20002
@@ -2087,28 +2144,30 @@ const (
20872144
ErrCodeStageTopicContainsNotAllowedWordsForPublicStages = 20031
20882145
ErrCodeGuildPremiumSubscriptionLevelTooLow = 20035
20892146

2090-
ErrCodeMaximumGuildsReached = 30001
2091-
ErrCodeMaximumPinsReached = 30003
2092-
ErrCodeMaximumNumberOfRecipientsReached = 30004
2093-
ErrCodeMaximumGuildRolesReached = 30005
2094-
ErrCodeMaximumNumberOfWebhooksReached = 30007
2095-
ErrCodeMaximumNumberOfEmojisReached = 30008
2096-
ErrCodeTooManyReactions = 30010
2097-
ErrCodeMaximumNumberOfGuildChannelsReached = 30013
2098-
ErrCodeMaximumNumberOfAttachmentsInAMessageReached = 30015
2099-
ErrCodeMaximumNumberOfInvitesReached = 30016
2100-
ErrCodeMaximumNumberOfAnimatedEmojisReached = 30018
2101-
ErrCodeMaximumNumberOfServerMembersReached = 30019
2102-
ErrCodeMaximumNumberOfGuildDiscoverySubcategoriesReached = 30030
2103-
ErrCodeGuildAlreadyHasATemplate = 30031
2104-
ErrCodeMaximumNumberOfThreadParticipantsReached = 30033
2105-
ErrCodeMaximumNumberOfBansForNonGuildMembersHaveBeenExceeded = 30035
2106-
ErrCodeMaximumNumberOfBansFetchesHasBeenReached = 30037
2107-
ErrCodeMaximumNumberOfUncompletedGuildScheduledEventsReached = 30038
2108-
ErrCodeMaximumNumberOfStickersReached = 30039
2109-
ErrCodeMaximumNumberOfPruneRequestsHasBeenReached = 30040
2110-
ErrCodeMaximumNumberOfGuildWidgetSettingsUpdatesHasBeenReached = 30042
2111-
ErrCodeMaximumNumberOfEditsToMessagesOlderThanOneHourReached = 30046
2147+
ErrCodeMaximumGuildsReached = 30001
2148+
ErrCodeMaximumPinsReached = 30003
2149+
ErrCodeMaximumNumberOfRecipientsReached = 30004
2150+
ErrCodeMaximumGuildRolesReached = 30005
2151+
ErrCodeMaximumNumberOfWebhooksReached = 30007
2152+
ErrCodeMaximumNumberOfEmojisReached = 30008
2153+
ErrCodeTooManyReactions = 30010
2154+
ErrCodeMaximumNumberOfGuildChannelsReached = 30013
2155+
ErrCodeMaximumNumberOfAttachmentsInAMessageReached = 30015
2156+
ErrCodeMaximumNumberOfInvitesReached = 30016
2157+
ErrCodeMaximumNumberOfAnimatedEmojisReached = 30018
2158+
ErrCodeMaximumNumberOfServerMembersReached = 30019
2159+
ErrCodeMaximumNumberOfGuildDiscoverySubcategoriesReached = 30030
2160+
ErrCodeGuildAlreadyHasATemplate = 30031
2161+
ErrCodeMaximumNumberOfThreadParticipantsReached = 30033
2162+
ErrCodeMaximumNumberOfBansForNonGuildMembersHaveBeenExceeded = 30035
2163+
ErrCodeMaximumNumberOfBansFetchesHasBeenReached = 30037
2164+
ErrCodeMaximumNumberOfUncompletedGuildScheduledEventsReached = 30038
2165+
ErrCodeMaximumNumberOfStickersReached = 30039
2166+
ErrCodeMaximumNumberOfPruneRequestsHasBeenReached = 30040
2167+
ErrCodeMaximumNumberOfGuildWidgetSettingsUpdatesHasBeenReached = 30042
2168+
ErrCodeMaximumNumberOfEditsToMessagesOlderThanOneHourReached = 30046
2169+
ErrCodeMaximumNumberOfPinnedThreadsInForumChannelHasBeenReached = 30047
2170+
ErrCodeMaximumNumberOfTagsInForumChannelHasBeenReached = 30048
21122171

21132172
ErrCodeUnauthorized = 40001
21142173
ErrCodeActionRequiredVerifiedAccount = 40002
@@ -2121,6 +2180,7 @@ const (
21212180
ErrCodeMessageAlreadyCrossposted = 40033
21222181
ErrCodeAnApplicationWithThatNameAlreadyExists = 40041
21232182
ErrCodeInteractionHasAlreadyBeenAcknowledged = 40060
2183+
ErrCodeTagNamesMustBeUnique = 40061
21242184

21252185
ErrCodeMissingAccess = 50001
21262186
ErrCodeInvalidAccountType = 50002

0 commit comments

Comments
 (0)