Threads reloaded (#1058)
* feat(endpoints): bumped discord version to 9 * feat: threads barebones * feat(threads): documentation * feat(threads): membership caching * feat(threads): added type to StartThread method * fix: replaced missing Timestamp definitions with time.Time * chore: removed debug logs * chore: removed thread alias for channel type * feat(webhooks): separated thread option into method * fix(state): ThreadMembersUpdate member duplication bug * fix: golint * feat(threads): pr fixes and BeforeUpdate in ThreadUpdate * feat: removed unnecessary todo * feat(state): removed thread last message update in MessageAdd * Revert "feat(state): removed thread last message update in MessageAdd" This reverts commit 4ca359fd2cc304e5d0ec2937e25c0c487a1f2096. * feat(state): update only last message id for thread update Implements updating message id in MESSAGE_CREATE and MESSAGE_DELETE events. Refer to https://discord.com/developers/docs/topics/gateway#thread-update for more info. * fix(restapi): passing threadID in WebhookThreadExecute * feat(state): dropped last_message_id updates for threads * fix: gofmt * feat(events#ThreadCreate): added newly_created field * feat(restapi)!: corrected names of thread functions
This commit is contained in:
parent
6015eed933
commit
992358e106
8 changed files with 899 additions and 121 deletions
89
endpoints.go
89
endpoints.go
|
@ -14,7 +14,7 @@ package discordgo
|
|||
import "strconv"
|
||||
|
||||
// APIVersion is the Discord API version used for the REST and Websocket API.
|
||||
var APIVersion = "8"
|
||||
var APIVersion = "9"
|
||||
|
||||
// Known Discord API Endpoints.
|
||||
var (
|
||||
|
@ -53,50 +53,61 @@ var (
|
|||
uDiscriminatorInt, _ := strconv.Atoi(uDiscriminator)
|
||||
return EndpointCDN + "embed/avatars/" + strconv.Itoa(uDiscriminatorInt%5) + ".png"
|
||||
}
|
||||
|
||||
EndpointUserGuilds = func(uID string) string { return EndpointUsers + uID + "/guilds" }
|
||||
EndpointUserGuild = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID }
|
||||
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 }
|
||||
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 }
|
||||
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 }
|
||||
|
||||
EndpointChannel = func(cID string) string { return EndpointChannels + cID }
|
||||
EndpointChannelPermissions = func(cID string) string { return EndpointChannels + cID + "/permissions" }
|
||||
EndpointChannelPermission = func(cID, tID string) string { return EndpointChannelPermissions(cID) + "/" + tID }
|
||||
EndpointChannelInvites = func(cID string) string { return EndpointChannels + cID + "/invites" }
|
||||
EndpointChannelTyping = func(cID string) string { return EndpointChannels + cID + "/typing" }
|
||||
EndpointChannelMessages = func(cID string) string { return EndpointChannels + cID + "/messages" }
|
||||
EndpointChannelMessage = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID }
|
||||
EndpointChannelMessagesBulkDelete = func(cID string) string { return EndpointChannel(cID) + "/messages/bulk-delete" }
|
||||
EndpointChannelMessagesPins = func(cID string) string { return EndpointChannel(cID) + "/pins" }
|
||||
EndpointChannelMessagePin = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID }
|
||||
EndpointChannelMessageCrosspost = func(cID, mID string) string { return EndpointChannel(cID) + "/messages/" + mID + "/crosspost" }
|
||||
EndpointChannelFollow = func(cID string) string { return EndpointChannel(cID) + "/followers" }
|
||||
EndpointChannel = func(cID string) string { return EndpointChannels + cID }
|
||||
EndpointChannelThreads = func(cID string) string { return EndpointChannel(cID) + "/threads" }
|
||||
EndpointChannelActiveThreads = func(cID string) string { return EndpointChannelThreads(cID) + "/active" }
|
||||
EndpointChannelPublicArchivedThreads = func(cID string) string { return EndpointChannelThreads(cID) + "/archived/public" }
|
||||
EndpointChannelPrivateArchivedThreads = func(cID string) string { return EndpointChannelThreads(cID) + "/archived/private" }
|
||||
EndpointChannelJoinedPrivateArchivedThreads = func(cID string) string { return EndpointChannel(cID) + "/users/@me/threads/archived/private" }
|
||||
EndpointChannelPermissions = func(cID string) string { return EndpointChannels + cID + "/permissions" }
|
||||
EndpointChannelPermission = func(cID, tID string) string { return EndpointChannels + cID + "/permissions/" + tID }
|
||||
EndpointChannelInvites = func(cID string) string { return EndpointChannels + cID + "/invites" }
|
||||
EndpointChannelTyping = func(cID string) string { return EndpointChannels + cID + "/typing" }
|
||||
EndpointChannelMessages = func(cID string) string { return EndpointChannels + cID + "/messages" }
|
||||
EndpointChannelMessage = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID }
|
||||
EndpointChannelMessageThread = func(cID, mID string) string { return EndpointChannelMessage(cID, mID) + "/threads" }
|
||||
EndpointChannelMessagesBulkDelete = func(cID string) string { return EndpointChannel(cID) + "/messages/bulk-delete" }
|
||||
EndpointChannelMessagesPins = func(cID string) string { return EndpointChannel(cID) + "/pins" }
|
||||
EndpointChannelMessagePin = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID }
|
||||
EndpointChannelMessageCrosspost = func(cID, mID string) string { return EndpointChannel(cID) + "/messages/" + mID + "/crosspost" }
|
||||
EndpointChannelFollow = func(cID string) string { return EndpointChannel(cID) + "/followers" }
|
||||
EndpointThreadMembers = func(tID string) string { return EndpointChannel(tID) + "/thread-members" }
|
||||
EndpointThreadMember = func(tID, mID string) string { return EndpointThreadMembers(tID) + "/" + mID }
|
||||
|
||||
EndpointGroupIcon = func(cID, hash string) string { return EndpointCDNChannelIcons + cID + "/" + hash + ".png" }
|
||||
|
||||
|
|
144
eventhandlers.go
144
eventhandlers.go
|
@ -44,6 +44,12 @@ const (
|
|||
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"
|
||||
|
@ -774,6 +780,126 @@ func (eh resumedEventHandler) Handle(s *Session, i interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
// threadCreateEventHandler is an event handler for ThreadCreate events.
|
||||
type threadCreateEventHandler func(*Session, *ThreadCreate)
|
||||
|
||||
// Type returns the event type for ThreadCreate events.
|
||||
func (eh threadCreateEventHandler) Type() string {
|
||||
return threadCreateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of ThreadCreate.
|
||||
func (eh threadCreateEventHandler) New() interface{} {
|
||||
return &ThreadCreate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for ThreadCreate events.
|
||||
func (eh threadCreateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*ThreadCreate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// threadDeleteEventHandler is an event handler for ThreadDelete events.
|
||||
type threadDeleteEventHandler func(*Session, *ThreadDelete)
|
||||
|
||||
// Type returns the event type for ThreadDelete events.
|
||||
func (eh threadDeleteEventHandler) Type() string {
|
||||
return threadDeleteEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of ThreadDelete.
|
||||
func (eh threadDeleteEventHandler) New() interface{} {
|
||||
return &ThreadDelete{}
|
||||
}
|
||||
|
||||
// Handle is the handler for ThreadDelete events.
|
||||
func (eh threadDeleteEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*ThreadDelete); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// threadListSyncEventHandler is an event handler for ThreadListSync events.
|
||||
type threadListSyncEventHandler func(*Session, *ThreadListSync)
|
||||
|
||||
// Type returns the event type for ThreadListSync events.
|
||||
func (eh threadListSyncEventHandler) Type() string {
|
||||
return threadListSyncEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of ThreadListSync.
|
||||
func (eh threadListSyncEventHandler) New() interface{} {
|
||||
return &ThreadListSync{}
|
||||
}
|
||||
|
||||
// Handle is the handler for ThreadListSync events.
|
||||
func (eh threadListSyncEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*ThreadListSync); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// threadMemberUpdateEventHandler is an event handler for ThreadMemberUpdate events.
|
||||
type threadMemberUpdateEventHandler func(*Session, *ThreadMemberUpdate)
|
||||
|
||||
// Type returns the event type for ThreadMemberUpdate events.
|
||||
func (eh threadMemberUpdateEventHandler) Type() string {
|
||||
return threadMemberUpdateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of ThreadMemberUpdate.
|
||||
func (eh threadMemberUpdateEventHandler) New() interface{} {
|
||||
return &ThreadMemberUpdate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for ThreadMemberUpdate events.
|
||||
func (eh threadMemberUpdateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*ThreadMemberUpdate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// threadMembersUpdateEventHandler is an event handler for ThreadMembersUpdate events.
|
||||
type threadMembersUpdateEventHandler func(*Session, *ThreadMembersUpdate)
|
||||
|
||||
// Type returns the event type for ThreadMembersUpdate events.
|
||||
func (eh threadMembersUpdateEventHandler) Type() string {
|
||||
return threadMembersUpdateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of ThreadMembersUpdate.
|
||||
func (eh threadMembersUpdateEventHandler) New() interface{} {
|
||||
return &ThreadMembersUpdate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for ThreadMembersUpdate events.
|
||||
func (eh threadMembersUpdateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*ThreadMembersUpdate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// threadUpdateEventHandler is an event handler for ThreadUpdate events.
|
||||
type threadUpdateEventHandler func(*Session, *ThreadUpdate)
|
||||
|
||||
// Type returns the event type for ThreadUpdate events.
|
||||
func (eh threadUpdateEventHandler) Type() string {
|
||||
return threadUpdateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of ThreadUpdate.
|
||||
func (eh threadUpdateEventHandler) New() interface{} {
|
||||
return &ThreadUpdate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for ThreadUpdate events.
|
||||
func (eh threadUpdateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*ThreadUpdate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// typingStartEventHandler is an event handler for TypingStart events.
|
||||
type typingStartEventHandler func(*Session, *TypingStart)
|
||||
|
||||
|
@ -1012,6 +1138,18 @@ func handlerForInterface(handler interface{}) EventHandler {
|
|||
return relationshipRemoveEventHandler(v)
|
||||
case func(*Session, *Resumed):
|
||||
return resumedEventHandler(v)
|
||||
case func(*Session, *ThreadCreate):
|
||||
return threadCreateEventHandler(v)
|
||||
case func(*Session, *ThreadDelete):
|
||||
return threadDeleteEventHandler(v)
|
||||
case func(*Session, *ThreadListSync):
|
||||
return threadListSyncEventHandler(v)
|
||||
case func(*Session, *ThreadMemberUpdate):
|
||||
return threadMemberUpdateEventHandler(v)
|
||||
case func(*Session, *ThreadMembersUpdate):
|
||||
return threadMembersUpdateEventHandler(v)
|
||||
case func(*Session, *ThreadUpdate):
|
||||
return threadUpdateEventHandler(v)
|
||||
case func(*Session, *TypingStart):
|
||||
return typingStartEventHandler(v)
|
||||
case func(*Session, *UserGuildSettingsUpdate):
|
||||
|
@ -1067,6 +1205,12 @@ func init() {
|
|||
registerInterfaceProvider(relationshipAddEventHandler(nil))
|
||||
registerInterfaceProvider(relationshipRemoveEventHandler(nil))
|
||||
registerInterfaceProvider(resumedEventHandler(nil))
|
||||
registerInterfaceProvider(threadCreateEventHandler(nil))
|
||||
registerInterfaceProvider(threadDeleteEventHandler(nil))
|
||||
registerInterfaceProvider(threadListSyncEventHandler(nil))
|
||||
registerInterfaceProvider(threadMemberUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(threadMembersUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(threadUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(typingStartEventHandler(nil))
|
||||
registerInterfaceProvider(userGuildSettingsUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(userNoteUpdateEventHandler(nil))
|
||||
|
|
47
events.go
47
events.go
|
@ -73,6 +73,53 @@ type ChannelPinsUpdate struct {
|
|||
GuildID string `json:"guild_id,omitempty"`
|
||||
}
|
||||
|
||||
// ThreadCreate is the data for a ThreadCreate event.
|
||||
type ThreadCreate struct {
|
||||
*Channel
|
||||
NewlyCreated bool `json:"newly_created"`
|
||||
}
|
||||
|
||||
// ThreadUpdate is the data for a ThreadUpdate event.
|
||||
type ThreadUpdate struct {
|
||||
*Channel
|
||||
BeforeUpdate *Channel `json:"-"`
|
||||
}
|
||||
|
||||
// ThreadDelete is the data for a ThreadDelete event.
|
||||
type ThreadDelete struct {
|
||||
*Channel
|
||||
}
|
||||
|
||||
// ThreadListSync is the data for a ThreadListSync event.
|
||||
type ThreadListSync struct {
|
||||
// The id of the guild
|
||||
GuildID string `json:"guild_id"`
|
||||
// The parent channel ids whose threads are being synced.
|
||||
// If omitted, then threads were synced for the entire guild.
|
||||
// This array may contain channel_ids that have no active threads as well, so you know to clear that data.
|
||||
ChannelIDs []string `json:"channel_ids"`
|
||||
// All active threads in the given channels that the current user can access
|
||||
Threads []*Channel `json:"threads"`
|
||||
// All thread member objects from the synced threads for the current user,
|
||||
// indicating which threads the current user has been added to
|
||||
Members []*ThreadMember `json:"members"`
|
||||
}
|
||||
|
||||
// ThreadMemberUpdate is the data for a ThreadMemberUpdate event.
|
||||
type ThreadMemberUpdate struct {
|
||||
*ThreadMember
|
||||
GuildID string `json:"guild_id"`
|
||||
}
|
||||
|
||||
// ThreadMembersUpdate is the data for a ThreadMembersUpdate event.
|
||||
type ThreadMembersUpdate struct {
|
||||
ID string `json:"id"`
|
||||
GuildID string `json:"guild_id"`
|
||||
MemberCount int `json:"member_count"`
|
||||
AddedMembers []AddedThreadMember `json:"added_members"`
|
||||
RemovedMembers []string `json:"removed_member_ids"`
|
||||
}
|
||||
|
||||
// GuildCreate is the data for a GuildCreate event.
|
||||
type GuildCreate struct {
|
||||
*Guild
|
||||
|
|
73
examples/threads/main.go
Normal file
73
examples/threads/main.go
Normal file
|
@ -0,0 +1,73 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
// Flags
|
||||
var (
|
||||
BotToken = flag.String("token", "", "Bot token")
|
||||
)
|
||||
|
||||
const timeout time.Duration = time.Second * 10
|
||||
|
||||
var games map[string]time.Time = make(map[string]time.Time)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
s, _ := discordgo.New("Bot " + *BotToken)
|
||||
s.AddHandler(func(s *discordgo.Session, r *discordgo.Ready) {
|
||||
fmt.Println("Bot is ready")
|
||||
})
|
||||
s.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||
if strings.Contains(m.Content, "ping") {
|
||||
if ch, err := s.State.Channel(m.ChannelID); err != nil || !ch.IsThread() {
|
||||
thread, err := s.MessageThreadStartComplex(m.ChannelID, m.ID, &discordgo.ThreadStart{
|
||||
Name: "Pong game with " + m.Author.Username,
|
||||
AutoArchiveDuration: 60,
|
||||
Invitable: false,
|
||||
RateLimitPerUser: 10,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, _ = s.ChannelMessageSend(thread.ID, "pong")
|
||||
m.ChannelID = thread.ID
|
||||
} else {
|
||||
_, _ = s.ChannelMessageSendReply(m.ChannelID, "pong", m.Reference())
|
||||
}
|
||||
games[m.ChannelID] = time.Now()
|
||||
<-time.After(timeout)
|
||||
if time.Since(games[m.ChannelID]) >= timeout {
|
||||
_, err := s.ChannelEditComplex(m.ChannelID, &discordgo.ChannelEdit{
|
||||
Archived: true,
|
||||
Locked: true,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
s.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsAllWithoutPrivileged)
|
||||
|
||||
err := s.Open()
|
||||
if err != nil {
|
||||
log.Fatalf("Cannot open the session: %v", err)
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
stop := make(chan os.Signal, 1)
|
||||
signal.Notify(stop, os.Interrupt)
|
||||
<-stop
|
||||
log.Println("Graceful shutdown")
|
||||
|
||||
}
|
12
message.go
12
message.go
|
@ -38,8 +38,10 @@ const (
|
|||
MessageTypeChannelFollowAdd MessageType = 12
|
||||
MessageTypeGuildDiscoveryDisqualified MessageType = 14
|
||||
MessageTypeGuildDiscoveryRequalified MessageType = 15
|
||||
MessageTypeThreadCreated MessageType = 18
|
||||
MessageTypeReply MessageType = 19
|
||||
MessageTypeChatInputCommand MessageType = 20
|
||||
MessageTypeThreadStarterMessage MessageType = 21
|
||||
MessageTypeContextMenuCommand MessageType = 23
|
||||
)
|
||||
|
||||
|
@ -126,11 +128,21 @@ type Message struct {
|
|||
// To generate a reference to this message, use (*Message).Reference().
|
||||
MessageReference *MessageReference `json:"message_reference"`
|
||||
|
||||
// The message associated with the message_reference
|
||||
// NOTE: This field is only returned for messages with a type of 19 (REPLY) or 21 (THREAD_STARTER_MESSAGE).
|
||||
// If the message is a reply but the referenced_message field is not present,
|
||||
// the backend did not attempt to fetch the message that was being replied to, so its state is unknown.
|
||||
// If the field exists but is null, the referenced message was deleted.
|
||||
ReferencedMessage *Message `json:"referenced_message"`
|
||||
|
||||
// The flags of the message, which describe extra features of a message.
|
||||
// This is a combination of bit masks; the presence of a certain permission can
|
||||
// be checked by performing a bitwise AND between this int and the flag.
|
||||
Flags MessageFlags `json:"flags"`
|
||||
|
||||
// The thread that was started from this message, includes thread member object
|
||||
Thread *Channel `json:"thread,omitempty"`
|
||||
|
||||
// An array of Sticker objects, if any were sent.
|
||||
StickerItems []*Sticker `json:"sticker_items"`
|
||||
}
|
||||
|
|
253
restapi.go
253
restapi.go
|
@ -1988,15 +1988,19 @@ func (s *Session) WebhookDeleteWithToken(webhookID, token string) (st *Webhook,
|
|||
return
|
||||
}
|
||||
|
||||
// WebhookExecute executes a webhook.
|
||||
// webhookID: The ID of a webhook.
|
||||
// token : The auth token for the webhook
|
||||
// wait : Waits for server confirmation of message send and ensures that the return struct is populated (it is nil otherwise)
|
||||
func (s *Session) WebhookExecute(webhookID, token string, wait bool, data *WebhookParams) (st *Message, err error) {
|
||||
func (s *Session) webhookExecute(webhookID, token string, wait bool, threadID string, data *WebhookParams) (st *Message, err error) {
|
||||
uri := EndpointWebhookToken(webhookID, token)
|
||||
|
||||
v := url.Values{}
|
||||
if wait {
|
||||
uri += "?wait=true"
|
||||
v.Set("wait", "true")
|
||||
}
|
||||
|
||||
if threadID != "" {
|
||||
v.Set("thread_id", threadID)
|
||||
}
|
||||
if len(v) != 0 {
|
||||
uri += "?" + v.Encode()
|
||||
}
|
||||
|
||||
var response []byte
|
||||
|
@ -2018,6 +2022,23 @@ func (s *Session) WebhookExecute(webhookID, token string, wait bool, data *Webho
|
|||
return
|
||||
}
|
||||
|
||||
// WebhookExecute executes a webhook.
|
||||
// webhookID: The ID of a webhook.
|
||||
// token : The auth token for the webhook
|
||||
// wait : Waits for server confirmation of message send and ensures that the return struct is populated (it is nil otherwise)
|
||||
func (s *Session) WebhookExecute(webhookID, token string, wait bool, data *WebhookParams) (st *Message, err error) {
|
||||
return s.webhookExecute(webhookID, token, wait, "", data)
|
||||
}
|
||||
|
||||
// WebhookThreadExecute executes a webhook in a thread.
|
||||
// webhookID: The ID of a webhook.
|
||||
// token : The auth token for the webhook
|
||||
// wait : Waits for server confirmation of message send and ensures that the return struct is populated (it is nil otherwise)
|
||||
// threadID : Sends a message to the specified thread within a webhook's channel. The thread will automatically be unarchived.
|
||||
func (s *Session) WebhookThreadExecute(webhookID, token string, wait bool, threadID string, data *WebhookParams) (st *Message, err error) {
|
||||
return s.webhookExecute(webhookID, token, wait, threadID, data)
|
||||
}
|
||||
|
||||
// WebhookMessage gets a webhook message.
|
||||
// webhookID : The ID of a webhook
|
||||
// token : The auth token for the webhook
|
||||
|
@ -2164,6 +2185,226 @@ func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit i
|
|||
return
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Functions specific to threads
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
// MessageThreadStartComplex creates a new thread from an existing message.
|
||||
// channelID : Channel to create thread in
|
||||
// messageID : Message to start thread from
|
||||
// data : Parameters of the thread
|
||||
func (s *Session) MessageThreadStartComplex(channelID, messageID string, data *ThreadStart) (ch *Channel, err error) {
|
||||
endpoint := EndpointChannelMessageThread(channelID, messageID)
|
||||
var body []byte
|
||||
body, err = s.RequestWithBucketID("POST", endpoint, data, endpoint)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = unmarshal(body, &ch)
|
||||
return
|
||||
}
|
||||
|
||||
// MessageThreadStart creates a new thread from an existing message.
|
||||
// channelID : Channel to create thread in
|
||||
// messageID : Message to start thread from
|
||||
// name : Name of the thread
|
||||
// archiveDuration : Auto archive duration (in minutes)
|
||||
func (s *Session) MessageThreadStart(channelID, messageID string, name string, archiveDuration int) (ch *Channel, err error) {
|
||||
return s.MessageThreadStartComplex(channelID, messageID, &ThreadStart{
|
||||
Name: name,
|
||||
AutoArchiveDuration: archiveDuration,
|
||||
})
|
||||
}
|
||||
|
||||
// ThreadStartComplex creates a new thread.
|
||||
// channelID : Channel to create thread in
|
||||
// data : Parameters of the thread
|
||||
func (s *Session) ThreadStartComplex(channelID string, data *ThreadStart) (ch *Channel, err error) {
|
||||
endpoint := EndpointChannelThreads(channelID)
|
||||
var body []byte
|
||||
body, err = s.RequestWithBucketID("POST", endpoint, data, endpoint)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = unmarshal(body, &ch)
|
||||
return
|
||||
}
|
||||
|
||||
// ThreadStart creates a new thread.
|
||||
// channelID : Channel to create thread in
|
||||
// name : Name of the thread
|
||||
// archiveDuration : Auto archive duration (in minutes)
|
||||
func (s *Session) ThreadStart(channelID, name string, typ ChannelType, archiveDuration int) (ch *Channel, err error) {
|
||||
return s.ThreadStartComplex(channelID, &ThreadStart{
|
||||
Name: name,
|
||||
Type: typ,
|
||||
AutoArchiveDuration: archiveDuration,
|
||||
})
|
||||
}
|
||||
|
||||
// ThreadJoin adds current user to a thread
|
||||
func (s *Session) ThreadJoin(id string) error {
|
||||
endpoint := EndpointThreadMember(id, "@me")
|
||||
_, err := s.RequestWithBucketID("PUT", endpoint, nil, endpoint)
|
||||
return err
|
||||
}
|
||||
|
||||
// ThreadLeave removes current user to a thread
|
||||
func (s *Session) ThreadLeave(id string) error {
|
||||
endpoint := EndpointThreadMember(id, "@me")
|
||||
_, err := s.RequestWithBucketID("DELETE", endpoint, nil, endpoint)
|
||||
return err
|
||||
}
|
||||
|
||||
// ThreadMemberAdd adds another member to a thread
|
||||
func (s *Session) ThreadMemberAdd(threadID, memberID string) error {
|
||||
endpoint := EndpointThreadMember(threadID, memberID)
|
||||
_, err := s.RequestWithBucketID("PUT", endpoint, nil, endpoint)
|
||||
return err
|
||||
}
|
||||
|
||||
// ThreadMemberRemove removes another member from a thread
|
||||
func (s *Session) ThreadMemberRemove(threadID, memberID string) error {
|
||||
endpoint := EndpointThreadMember(threadID, memberID)
|
||||
_, err := s.RequestWithBucketID("DELETE", endpoint, nil, endpoint)
|
||||
return err
|
||||
}
|
||||
|
||||
// ThreadMember returns thread member object for the specified member of a thread
|
||||
func (s *Session) ThreadMember(threadID, memberID string) (member *ThreadMember, err error) {
|
||||
endpoint := EndpointThreadMember(threadID, memberID)
|
||||
var body []byte
|
||||
body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = unmarshal(body, &member)
|
||||
return
|
||||
}
|
||||
|
||||
// ThreadMembers returns all members of specified thread.
|
||||
func (s *Session) ThreadMembers(threadID string) (members []*ThreadMember, err error) {
|
||||
var body []byte
|
||||
body, err = s.RequestWithBucketID("GET", EndpointThreadMembers(threadID), nil, EndpointThreadMembers(threadID))
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = unmarshal(body, &members)
|
||||
return
|
||||
}
|
||||
|
||||
// ThreadsActive returns all active threads for specified channel.
|
||||
func (s *Session) ThreadsActive(channelID string) (threads *ThreadsList, err error) {
|
||||
var body []byte
|
||||
body, err = s.RequestWithBucketID("GET", EndpointChannelActiveThreads(channelID), nil, EndpointChannelActiveThreads(channelID))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = unmarshal(body, &threads)
|
||||
return
|
||||
}
|
||||
|
||||
// GuildThreadsActive returns all active threads for specified guild.
|
||||
func (s *Session) GuildThreadsActive(guildID string) (threads *ThreadsList, err error) {
|
||||
var body []byte
|
||||
body, err = s.RequestWithBucketID("GET", EndpointGuildActiveThreads(guildID), nil, EndpointGuildActiveThreads(guildID))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = unmarshal(body, &threads)
|
||||
return
|
||||
}
|
||||
|
||||
// ThreadsArchived returns archived threads for specified channel.
|
||||
// before : If specified returns only threads before the timestamp
|
||||
// limit : Optional maximum amount of threads to return.
|
||||
func (s *Session) ThreadsArchived(channelID string, before *time.Time, limit int) (threads *ThreadsList, err error) {
|
||||
endpoint := EndpointChannelPublicArchivedThreads(channelID)
|
||||
v := url.Values{}
|
||||
if before != nil {
|
||||
v.Set("before", before.Format(time.RFC3339))
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
v.Set("limit", strconv.Itoa(limit))
|
||||
}
|
||||
|
||||
if len(v) > 0 {
|
||||
endpoint += "?" + v.Encode()
|
||||
}
|
||||
|
||||
var body []byte
|
||||
body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = unmarshal(body, &threads)
|
||||
return
|
||||
}
|
||||
|
||||
// ThreadsPrivateArchived returns archived private threads for specified channel.
|
||||
// before : If specified returns only threads before the timestamp
|
||||
// limit : Optional maximum amount of threads to return.
|
||||
func (s *Session) ThreadsPrivateArchived(channelID string, before *time.Time, limit int) (threads *ThreadsList, err error) {
|
||||
endpoint := EndpointChannelPrivateArchivedThreads(channelID)
|
||||
v := url.Values{}
|
||||
if before != nil {
|
||||
v.Set("before", before.Format(time.RFC3339))
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
v.Set("limit", strconv.Itoa(limit))
|
||||
}
|
||||
|
||||
if len(v) > 0 {
|
||||
endpoint += "?" + v.Encode()
|
||||
}
|
||||
var body []byte
|
||||
body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = unmarshal(body, &threads)
|
||||
return
|
||||
}
|
||||
|
||||
// ThreadsPrivateJoinedArchived returns archived joined private threads for specified channel.
|
||||
// before : If specified returns only threads before the timestamp
|
||||
// limit : Optional maximum amount of threads to return.
|
||||
func (s *Session) ThreadsPrivateJoinedArchived(channelID string, before *time.Time, limit int) (threads *ThreadsList, err error) {
|
||||
endpoint := EndpointChannelJoinedPrivateArchivedThreads(channelID)
|
||||
v := url.Values{}
|
||||
if before != nil {
|
||||
v.Set("before", before.Format(time.RFC3339))
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
v.Set("limit", strconv.Itoa(limit))
|
||||
}
|
||||
|
||||
if len(v) > 0 {
|
||||
endpoint += "?" + v.Encode()
|
||||
}
|
||||
var body []byte
|
||||
body, err = s.RequestWithBucketID("GET", endpoint, nil, endpoint)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = unmarshal(body, &threads)
|
||||
return
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Functions specific to application (slash) commands
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
|
275
state.go
275
state.go
|
@ -38,13 +38,15 @@ type State struct {
|
|||
Ready
|
||||
|
||||
// MaxMessageCount represents how many messages per channel the state will store.
|
||||
MaxMessageCount int
|
||||
TrackChannels bool
|
||||
TrackEmojis bool
|
||||
TrackMembers bool
|
||||
TrackRoles bool
|
||||
TrackVoice bool
|
||||
TrackPresences bool
|
||||
MaxMessageCount int
|
||||
TrackChannels bool
|
||||
TrackThreads bool
|
||||
TrackEmojis bool
|
||||
TrackMembers bool
|
||||
TrackThreadMembers bool
|
||||
TrackRoles bool
|
||||
TrackVoice bool
|
||||
TrackPresences bool
|
||||
|
||||
guildMap map[string]*Guild
|
||||
channelMap map[string]*Channel
|
||||
|
@ -58,15 +60,17 @@ func NewState() *State {
|
|||
PrivateChannels: []*Channel{},
|
||||
Guilds: []*Guild{},
|
||||
},
|
||||
TrackChannels: true,
|
||||
TrackEmojis: true,
|
||||
TrackMembers: true,
|
||||
TrackRoles: true,
|
||||
TrackVoice: true,
|
||||
TrackPresences: true,
|
||||
guildMap: make(map[string]*Guild),
|
||||
channelMap: make(map[string]*Channel),
|
||||
memberMap: make(map[string]map[string]*Member),
|
||||
TrackChannels: true,
|
||||
TrackThreads: true,
|
||||
TrackEmojis: true,
|
||||
TrackMembers: true,
|
||||
TrackThreadMembers: true,
|
||||
TrackRoles: true,
|
||||
TrackVoice: true,
|
||||
TrackPresences: true,
|
||||
guildMap: make(map[string]*Guild),
|
||||
channelMap: make(map[string]*Channel),
|
||||
memberMap: make(map[string]map[string]*Member),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,6 +97,11 @@ func (s *State) GuildAdd(guild *Guild) error {
|
|||
s.channelMap[c.ID] = c
|
||||
}
|
||||
|
||||
// Add all the threads to the state in case of thread sync list.
|
||||
for _, t := range guild.Threads {
|
||||
s.channelMap[t.ID] = t
|
||||
}
|
||||
|
||||
// If this guild contains a new member slice, we must regenerate the member map so the pointers stay valid
|
||||
if guild.Members != nil {
|
||||
s.createMemberMap(guild)
|
||||
|
@ -122,6 +131,9 @@ func (s *State) GuildAdd(guild *Guild) error {
|
|||
if guild.Channels == nil {
|
||||
guild.Channels = g.Channels
|
||||
}
|
||||
if guild.Threads == nil {
|
||||
guild.Threads = g.Threads
|
||||
}
|
||||
if guild.VoiceStates == nil {
|
||||
guild.VoiceStates = g.VoiceStates
|
||||
}
|
||||
|
@ -180,21 +192,12 @@ func (s *State) Guild(guildID string) (*Guild, error) {
|
|||
return nil, ErrStateNotFound
|
||||
}
|
||||
|
||||
// PresenceAdd adds a presence to the current world state, or
|
||||
// updates it if it already exists.
|
||||
func (s *State) PresenceAdd(guildID string, presence *Presence) error {
|
||||
if s == nil {
|
||||
return ErrNilState
|
||||
func (s *State) presenceAdd(guildID string, presence *Presence) error {
|
||||
guild, ok := s.guildMap[guildID]
|
||||
if !ok {
|
||||
return ErrStateNotFound
|
||||
}
|
||||
|
||||
guild, err := s.Guild(guildID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
for i, p := range guild.Presences {
|
||||
if p.User.ID == presence.User.ID {
|
||||
//guild.Presences[i] = presence
|
||||
|
@ -233,6 +236,19 @@ func (s *State) PresenceAdd(guildID string, presence *Presence) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// PresenceAdd adds a presence to the current world state, or
|
||||
// updates it if it already exists.
|
||||
func (s *State) PresenceAdd(guildID string, presence *Presence) error {
|
||||
if s == nil {
|
||||
return ErrNilState
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
return s.presenceAdd(guildID, presence)
|
||||
}
|
||||
|
||||
// PresenceRemove removes a presence from the current world state.
|
||||
func (s *State) PresenceRemove(guildID string, presence *Presence) error {
|
||||
if s == nil {
|
||||
|
@ -279,21 +295,12 @@ func (s *State) Presence(guildID, userID string) (*Presence, error) {
|
|||
|
||||
// TODO: Consider moving Guild state update methods onto *Guild.
|
||||
|
||||
// MemberAdd adds a member to the current world state, or
|
||||
// updates it if it already exists.
|
||||
func (s *State) MemberAdd(member *Member) error {
|
||||
if s == nil {
|
||||
return ErrNilState
|
||||
func (s *State) memberAdd(member *Member) error {
|
||||
guild, ok := s.guildMap[member.GuildID]
|
||||
if !ok {
|
||||
return ErrStateNotFound
|
||||
}
|
||||
|
||||
guild, err := s.Guild(member.GuildID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
members, ok := s.memberMap[member.GuildID]
|
||||
if !ok {
|
||||
return ErrStateNotFound
|
||||
|
@ -311,10 +318,22 @@ func (s *State) MemberAdd(member *Member) error {
|
|||
}
|
||||
*m = *member
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MemberAdd adds a member to the current world state, or
|
||||
// updates it if it already exists.
|
||||
func (s *State) MemberAdd(member *Member) error {
|
||||
if s == nil {
|
||||
return ErrNilState
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
return s.memberAdd(member)
|
||||
}
|
||||
|
||||
// MemberRemove removes a member from current world state.
|
||||
func (s *State) MemberRemove(member *Member) error {
|
||||
if s == nil {
|
||||
|
@ -465,6 +484,9 @@ func (s *State) ChannelAdd(channel *Channel) error {
|
|||
if channel.PermissionOverwrites == nil {
|
||||
channel.PermissionOverwrites = c.PermissionOverwrites
|
||||
}
|
||||
if channel.ThreadMetadata == nil {
|
||||
channel.ThreadMetadata = c.ThreadMetadata
|
||||
}
|
||||
|
||||
*c = *channel
|
||||
return nil
|
||||
|
@ -472,12 +494,18 @@ func (s *State) ChannelAdd(channel *Channel) error {
|
|||
|
||||
if channel.Type == ChannelTypeDM || channel.Type == ChannelTypeGroupDM {
|
||||
s.PrivateChannels = append(s.PrivateChannels, channel)
|
||||
} else {
|
||||
guild, ok := s.guildMap[channel.GuildID]
|
||||
if !ok {
|
||||
return ErrStateNotFound
|
||||
}
|
||||
s.channelMap[channel.ID] = channel
|
||||
return nil
|
||||
}
|
||||
|
||||
guild, ok := s.guildMap[channel.GuildID]
|
||||
if !ok {
|
||||
return ErrStateNotFound
|
||||
}
|
||||
|
||||
if channel.IsThread() {
|
||||
guild.Threads = append(guild.Threads, channel)
|
||||
} else {
|
||||
guild.Channels = append(guild.Channels, channel)
|
||||
}
|
||||
|
||||
|
@ -507,15 +535,26 @@ func (s *State) ChannelRemove(channel *Channel) error {
|
|||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
guild, err := s.Guild(channel.GuildID)
|
||||
if err != nil {
|
||||
return err
|
||||
delete(s.channelMap, channel.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
guild, err := s.Guild(channel.GuildID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
if channel.IsThread() {
|
||||
for i, t := range guild.Threads {
|
||||
if t.ID == channel.ID {
|
||||
guild.Threads = append(guild.Threads[:i], guild.Threads[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
} else {
|
||||
for i, c := range guild.Channels {
|
||||
if c.ID == channel.ID {
|
||||
guild.Channels = append(guild.Channels[:i], guild.Channels[i+1:]...)
|
||||
|
@ -529,6 +568,99 @@ func (s *State) ChannelRemove(channel *Channel) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// ThreadListSync syncs guild threads with provided ones.
|
||||
func (s *State) ThreadListSync(tls *ThreadListSync) error {
|
||||
guild, err := s.Guild(tls.GuildID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
// This algorithm filters out archived or
|
||||
// threads which are children of channels in channelIDs
|
||||
// and then it adds all synced threads to guild threads and cache
|
||||
index := 0
|
||||
outer:
|
||||
for _, t := range guild.Threads {
|
||||
if !t.ThreadMetadata.Archived && tls.ChannelIDs != nil {
|
||||
for _, v := range tls.ChannelIDs {
|
||||
if t.ParentID == v {
|
||||
delete(s.channelMap, t.ID)
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
guild.Threads[index] = t
|
||||
index++
|
||||
} else {
|
||||
delete(s.channelMap, t.ID)
|
||||
}
|
||||
}
|
||||
guild.Threads = guild.Threads[:index]
|
||||
for _, t := range tls.Threads {
|
||||
s.channelMap[t.ID] = t
|
||||
guild.Threads = append(guild.Threads, t)
|
||||
}
|
||||
|
||||
for _, m := range tls.Members {
|
||||
if c, ok := s.channelMap[m.ID]; ok {
|
||||
c.Member = m
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ThreadMembersUpdate updates thread members list
|
||||
func (s *State) ThreadMembersUpdate(tmu *ThreadMembersUpdate) error {
|
||||
thread, err := s.Channel(tmu.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
for idx, member := range thread.Members {
|
||||
for _, removedMember := range tmu.RemovedMembers {
|
||||
if member.ID == removedMember {
|
||||
thread.Members = append(thread.Members[:idx], thread.Members[idx+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, addedMember := range tmu.AddedMembers {
|
||||
thread.Members = append(thread.Members, addedMember.ThreadMember)
|
||||
if addedMember.Member != nil {
|
||||
err = s.memberAdd(addedMember.Member)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if addedMember.Presence != nil {
|
||||
err = s.presenceAdd(tmu.GuildID, addedMember.Presence)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
thread.MemberCount = tmu.MemberCount
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ThreadMemberUpdate sets or updates member data for the current user.
|
||||
func (s *State) ThreadMemberUpdate(mu *ThreadMemberUpdate) error {
|
||||
thread, err := s.Channel(mu.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
thread.Member = mu.ThreadMember
|
||||
return nil
|
||||
}
|
||||
|
||||
// GuildChannel gets a channel by ID from a guild.
|
||||
// This method is Deprecated, use Channel(channelID)
|
||||
func (s *State) GuildChannel(guildID, channelID string) (*Channel, error) {
|
||||
|
@ -668,6 +800,7 @@ func (s *State) MessageAdd(message *Message) error {
|
|||
if len(c.Messages) > s.MaxMessageCount {
|
||||
c.Messages = c.Messages[len(c.Messages)-s.MaxMessageCount:]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -693,6 +826,7 @@ func (s *State) messageRemoveByID(channelID, messageID string) error {
|
|||
for i, m := range c.Messages {
|
||||
if m.ID == messageID {
|
||||
c.Messages = append(c.Messages[:i], c.Messages[i+1:]...)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -913,6 +1047,35 @@ func (s *State) OnInterface(se *Session, i interface{}) (err error) {
|
|||
if s.TrackChannels {
|
||||
err = s.ChannelRemove(t.Channel)
|
||||
}
|
||||
case *ThreadCreate:
|
||||
if s.TrackThreads {
|
||||
err = s.ChannelAdd(t.Channel)
|
||||
}
|
||||
case *ThreadUpdate:
|
||||
if s.TrackThreads {
|
||||
old, err := s.Channel(t.ID)
|
||||
if err == nil {
|
||||
oldCopy := *old
|
||||
t.BeforeUpdate = &oldCopy
|
||||
}
|
||||
err = s.ChannelAdd(t.Channel)
|
||||
}
|
||||
case *ThreadDelete:
|
||||
if s.TrackThreads {
|
||||
err = s.ChannelRemove(t.Channel)
|
||||
}
|
||||
case *ThreadMemberUpdate:
|
||||
if s.TrackThreads {
|
||||
err = s.ThreadMemberUpdate(t)
|
||||
}
|
||||
case *ThreadMembersUpdate:
|
||||
if s.TrackThreadMembers {
|
||||
err = s.ThreadMembersUpdate(t)
|
||||
}
|
||||
case *ThreadListSync:
|
||||
if s.TrackThreads {
|
||||
err = s.ThreadListSync(t)
|
||||
}
|
||||
case *MessageCreate:
|
||||
if s.MaxMessageCount != 0 {
|
||||
err = s.MessageAdd(t.Message)
|
||||
|
|
127
structs.go
127
structs.go
|
@ -226,13 +226,16 @@ type ChannelType int
|
|||
|
||||
// Block contains known ChannelType values
|
||||
const (
|
||||
ChannelTypeGuildText ChannelType = 0
|
||||
ChannelTypeDM ChannelType = 1
|
||||
ChannelTypeGuildVoice ChannelType = 2
|
||||
ChannelTypeGroupDM ChannelType = 3
|
||||
ChannelTypeGuildCategory ChannelType = 4
|
||||
ChannelTypeGuildNews ChannelType = 5
|
||||
ChannelTypeGuildStore ChannelType = 6
|
||||
ChannelTypeGuildText ChannelType = 0
|
||||
ChannelTypeDM ChannelType = 1
|
||||
ChannelTypeGuildVoice ChannelType = 2
|
||||
ChannelTypeGroupDM ChannelType = 3
|
||||
ChannelTypeGuildCategory ChannelType = 4
|
||||
ChannelTypeGuildNews ChannelType = 5
|
||||
ChannelTypeGuildStore ChannelType = 6
|
||||
ChannelTypeGuildNewsThread ChannelType = 10
|
||||
ChannelTypeGuildPublicThread ChannelType = 11
|
||||
ChannelTypeGuildPrivateThread ChannelType = 12
|
||||
)
|
||||
|
||||
// A Channel holds all data related to an individual Discord channel.
|
||||
|
@ -261,6 +264,11 @@ type Channel struct {
|
|||
// nil if the channel has no pinned messages.
|
||||
LastPinTimestamp *time.Time `json:"last_pin_timestamp"`
|
||||
|
||||
// An approximate count of messages in a thread, stops counting at 50
|
||||
MessageCount int `json:"message_count"`
|
||||
// An approximate count of users in a thread, stops counting at 50
|
||||
MemberCount int `json:"member_count"`
|
||||
|
||||
// Whether the channel is marked as NSFW.
|
||||
NSFW bool `json:"nsfw"`
|
||||
|
||||
|
@ -286,18 +294,26 @@ type Channel struct {
|
|||
// The user limit of the voice channel.
|
||||
UserLimit int `json:"user_limit"`
|
||||
|
||||
// The ID of the parent channel, if the channel is under a category
|
||||
// The ID of the parent channel, if the channel is under a category. For threads - id of the channel thread was created in.
|
||||
ParentID string `json:"parent_id"`
|
||||
|
||||
// Amount of seconds a user has to wait before sending another message (0-21600)
|
||||
// Amount of seconds a user has to wait before sending another message or creating another thread (0-21600)
|
||||
// bots, as well as users with the permission manage_messages or manage_channel, are unaffected
|
||||
RateLimitPerUser int `json:"rate_limit_per_user"`
|
||||
|
||||
// ID of the DM creator Zeroed if guild channel
|
||||
// ID of the creator of the group DM or thread
|
||||
OwnerID string `json:"owner_id"`
|
||||
|
||||
// ApplicationID of the DM creator Zeroed if guild channel or not a bot user
|
||||
ApplicationID string `json:"application_id"`
|
||||
|
||||
// Thread-specific fields not needed by other channels
|
||||
ThreadMetadata *ThreadMetadata `json:"thread_metadata,omitempty"`
|
||||
// Thread member object for the current user, if they have joined the thread, only included on certain API endpoints
|
||||
Member *ThreadMember `json:"thread_member"`
|
||||
|
||||
// All thread members. State channels only.
|
||||
Members []*ThreadMember `json:"-"`
|
||||
}
|
||||
|
||||
// Mention returns a string which mentions the channel
|
||||
|
@ -305,6 +321,11 @@ func (c *Channel) Mention() string {
|
|||
return fmt.Sprintf("<#%s>", c.ID)
|
||||
}
|
||||
|
||||
// IsThread is a helper function to determine if channel is a thread or not
|
||||
func (c *Channel) IsThread() bool {
|
||||
return c.Type == ChannelTypeGuildPublicThread || c.Type == ChannelTypeGuildPrivateThread || c.Type == ChannelTypeGuildNewsThread
|
||||
}
|
||||
|
||||
// A ChannelEdit holds Channel Field data for a channel edit.
|
||||
type ChannelEdit struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
|
@ -316,6 +337,13 @@ type ChannelEdit struct {
|
|||
PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites,omitempty"`
|
||||
ParentID string `json:"parent_id,omitempty"`
|
||||
RateLimitPerUser int `json:"rate_limit_per_user,omitempty"`
|
||||
|
||||
// NOTE: threads only
|
||||
|
||||
Archived bool `json:"archived,omitempty"`
|
||||
AutoArchiveDuration int `json:"auto_archive_duration,omitempty"`
|
||||
Locked bool `json:"locked,bool"`
|
||||
Invitable bool `json:"invitable,omitempty"`
|
||||
}
|
||||
|
||||
// A ChannelFollow holds data returned after following a news channel
|
||||
|
@ -342,6 +370,56 @@ type PermissionOverwrite struct {
|
|||
Allow int64 `json:"allow,string"`
|
||||
}
|
||||
|
||||
// ThreadStart stores all parameters you can use with MessageThreadStartComplex or ThreadStartComplex
|
||||
type ThreadStart struct {
|
||||
Name string `json:"name"`
|
||||
AutoArchiveDuration int `json:"auto_archive_duration,omitempty"`
|
||||
Type ChannelType `json:"type,omitempty"`
|
||||
Invitable bool `json:"invitable"`
|
||||
RateLimitPerUser int `json:"rate_limit_per_user,omitempty"`
|
||||
}
|
||||
|
||||
// ThreadMetadata contains a number of thread-specific channel fields that are not needed by other channel types.
|
||||
type ThreadMetadata struct {
|
||||
// Whether the thread is archived
|
||||
Archived bool `json:"archived"`
|
||||
// Duration in minutes to automatically archive the thread after recent activity, can be set to: 60, 1440, 4320, 10080
|
||||
AutoArchiveDuration int `json:"auto_archive_duration"`
|
||||
// Timestamp when the thread's archive status was last changed, used for calculating recent activity
|
||||
ArchiveTimestamp time.Time `json:"archive_timestamp"`
|
||||
// Whether the thread is locked; when a thread is locked, only users with MANAGE_THREADS can unarchive it
|
||||
Locked bool `json:"locked"`
|
||||
// Whether non-moderators can add other non-moderators to a thread; only available on private threads
|
||||
Invitable bool `json:"invitable"`
|
||||
}
|
||||
|
||||
// ThreadMember is used to indicate whether a user has joined a thread or not.
|
||||
// NOTE: ID and UserID are empty (omitted) on the member sent within each thread in the GUILD_CREATE event.
|
||||
type ThreadMember struct {
|
||||
// The id of the thread
|
||||
ID string `json:"id,omitempty"`
|
||||
// The id of the user
|
||||
UserID string `json:"user_id,omitempty"`
|
||||
// The time the current user last joined the thread
|
||||
JoinTimestamp time.Time `json:"join_timestamp"`
|
||||
// Any user-thread settings, currently only used for notifications
|
||||
Flags int
|
||||
}
|
||||
|
||||
// ThreadsList represents a list of threads alongisde with thread member objects for the current user.
|
||||
type ThreadsList struct {
|
||||
Threads []*Channel `json:"threads"`
|
||||
Members []*ThreadMember `json:"members"`
|
||||
HasMore bool `json:"has_more"`
|
||||
}
|
||||
|
||||
// AddedThreadMember holds information about the user who was added to the thread
|
||||
type AddedThreadMember struct {
|
||||
*ThreadMember
|
||||
Member *Member `json:"member"`
|
||||
Presence *Presence `json:"presence"`
|
||||
}
|
||||
|
||||
// Emoji struct holds data related to Emoji's
|
||||
type Emoji struct {
|
||||
ID string `json:"id"`
|
||||
|
@ -507,6 +585,11 @@ type Guild struct {
|
|||
// update events, and thus is only present in state-cached guilds.
|
||||
Channels []*Channel `json:"channels"`
|
||||
|
||||
// A list of all active threads in the guild that current user has permission to view
|
||||
// This field is only present in GUILD_CREATE events and websocket
|
||||
// update events and thus is only present in state-cached guilds.
|
||||
Threads []*Channel `json:"threads"`
|
||||
|
||||
// A list of voice states for the guild.
|
||||
// This field is only present in GUILD_CREATE events and websocket
|
||||
// update events, and thus is only present in state-cached guilds.
|
||||
|
@ -1367,16 +1450,20 @@ type IdentifyProperties struct {
|
|||
// Constants for the different bit offsets of text channel permissions
|
||||
const (
|
||||
// Deprecated: PermissionReadMessages has been replaced with PermissionViewChannel for text and voice channels
|
||||
PermissionReadMessages = 0x0000000000000400
|
||||
PermissionSendMessages = 0x0000000000000800
|
||||
PermissionSendTTSMessages = 0x0000000000001000
|
||||
PermissionManageMessages = 0x0000000000002000
|
||||
PermissionEmbedLinks = 0x0000000000004000
|
||||
PermissionAttachFiles = 0x0000000000008000
|
||||
PermissionReadMessageHistory = 0x0000000000010000
|
||||
PermissionMentionEveryone = 0x0000000000020000
|
||||
PermissionUseExternalEmojis = 0x0000000000040000
|
||||
PermissionUseSlashCommands = 0x0000000080000000
|
||||
PermissionReadMessages = 0x0000000000000400
|
||||
PermissionSendMessages = 0x0000000000000800
|
||||
PermissionSendTTSMessages = 0x0000000000001000
|
||||
PermissionManageMessages = 0x0000000000002000
|
||||
PermissionEmbedLinks = 0x0000000000004000
|
||||
PermissionAttachFiles = 0x0000000000008000
|
||||
PermissionReadMessageHistory = 0x0000000000010000
|
||||
PermissionMentionEveryone = 0x0000000000020000
|
||||
PermissionUseExternalEmojis = 0x0000000000040000
|
||||
PermissionUseSlashCommands = 0x0000000080000000
|
||||
PermissionManageThreads = 0x0000000400000000
|
||||
PermissionCreatePublicThreads = 0x0000000800000000
|
||||
PermissionCreatePrivateThreads = 0x0000001000000000
|
||||
PermissionSendMessagesInThreads = 0x0000004000000000
|
||||
)
|
||||
|
||||
// Constants for the different bit offsets of voice permissions
|
||||
|
|
Loading…
Reference in a new issue