1
0
Fork 0
mirror of https://github.com/diamondburned/arikawa.git synced 2025-10-28 12:54:47 +00:00

api: Threads (#257)

* api/gateway: Implement threads

* discord/api: rename PermissionOverwrites fields to Overwrites

* discord: rename Minutes to ArchiveDuration

* discord: Rename ArchiveDuration constants

Co-authored-by: diamondburned <datutbrus@gmail.com>
This commit is contained in:
Maximilian von Lindern 2021-08-08 22:19:15 +02:00 committed by GitHub
parent 0d7774bd6a
commit c00567599b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 573 additions and 106 deletions

View file

@ -49,10 +49,10 @@ type CreateChannelData struct {
//
// Channel Types: All
Position option.Int `json:"position,omitempty"`
// Permissions are the channel's permission overwrites.
// Overwrites are the channel's permission overwrites.
//
// Channel Types: All
Permissions []discord.Overwrite `json:"permission_overwrites,omitempty"`
Overwrites []discord.Overwrite `json:"permission_overwrites,omitempty"`
// CategoryID is the id of the parent category for a channel.
//
// Channel Types: Text, News, Store, Voice
@ -94,13 +94,20 @@ func (c *Client) CreateChannel(
type MoveChannelData struct {
// ID is the channel id.
ID discord.ChannelID `json:"id"`
// Position is the sorting position of the channel
// Position is the sorting position of the channel.
Position option.Int `json:"position"`
// LockPermissions syncs the permission overwrites with the new parent,
// if moving to a new category.
LockPermissions option.Bool `json:"lock_permissions"`
// CategoryID is the new parent ID for the channel that is moved.
CategoryID discord.ChannelID `json:"parent_id"`
}
// MoveChannel modifies the position of channels in the guild.
//
// Requires MANAGE_CHANNELS.
//
// Fires multiple Channel Update Gateway events.
func (c *Client) MoveChannel(guildID discord.GuildID, data []MoveChannelData) error {
return c.FastRequest(
"PATCH",
@ -126,9 +133,9 @@ type ModifyChannelData struct {
//
// Channel Types: Text, News
Type *discord.ChannelType `json:"type,omitempty"`
// Postion is the position of the channel in the left-hand listing
// Position is the position of the channel in the left-hand listing.
//
// Channel Types: All
// Channel Types: Text, News, Voice, Store, Category
Position option.NullableInt `json:"position,omitempty"`
// Topic is the 0-1024 character channel topic.
//
@ -136,14 +143,14 @@ type ModifyChannelData struct {
Topic option.NullableString `json:"topic,omitempty"`
// NSFW specifies whether the channel is nsfw.
//
// Channel Types: Text, News, Store.
// Channel Types: Text, News, Store
NSFW option.NullableBool `json:"nsfw,omitempty"`
// UserRateLimit is the amount of seconds a user has to wait before sending
// another message (0-21600).
// Bots, as well as users with the permission manage_messages or
// manage_channel, are unaffected.
//
// Channel Types: Text
// Channel Types: Text, Thread
UserRateLimit option.NullableUint `json:"rate_limit_per_user,omitempty"`
// VoiceBitrate is the bitrate (in bits) of the voice channel.
// 8000 to 96000 (128000 for VIP servers)
@ -155,18 +162,44 @@ type ModifyChannelData struct {
//
// Channel Types: Voice
VoiceUserLimit option.NullableUint `json:"user_limit,omitempty"`
// Permissions are the channel or category-specific permissions.
// Overwrites are the channel or category-specific permissions.
//
// Channel Types: All
Permissions *[]discord.Overwrite `json:"permission_overwrites,omitempty"`
// Channel Types: Text, News, Store, Voice, Category
Overwrites *[]discord.Overwrite `json:"permission_overwrites,omitempty"`
// CategoryID is the id of the new parent category for a channel.
//
// Channel Types: Text, News, Store, Voice
CategoryID discord.ChannelID `json:"parent_id,string,omitempty"`
// Icon is a base64 encoded icon.
//
// Channel Types: Group DM
Icon string `json:"icon,omitempty"`
// Archived specifies whether the thread is archived.
Archived option.Bool `json:"archived,omitempty"`
// AutoArchiveDuration is the duration in minutes to automatically archive
// the thread after recent activity.
//
// Note that the three and seven day archive durations require the server
// to be boosted.
AutoArchiveDuration discord.ArchiveDuration `json:"auto_archive_duration,omitempty"`
// Locked specifies whether the thread is locked. When a thread is locked,
// only users with MANAGE_THREADS can unarchive it.
Locked option.Bool `json:"locked,omitempty"`
}
// ModifyChannel updates a channel's settings.
//
// Requires the MANAGE_CHANNELS permission for the guild.
// If modifying a guild channel, requires the MANAGE_CHANNELS permission for
// that guild. If modifying a thread, requires the MANAGE_THREADS permission.
// Furthermore, if modifying permission overwrites, the MANAGE_ROLES permission
// is required. Only permissions your bot has in the guild or channel can be
// allowed/denied (unless your bot has a MANAGE_ROLES overwrite in the
// channel).
//
// Fires a Channel Update event when modifying a guild channel, and a Thread
// Update event when modifying a thread.
func (c *Client) ModifyChannel(channelID discord.ChannelID, data ModifyChannelData) error {
return c.FastRequest("PATCH", EndpointChannels+channelID.String(), httputil.WithJSONBody(data))
}
@ -196,7 +229,8 @@ type EditChannelPermissionData struct {
//
// Requires the MANAGE_ROLES permission.
func (c *Client) EditChannelPermission(
channelID discord.ChannelID, overwriteID discord.Snowflake, data EditChannelPermissionData) error {
channelID discord.ChannelID,
overwriteID discord.Snowflake, data EditChannelPermissionData) error {
return c.FastRequest(
"PUT", EndpointChannels+channelID.String()+"/permissions/"+overwriteID.String(),
@ -245,7 +279,8 @@ func (c *Client) UnpinMessage(channelID discord.ChannelID, messageID discord.Mes
// AddRecipient adds a user to a group direct message. As accessToken is needed,
// clearly this endpoint should only be used for OAuth. AccessToken can be
// obtained with the "gdm.join" scope.
func (c *Client) AddRecipient(channelID discord.ChannelID, userID discord.UserID, accessToken, nickname string) error {
func (c *Client) AddRecipient(
channelID discord.ChannelID, userID discord.UserID, accessToken, nickname string) error {
var params struct {
AccessToken string `json:"access_token"`
@ -285,3 +320,226 @@ func (c *Client) Ack(channelID discord.ChannelID, messageID discord.MessageID, a
httputil.WithJSONBody(ack),
)
}
// https://discord.com/developers/docs/resources/channel#start-thread-with-message-json-params
// and
// https://discord.com/developers/docs/resources/channel#start-thread-without-message-json-params
type StartThreadData struct {
// Name is the 1-100 character channel name.
Name string `json:"name"`
// AutoArchiveDuration is the duration in minutes to automatically archive
// the thread after recent activity.
//
// Note that the three and seven day archive durations require the server
// to be boosted.
AutoArchiveDuration discord.ArchiveDuration `json:"auto_archive_duration"`
// Type is the type of thread to create.
//
// This field can only be used when starting a thread without a message
Type discord.ChannelType `json:"type,omitempty"` // we can omit, since thread types start at 10
}
// StartThreadWithMessage creates a new thread from an existing message.
//
// When called on a GUILD_TEXT channel, creates a GUILD_PUBLIC_THREAD. When
// called on a GUILD_NEWS channel, creates a GUILD_NEWS_THREAD. The id of the
// created thread will be the same as the id of the message, and as such a
// message can only have a single thread created from it.
//
// Fires a Thread Create Gateway event.
func (c *Client) StartThreadWithMessage(
channelID discord.ChannelID,
messageID discord.MessageID, data StartThreadData) (*discord.Channel, error) {
data.Type = 0
var ch *discord.Channel
return ch, c.RequestJSON(
&ch, "POST",
EndpointChannels+channelID.String()+"/messages/"+messageID.String()+"/threads",
httputil.WithJSONBody(data),
)
}
// StartThreadWithoutMessage creates a new thread that is not connected to an
// existing message.
//
// Fires a Thread Create Gateway event.
func (c *Client) StartThreadWithoutMessage(
channelID discord.ChannelID, data StartThreadData) (*discord.Channel, error) {
var ch *discord.Channel
return ch, c.RequestJSON(
&ch, "POST",
EndpointChannels+channelID.String()+"/threads",
httputil.WithJSONBody(data),
)
}
// JoinThread adds the current user to a thread. Also requires the thread is
// not archived.
//
// Fires a Thread Members Update Gateway event.
func (c *Client) JoinThread(threadID discord.ChannelID) error {
return c.FastRequest("PUT", EndpointChannels+threadID.String()+"/thread-members/@me")
}
// AddThreadMember adds another member to a thread. Requires the ability to
// send messages in the thread. Also requires the thread is not archived.
//
// Fires a Thread Members Update Gateway event.
func (c *Client) AddThreadMember(threadID discord.ChannelID, userID discord.UserID) error {
return c.FastRequest(
"PUT",
EndpointChannels+threadID.String()+"/thread-members/"+userID.String(),
)
}
// LeaveThread removes the current user from a thread. Also requires the thread
// is not archived.
//
// Fires a Thread Members Update Gateway event.
func (c *Client) LeaveThread(threadID discord.ChannelID) error {
return c.FastRequest("DELETE", EndpointChannels+threadID.String()+"/thread-members/@me")
}
// RemoveThreadMember removes another member from a thread. Requires the
// MANAGE_THREADS permission, or the creator of the thread if it is a
// discord.GuildPrivateThread. Also requires the thread is not archived.
//
// Fires a Thread Members Update Gateway event.
func (c *Client) RemoveThreadMember(threadID discord.ChannelID, userID discord.UserID) error {
return c.FastRequest(
"DELETE",
EndpointChannels+threadID.String()+"/thread-members/"+userID.String(),
)
}
// ThreadMembers list all members of the thread.
//
// This endpoint is restricted according to whether the GUILD_MEMBERS
// Privileged Intent is enabled for your application.
func (c *Client) ThreadMembers(threadID discord.ChannelID) ([]discord.ThreadMember, error) {
var m []discord.ThreadMember
return m, c.RequestJSON(&m, "GET", EndpointChannels+threadID.String()+"/thread-members")
}
// https://discord.com/developers/docs/resources/guild#list-active-threads-response-body
type ActiveThread struct {
// Threads are the active threads, ordered by descending ID.
Threads []discord.Channel `json:"threads"`
// Members contains a thread member for each of the Threads the current
// user has joined.
Members []discord.ThreadMember `json:"members"`
}
// ActiveThreads returns all the active threads in the guild, including public
// and private threads.
func (c *Client) ActiveThreads(guildID discord.GuildID) ([]ActiveThread, error) {
var t []ActiveThread
return t, c.RequestJSON(&t, "GET", EndpointGuilds+guildID.String()+"/threads/active")
}
// https://discord.com/developers/docs/resources/channel#list-public-archived-threads-response-body
// and
// https://discord.com/developers/docs/resources/channel#list-private-archived-threads-response-body
// and
// https://discord.com/developers/docs/resources/channel#list-private-archived-threads-response-body
type ArchivedThread struct {
// Threads are the active threads, ordered by descending ArchiveTimestamp.
Threads []discord.Channel `json:"threads"`
// Members contains a thread member for each of the Threads the current
// user has joined.
Members []discord.ThreadMember `json:"members"`
// More specifies whether there are potentially additional threads that
// could be returned on a subsequent call.
More bool `json:"has_more"`
}
// PublicArchivedThreadsBefore returns archived threads in the channel that are
// public.
//
// When called on a GUILD_TEXT channel, returns threads of type
// GUILD_PUBLIC_THREAD. When called on a GUILD_NEWS channel returns threads of
// type GUILD_NEWS_THREAD.
//
// Threads are ordered by ArchiveTimestamp, in descending order.
//
// Requires the READ_MESSAGE_HISTORY permission.
func (c *Client) PublicArchivedThreadsBefore(
channelID discord.ChannelID,
before discord.Timestamp, limit uint) ([]ArchivedThread, error) {
var param struct {
Before string `schema:"before,omitempty"`
Limit uint `schema:"limit"`
}
if before.IsValid() {
param.Before = before.Format(discord.TimestampFormat)
}
param.Limit = limit
var t []ArchivedThread
return t, c.RequestJSON(
&t, "GET",
EndpointChannels+channelID.String()+"/threads/archived/public",
httputil.WithSchema(c, param),
)
}
// PrivateArchivedThreadsBefore returns archived threads in the channel that
// are of type GUILD_PRIVATE_THREAD.
//
// Threads are ordered by ArchiveTimestamp, in descending order.
//
// Requires both the READ_MESSAGE_HISTORY and MANAGE_THREADS permissions.
func (c *Client) PrivateArchivedThreadsBefore(
channelID discord.ChannelID,
before discord.Timestamp, limit uint) ([]ArchivedThread, error) {
var param struct {
Before string `schema:"before,omitempty"`
Limit uint `schema:"limit"`
}
if before.IsValid() {
param.Before = before.Format(discord.TimestampFormat)
}
param.Limit = limit
var t []ArchivedThread
return t, c.RequestJSON(
&t, "GET",
EndpointChannels+channelID.String()+"/threads/archived/private",
httputil.WithSchema(c, param),
)
}
// JoinedPrivateArchivedThreadsBefore returns archived threads in the channel
// that are of type GUILD_PRIVATE_THREAD, and the user has joined.
//
// Threads are ordered by their ID, in descending order.
//
// Requires the READ_MESSAGE_HISTORY permission
func (c *Client) JoinedPrivateArchivedThreadsBefore(
channelID discord.ChannelID,
before discord.Timestamp, limit uint) ([]ArchivedThread, error) {
var param struct {
Before string `schema:"before,omitempty"`
Limit uint `schema:"limit"`
}
if before.IsValid() {
param.Before = before.Format(discord.TimestampFormat)
}
param.Limit = limit
var t []ArchivedThread
return t, c.RequestJSON(
&t, "GET",
EndpointChannels+channelID.String()+"/users/@me/threads/archived/private",
httputil.WithSchema(c, param),
)
}

View file

@ -15,6 +15,9 @@ type Channel struct {
// ID is the id of this channel.
ID ChannelID `json:"id"`
// GuildID is the id of the guild.
//
// This field may be missing for some channel objects received over gateway
// guild dispatches.
GuildID GuildID `json:"guild_id,omitempty"`
// Type is the type of channel.
@ -24,8 +27,9 @@ type Channel struct {
// Position is the sorting position of the channel.
Position int `json:"position,omitempty"`
// Permissions are the explicit permission overrides for members and roles.
Permissions []Overwrite `json:"permission_overwrites,omitempty"`
// Overwrites are the explicit permission overrides for members
// and roles.
Overwrites []Overwrite `json:"permission_overwrites,omitempty"`
// Name is the name of the channel (2-100 characters).
Name string `json:"name,omitempty"`
@ -50,16 +54,17 @@ type Channel struct {
DMRecipients []User `json:"recipients,omitempty"`
// Icon is the icon hash.
Icon Hash `json:"icon,omitempty"`
// DMOwnerID is the id of the DM creator.
DMOwnerID UserID `json:"owner_id,omitempty"`
// OwnerID is the id of the DM or thread creator.
OwnerID UserID `json:"owner_id,omitempty"`
// AppID is the application id of the group DM creator if it is
// bot-created.
AppID AppID `json:"application_id,omitempty"`
// ParentID for guild channels: id of the parent category for a channel
// (each parent category can contain up to 50 channels), for threads: the
// id of the text channel this thread was created.
ParentID ChannelID `json:"parent_id,omitempty"`
// CategoryID is the id of the parent category for a channel (each parent
// category can contain up to 50 channels).
CategoryID ChannelID `json:"parent_id,omitempty"`
// LastPinTime is when the last pinned message was pinned.
LastPinTime Timestamp `json:"last_pin_timestamp,omitempty"`
@ -67,6 +72,29 @@ type Channel struct {
RTCRegionID string `json:"rtc_region,omitempty"`
// VideoQualityMode is the camera video quality mode of the voice channel.
VideoQualityMode VideoQualityMode `json:"video_quality_mode,omitempty"`
// MessageCount is an approximate count of messages in a thread. However,
// counting stops at 50.
MessageCount int `json:"message_count,omitempty"`
// MemberCount is an approximate count of users in a thread. However,
// counting stops at 50.
MemberCount int `json:"member_count,omitempty"`
// ThreadMetadata contains thread-specific fields not needed by other
// channels.
ThreadMetadata *ThreadMetadata `json:"thread_metadata,omitempty"`
// ThreadMember is the thread member object for the current user, if they
// have joined the thread, only included on certain API endpoints.
ThreadMember *ThreadMember `json:"thread_member,omitempty"`
// DefaultAutoArchiveDuration is the default duration for newly created
// threads, in minutes, to automatically archive the thread after recent
// activity.
DefaultAutoArchiveDuration ArchiveDuration `json:"default_auto_archive_duration,omitempty"`
// SelfPermissions are the computed permissions for the invoking user in
// the channel, including overwrites, only included when part of the
// resolved data received on a slash command interaction.
SelfPermissions Permissions `json:"permissions,omitempty,string"`
}
func (ch *Channel) UnmarshalJSON(data []byte) error {
@ -210,3 +238,46 @@ const (
AutoVideoQuality VideoQualityMode = iota + 1
FullVideoQuality
)
// ThreadMetadata contains a number of thread-specific channel fields that are
// not needed by other channel types.
//
// https://discord.com/developers/docs/resources/channel#thread-metadata-object
type ThreadMetadata struct {
// Archived specifies whether the thread is archived.
Archived bool `json:"archived"`
// AutoArchiveDuration is the duration in minutes to automatically archive
// the thread after recent activity.
AutoArchiveDuration ArchiveDuration `json:"auto_archive_duration"`
// ArchiveTimestamp timestamp when the thread's archive status was last
// changed, used for calculating recent activity.
ArchiveTimestamp Timestamp `json:"archive_timestamp"`
// Locked specifies whether the thread is locked; when a thread is locked,
// only users with MANAGE_THREADS can unarchive it.
Locked bool `json:"locked,omitempty"`
}
type ThreadMember struct {
// ID is the id of the thread.
//
// This field will be omitted on the member sent within each thread in the
// guild create event.
ID ChannelID `json:"id,omitempty"`
// UserID is the id of the user.
//
// This field will be omitted on the member sent within each thread in the
// guild create event.
UserID UserID `json:"user_id,omitempty"`
// Member is the member, only included in Thread Members Update Events.
Member *Member `json:"member,omitempty"`
// Presence is the presence, only included in Thread Members Update Events.
Presence *Presence `json:"presence,omitempty"`
// JoinTimestamp is the time the current user last joined the thread.
JoinTimestamp Timestamp `json:"join_timestamp"`
// Flags are any user-thread settings.
Flags ThreadMemberFlags `json:"flags"`
}
// ThreadMemberFlags are the flags of a ThreadMember.
// Currently, none are documented.
type ThreadMemberFlags uint64

View file

@ -470,7 +470,7 @@ var DefaultMemberColor Color = 0x0
// MemberColor computes the effective color of the Member, taking into account
// the role colors.
func MemberColor(guild Guild, member Member) Color {
var c = DefaultMemberColor
c := DefaultMemberColor
var pos int
for _, r := range guild.Roles {
@ -488,3 +488,32 @@ func MemberColor(guild Guild, member Member) Color {
return c
}
// Presence represents a partial Presence structure used by other structs to be
// easily embedded. It does not contain any ID to identify who it belongs
// to. For more information, refer to the PresenceUpdateEvent struct.
type Presence struct {
// User is the user presence is being updated for. Only the ID field is
// guaranteed to be valid per Discord documentation.
User User `json:"user"`
// GuildID is the id of the guild
GuildID GuildID `json:"guild_id"`
// Status is either "idle", "dnd", "online", or "offline".
Status Status `json:"status"`
// Activities are the user's current activities.
Activities []Activity `json:"activities"`
// ClientStatus is the user's platform-dependent status.
ClientStatus ClientStatus `json:"client_status"`
}
type ClientStatus struct {
// Desktop is the user's status set for an active desktop (Windows,
// Linux, Mac) application session.
Desktop Status `json:"desktop,omitempty"`
// Mobile is the user's status set for an active mobile (iOS, Android)
// application session.
Mobile Status `json:"mobile,omitempty"`
// Web is the user's status set for an active web (browser, bot
// account) application session.
Web Status `json:"web,omitempty"`
}

View file

@ -178,3 +178,15 @@ const (
// Kick kicks the subscriber from the guild.
Kick
)
// Status is the enumerate type for a user's status.
type Status string
const (
UnknownStatus Status = ""
OnlineStatus Status = "online"
DoNotDisturbStatus Status = "dnd"
IdleStatus Status = "idle"
InvisibleStatus Status = "invisible"
OfflineStatus Status = "offline"
)

View file

@ -138,9 +138,22 @@ const (
GuildDiscoveryRequalifiedMessage
GuildDiscoveryGracePeriodInitialWarning
GuildDiscoveryGracePeriodFinalWarning
_
// ThreadCreatedMessage is a new message sent to the parent GuildText
// channel, used to inform users that a thread has been created. It is
// currently only sent in one case: when a GuildPublicThread is created
// from an older message (older is still TBD, but is currently set to a
// very small value). The message contains a message reference with the
// GuildID and ChannelID of the thread. The content of the message is the
// name of the thread.
ThreadCreatedMessage
InlinedReplyMessage
ApplicationCommandMessage
// ThreadStarterMessage is a new message sent as the first message in
// threads that are started from an existing message in the parent channel.
// It only contains a message reference field that points to the message
// from which the thread was started.
ThreadStarterMessage
GuildInviteReminderMessage
)
type MessageFlags enum.Enum

View file

@ -66,8 +66,23 @@ const (
PermissionManageRoles
// Allows management and editing of webhooks
PermissionManageWebhooks
// Allows management and editing of emojis
PermissionManageEmojis
// Allows members to use slash commands in text channels
PermissionManageEmojisAndStickers
// Allows members to use slash commands in text channels
PermissionUseSlashCommands
// Allows for requesting to speak in stage channels. (This permission is
// under active development and may be changed or removed.)
PermissionRequestToSpeak
_
// Allows for deleting and archiving threads, and viewing all private
// threads
PermissionManageThreads
// Allows for creating and participating in threads.
PermissionUsePublicThreads
// Allows for creating and participating in private threads.
PermissionUsePrivateThreads
// Allows the usage of custom stickers from other servers
PermissionUseExternalStickers
PermissionAllText = 0 |
PermissionViewChannel |
@ -78,7 +93,11 @@ const (
PermissionAttachFiles |
PermissionReadMessageHistory |
PermissionMentionEveryone |
PermissionUseExternalEmojis
PermissionUseExternalEmojis |
PermissionUseSlashCommands |
PermissionUsePublicThreads |
PermissionUsePrivateThreads |
PermissionUseExternalStickers
PermissionAllVoice = 0 |
PermissionViewChannel |
@ -89,7 +108,8 @@ const (
PermissionDeafenMembers |
PermissionMoveMembers |
PermissionUseVAD |
PermissionPrioritySpeaker
PermissionPrioritySpeaker |
PermissionRequestToSpeak
PermissionAllChannel = 0 |
PermissionAllText |
@ -107,9 +127,10 @@ const (
PermissionManageGuild |
PermissionAdministrator |
PermissionManageWebhooks |
PermissionManageEmojis |
PermissionManageEmojisAndStickers |
PermissionManageNicknames |
PermissionChangeNickname
PermissionChangeNickname |
PermissionManageThreads
)
func (p Permissions) Has(perm Permissions) bool {
@ -147,7 +168,7 @@ func CalcOverwrites(guild Guild, channel Channel, member Member) Permissions {
return PermissionAll
}
for _, overwrite := range channel.Permissions {
for _, overwrite := range channel.Overwrites {
if GuildID(overwrite.ID) == guild.ID {
perm &= ^overwrite.Deny
perm |= overwrite.Allow
@ -157,7 +178,7 @@ func CalcOverwrites(guild Guild, channel Channel, member Member) Permissions {
var deny, allow Permissions
for _, overwrite := range channel.Permissions {
for _, overwrite := range channel.Overwrites {
for _, id := range member.RoleIDs {
if id == RoleID(overwrite.ID) && overwrite.Type == OverwriteRole {
deny |= overwrite.Deny
@ -170,7 +191,7 @@ func CalcOverwrites(guild Guild, channel Channel, member Member) Permissions {
perm &= ^deny
perm |= allow
for _, overwrite := range channel.Permissions {
for _, overwrite := range channel.Overwrites {
if UserID(overwrite.ID) == member.User.ID {
perm &= ^overwrite.Deny
perm |= overwrite.Allow

View file

@ -107,9 +107,18 @@ func DurationToSeconds(dura time.Duration) Seconds {
func (s Seconds) MarshalJSON() ([]byte, error) {
if s < 1 {
return []byte("null"), nil
} else {
return []byte(strconv.Itoa(int(s))), nil
}
return []byte(strconv.Itoa(int(s))), nil
}
func (s *Seconds) UnmarshalJSON(data []byte) error {
if string(data) == "null" {
*s = NullSecond
return nil
}
return json.Unmarshal(data, (*int)(s))
}
func (s Seconds) String() string {
@ -138,3 +147,34 @@ func (ms Milliseconds) Duration() time.Duration {
const f64ms = Milliseconds(time.Millisecond)
return time.Duration(ms * f64ms)
}
//
// ArchiveDuration is the duration after which a thread without activity will
// be archived.
//
// The duration's unit is minutes.
type ArchiveDuration int
const (
OneHourArchive ArchiveDuration = 60
OneDayArchive ArchiveDuration = 24 * OneHourArchive
// ThreeDaysArchive archives a thread after three days.
//
// This duration is only available to nitro boosted guilds. The Features
// field of a Guild will indicate whether this is the case.
ThreeDaysArchive ArchiveDuration = 3 * OneDayArchive
// SevenDaysArchive archives a thread after seven days.
//
// This duration is only available to nitro boosted guilds. The Features
// field of a Guild will indicate whether this is the case.
SevenDaysArchive ArchiveDuration = 7 * OneDayArchive
)
func (m ArchiveDuration) String() string {
return m.Duration().String()
}
func (m ArchiveDuration) Duration() time.Duration {
return time.Duration(m) * time.Minute
}

View file

@ -3,8 +3,9 @@ package gateway
import (
"context"
"github.com/diamondburned/arikawa/v3/discord"
"github.com/pkg/errors"
"github.com/diamondburned/arikawa/v3/discord"
)
// Rules: VOICE_STATE_UPDATE -> VoiceStateUpdateEvent
@ -128,8 +129,8 @@ type UpdateStatusData struct {
// Activities can be null or an empty slice.
Activities []discord.Activity `json:"activities"`
Status Status `json:"status"`
AFK bool `json:"afk"`
Status discord.Status `json:"status"`
AFK bool `json:"afk"`
}
func (g *Gateway) UpdateStatus(data UpdateStatusData) error {

View file

@ -48,6 +48,69 @@ type (
LastMessageID discord.MessageID `json:"last_message_id"`
}
}
// ThreadCreateEvent is sent when a thread is created, relevant to the
// current user, or when the current user is added to a thread.
ThreadCreateEvent struct {
discord.Channel
}
// ThreadUpdateEvent is sent when a thread is updated.
ThreadUpdateEvent struct {
discord.Channel
}
// ThreadDeleteEvent is sent when a thread relevant to the current user is
// deleted.
ThreadDeleteEvent struct {
// ID is the id of this channel.
ID discord.ChannelID `json:"id"`
// GuildID is the id of the guild.
GuildID discord.GuildID `json:"guild_id,omitempty"`
// Type is the type of channel.
Type discord.ChannelType `json:"type,omitempty"`
// ParentID is the id of the text channel this thread was created.
ParentID discord.ChannelID `json:"parent_id,omitempty"`
}
// ThreadListSyncEvent is sent when the current user gains access to a
// channel.
ThreadListSyncEvent struct {
// GuildID is the id of the guild.
GuildID discord.GuildID `json:"guild_id"`
// ChannelIDs are the parent channel ids whose threads are being
// synced. If nil, then threads were synced for the entire guild.
// This slice may contain ChannelIDs that have no active threads as
// well, so you know to clear that data.
ChannelIDs []discord.ChannelID `json:"channel_ids,omitempty"`
Threads []discord.Channel `json:"threads"`
Members []discord.ThreadMember `json:"members"`
}
// ThreadMemberUpdateEvent is sent when the thread member object for the
// current user is updated.
ThreadMemberUpdateEvent struct {
discord.ThreadMember
}
// ThreadMembersUpdateEvent is sent when anyone is added to or removed from
// a thread. If the current user does not have the GUILD_MEMBERS Gateway
// Intent, then this event will only be sent if the current user was added
// to or removed from the thread.
ThreadMembersUpdateEvent struct {
// ID is the id of the thread.
ID discord.ChannelID
// GuildID is the id of the guild.
GuildID discord.GuildID
// MemberCount is the approximate number of members in the thread,
// capped at 50.
MemberCount int
// AddedMembers are the users who were added to the thread.
AddedMembers []discord.ThreadMember
// RemovedUserIDs are the ids of the users who were removed from the
// thread.
RemovedMemberIDs []discord.UserID
}
)
// https://discord.com/developers/docs/topics/gateway#guilds
@ -63,7 +126,8 @@ type (
VoiceStates []discord.VoiceState `json:"voice_states,omitempty"`
Members []discord.Member `json:"members,omitempty"`
Channels []discord.Channel `json:"channels,omitempty"`
Presences []Presence `json:"presences,omitempty"`
Threads []discord.Channel `json:"threads,omitempty"`
Presences []discord.Presence `json:"presences,omitempty"`
}
GuildUpdateEvent struct {
discord.Guild
@ -119,8 +183,8 @@ type (
NotFound []string `json:"not_found,omitempty"`
// Only filled if requested
Presences []Presence `json:"presences,omitempty"`
Nonce string `json:"nonce,omitempty"`
Presences []discord.Presence `json:"presences,omitempty"`
Nonce string `json:"nonce,omitempty"`
}
// GuildMemberListUpdate is an undocumented event. It's received when the
@ -163,8 +227,8 @@ type (
Group *GuildMemberListGroup `json:"group,omitempty"`
Member *struct {
discord.Member
HoistedRole string `json:"hoisted_role"`
Presence Presence `json:"presence"`
HoistedRole string `json:"hoisted_role"`
Presence discord.Presence `json:"presence"`
} `json:"member,omitempty"`
}
@ -266,58 +330,18 @@ type (
}
)
// Status is the enumerate type for a user's status.
type Status string
const (
UnknownStatus Status = ""
OnlineStatus Status = "online"
DoNotDisturbStatus Status = "dnd"
IdleStatus Status = "idle"
InvisibleStatus Status = "invisible"
OfflineStatus Status = "offline"
)
// https://discord.com/developers/docs/topics/gateway#presence
type (
// Presence represents a partial Presence structure used by other structs to be
// easily embedded. It does not contain any ID to identify who it belongs
// to. For more information, refer to the PresenceUpdateEvent struct.
Presence struct {
// User is the user presence is being updated for. Only the ID field is
// guaranteed to be valid per Discord documentation.
User discord.User `json:"user"`
// GuildID is the id of the guild
GuildID discord.GuildID `json:"guild_id"`
// Status is either "idle", "dnd", "online", or "offline".
Status Status `json:"status"`
// Activities are the user's current activities.
Activities []discord.Activity `json:"activities"`
// ClientStatus is the user's platform-dependent status.
ClientStatus ClientStatus `json:"client_status"`
}
// ClientStatus is the user's platform-dependent status.
//
// https://discord.com/developers/docs/topics/gateway#client-status-object
ClientStatus struct {
// Desktop is the user's status set for an active desktop (Windows,
// Linux, Mac) application session.
Desktop Status `json:"desktop,omitempty"`
// Mobile is the user's status set for an active mobile (iOS, Android)
// application session.
Mobile Status `json:"mobile,omitempty"`
// Web is the user's status set for an active web (browser, bot
// account) application session.
Web Status `json:"web,omitempty"`
}
// PresenceUpdateEvent represents the structure of the Presence Update Event
// object.
//
// https://discord.com/developers/docs/topics/gateway#presence-update-presence-update-event-fields
PresenceUpdateEvent struct {
Presence
discord.Presence
}
PresencesReplaceEvent []PresenceUpdateEvent
@ -325,8 +349,8 @@ type (
// SessionsReplaceEvent is an undocumented user event. It's likely used for
// current user's presence updates.
SessionsReplaceEvent []struct {
Status Status `json:"status"`
SessionID string `json:"session_id"`
Status discord.Status `json:"status"`
SessionID string `json:"session_id"`
Activities []discord.Activity `json:"activities"`

View file

@ -26,7 +26,7 @@ type (
ReadStates []ReadState `json:"read_state,omitempty"`
UserGuildSettings []UserGuildSetting `json:"user_guild_settings,omitempty"`
Relationships []discord.Relationship `json:"relationships,omitempty"`
Presences []Presence `json:"presences,omitempty"`
Presences []discord.Presence `json:"presences,omitempty"`
FriendSuggestionCount int `json:"friend_suggestion_count,omitempty"`
GeoOrderedRTCRegions []string `json:"geo_ordered_rtc_regions,omitempty"`
@ -75,7 +75,7 @@ type (
FriendSourceFlags FriendSourceFlags `json:"friend_source_flags"`
Status Status `json:"status"`
Status discord.Status `json:"status"`
CustomStatus *CustomUserStatus `json:"custom_status"`
}
@ -222,11 +222,11 @@ type (
UserID discord.UserID `json:"user_id"`
// Status is either "idle", "dnd", "online", or "offline".
Status Status `json:"status"`
Status discord.Status `json:"status"`
// Activities are the user's current activities.
Activities []discord.Activity `json:"activities"`
// ClientStaus is the user's platform-dependent status.
ClientStatus ClientStatus `json:"client_status"`
ClientStatus discord.ClientStatus `json:"client_status"`
// LastModified is only present in Friends.
LastModified discord.UnixMsTimestamp `json:"last_modified,omitempty"`
@ -249,8 +249,8 @@ func ConvertSupplementalMember(sm SupplementalMember) discord.Member {
// ConvertSupplementalPresence converts a SupplementalPresence to a regular
// Presence with an empty GuildID.
func ConvertSupplementalPresence(sp SupplementalPresence) Presence {
return Presence{
func ConvertSupplementalPresence(sp SupplementalPresence) discord.Presence {
return discord.Presence{
User: discord.User{ID: sp.UserID},
Status: sp.Status,
Activities: sp.Activities,

View file

@ -719,7 +719,7 @@ func (s *State) Messages(channelID discord.ChannelID, limit uint) ([]discord.Mes
// Presence checks the state for user presences. If no guildID is given, it
// will look for the presence in all cached guilds.
func (s *State) Presence(gID discord.GuildID, uID discord.UserID) (*gateway.Presence, error) {
func (s *State) Presence(gID discord.GuildID, uID discord.UserID) (*discord.Presence, error) {
if !s.Gateway.HasIntents(gateway.IntentGuildPresences) {
return nil, store.ErrNotFound
}

View file

@ -4,7 +4,6 @@ import (
"sync"
"github.com/diamondburned/arikawa/v3/discord"
"github.com/diamondburned/arikawa/v3/gateway"
"github.com/diamondburned/arikawa/v3/internal/moreatomic"
"github.com/diamondburned/arikawa/v3/state/store"
)
@ -15,7 +14,7 @@ type Presence struct {
type presences struct {
mut sync.Mutex
presences map[discord.UserID]gateway.Presence
presences map[discord.UserID]discord.Presence
}
var _ store.PresenceStore = (*Presence)(nil)
@ -24,7 +23,7 @@ func NewPresence() *Presence {
return &Presence{
guilds: *moreatomic.NewMap(func() interface{} {
return &presences{
presences: make(map[discord.UserID]gateway.Presence, 1),
presences: make(map[discord.UserID]discord.Presence, 1),
}
}),
}
@ -34,7 +33,7 @@ func (s *Presence) Reset() error {
return s.guilds.Reset()
}
func (s *Presence) Presence(gID discord.GuildID, uID discord.UserID) (*gateway.Presence, error) {
func (s *Presence) Presence(gID discord.GuildID, uID discord.UserID) (*discord.Presence, error) {
iv, ok := s.guilds.Load(gID)
if !ok {
return nil, store.ErrNotFound
@ -53,7 +52,7 @@ func (s *Presence) Presence(gID discord.GuildID, uID discord.UserID) (*gateway.P
return nil, store.ErrNotFound
}
func (s *Presence) Presences(guildID discord.GuildID) ([]gateway.Presence, error) {
func (s *Presence) Presences(guildID discord.GuildID) ([]discord.Presence, error) {
iv, ok := s.guilds.Load(guildID)
if !ok {
return nil, store.ErrNotFound
@ -64,7 +63,7 @@ func (s *Presence) Presences(guildID discord.GuildID) ([]gateway.Presence, error
ps.mut.Lock()
defer ps.mut.Unlock()
var presences = make([]gateway.Presence, 0, len(ps.presences))
var presences = make([]discord.Presence, 0, len(ps.presences))
for _, p := range ps.presences {
presences = append(presences, p)
}
@ -72,7 +71,7 @@ func (s *Presence) Presences(guildID discord.GuildID) ([]gateway.Presence, error
return presences, nil
}
func (s *Presence) PresenceSet(guildID discord.GuildID, p gateway.Presence, update bool) error {
func (s *Presence) PresenceSet(guildID discord.GuildID, p discord.Presence, update bool) error {
iv, _ := s.guilds.LoadOrStore(guildID)
ps := iv.(*presences)
@ -82,7 +81,7 @@ func (s *Presence) PresenceSet(guildID discord.GuildID, p gateway.Presence, upda
// Shitty if check is better than a realloc every time.
if ps.presences == nil {
ps.presences = make(map[discord.UserID]gateway.Presence, 1)
ps.presences = make(map[discord.UserID]discord.Presence, 1)
}
if _, ok := ps.presences[p.User.ID]; !ok || update {

View file

@ -38,7 +38,6 @@ import (
"fmt"
"github.com/diamondburned/arikawa/v3/discord"
"github.com/diamondburned/arikawa/v3/gateway"
)
// ErrNotFound is an error that a store can use to return when something isn't
@ -317,22 +316,22 @@ func (noop) MessageRemove(discord.ChannelID, discord.MessageID) error {
type PresenceStore interface {
Resetter
Presence(discord.GuildID, discord.UserID) (*gateway.Presence, error)
Presences(discord.GuildID) ([]gateway.Presence, error)
Presence(discord.GuildID, discord.UserID) (*discord.Presence, error)
Presences(discord.GuildID) ([]discord.Presence, error)
PresenceSet(guildID discord.GuildID, p gateway.Presence, update bool) error
PresenceSet(guildID discord.GuildID, p discord.Presence, update bool) error
PresenceRemove(discord.GuildID, discord.UserID) error
}
var _ PresenceStore = (*noop)(nil)
func (noop) Presence(discord.GuildID, discord.UserID) (*gateway.Presence, error) {
func (noop) Presence(discord.GuildID, discord.UserID) (*discord.Presence, error) {
return nil, ErrNotFound
}
func (noop) Presences(discord.GuildID) ([]gateway.Presence, error) {
func (noop) Presences(discord.GuildID) ([]discord.Presence, error) {
return nil, ErrNotFound
}
func (noop) PresenceSet(discord.GuildID, gateway.Presence, bool) error {
func (noop) PresenceSet(discord.GuildID, discord.Presence, bool) error {
return nil
}
func (noop) PresenceRemove(discord.GuildID, discord.UserID) error {