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
15
endpoints.go
15
endpoints.go
|
@ -14,7 +14,7 @@ package discordgo
|
||||||
import "strconv"
|
import "strconv"
|
||||||
|
|
||||||
// APIVersion is the Discord API version used for the REST and Websocket API.
|
// APIVersion is the Discord API version used for the REST and Websocket API.
|
||||||
var APIVersion = "8"
|
var APIVersion = "9"
|
||||||
|
|
||||||
// Known Discord API Endpoints.
|
// Known Discord API Endpoints.
|
||||||
var (
|
var (
|
||||||
|
@ -53,12 +53,15 @@ var (
|
||||||
uDiscriminatorInt, _ := strconv.Atoi(uDiscriminator)
|
uDiscriminatorInt, _ := strconv.Atoi(uDiscriminator)
|
||||||
return EndpointCDN + "embed/avatars/" + strconv.Itoa(uDiscriminatorInt%5) + ".png"
|
return EndpointCDN + "embed/avatars/" + strconv.Itoa(uDiscriminatorInt%5) + ".png"
|
||||||
}
|
}
|
||||||
|
|
||||||
EndpointUserGuilds = func(uID string) string { return EndpointUsers + uID + "/guilds" }
|
EndpointUserGuilds = func(uID string) string { return EndpointUsers + uID + "/guilds" }
|
||||||
EndpointUserGuild = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID }
|
EndpointUserGuild = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID }
|
||||||
EndpointUserChannels = func(uID string) string { return EndpointUsers + uID + "/channels" }
|
EndpointUserChannels = func(uID string) string { return EndpointUsers + uID + "/channels" }
|
||||||
EndpointUserConnections = func(uID string) string { return EndpointUsers + uID + "/connections" }
|
EndpointUserConnections = func(uID string) string { return EndpointUsers + uID + "/connections" }
|
||||||
|
|
||||||
EndpointGuild = func(gID string) string { return EndpointGuilds + gID }
|
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" }
|
EndpointGuildPreview = func(gID string) string { return EndpointGuilds + gID + "/preview" }
|
||||||
EndpointGuildChannels = func(gID string) string { return EndpointGuilds + gID + "/channels" }
|
EndpointGuildChannels = func(gID string) string { return EndpointGuilds + gID + "/channels" }
|
||||||
EndpointGuildMembers = func(gID string) string { return EndpointGuilds + gID + "/members" }
|
EndpointGuildMembers = func(gID string) string { return EndpointGuilds + gID + "/members" }
|
||||||
|
@ -86,17 +89,25 @@ var (
|
||||||
EndpointGuildSticker = func(gID, sID string) string { return EndpointGuilds + gID + "/stickers/" + sID }
|
EndpointGuildSticker = func(gID, sID string) string { return EndpointGuilds + gID + "/stickers/" + sID }
|
||||||
|
|
||||||
EndpointChannel = func(cID string) string { return EndpointChannels + cID }
|
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" }
|
EndpointChannelPermissions = func(cID string) string { return EndpointChannels + cID + "/permissions" }
|
||||||
EndpointChannelPermission = func(cID, tID string) string { return EndpointChannelPermissions(cID) + "/" + tID }
|
EndpointChannelPermission = func(cID, tID string) string { return EndpointChannels + cID + "/permissions/" + tID }
|
||||||
EndpointChannelInvites = func(cID string) string { return EndpointChannels + cID + "/invites" }
|
EndpointChannelInvites = func(cID string) string { return EndpointChannels + cID + "/invites" }
|
||||||
EndpointChannelTyping = func(cID string) string { return EndpointChannels + cID + "/typing" }
|
EndpointChannelTyping = func(cID string) string { return EndpointChannels + cID + "/typing" }
|
||||||
EndpointChannelMessages = func(cID string) string { return EndpointChannels + cID + "/messages" }
|
EndpointChannelMessages = func(cID string) string { return EndpointChannels + cID + "/messages" }
|
||||||
EndpointChannelMessage = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID }
|
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" }
|
EndpointChannelMessagesBulkDelete = func(cID string) string { return EndpointChannel(cID) + "/messages/bulk-delete" }
|
||||||
EndpointChannelMessagesPins = func(cID string) string { return EndpointChannel(cID) + "/pins" }
|
EndpointChannelMessagesPins = func(cID string) string { return EndpointChannel(cID) + "/pins" }
|
||||||
EndpointChannelMessagePin = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID }
|
EndpointChannelMessagePin = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID }
|
||||||
EndpointChannelMessageCrosspost = func(cID, mID string) string { return EndpointChannel(cID) + "/messages/" + mID + "/crosspost" }
|
EndpointChannelMessageCrosspost = func(cID, mID string) string { return EndpointChannel(cID) + "/messages/" + mID + "/crosspost" }
|
||||||
EndpointChannelFollow = func(cID string) string { return EndpointChannel(cID) + "/followers" }
|
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" }
|
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"
|
relationshipAddEventType = "RELATIONSHIP_ADD"
|
||||||
relationshipRemoveEventType = "RELATIONSHIP_REMOVE"
|
relationshipRemoveEventType = "RELATIONSHIP_REMOVE"
|
||||||
resumedEventType = "RESUMED"
|
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"
|
typingStartEventType = "TYPING_START"
|
||||||
userGuildSettingsUpdateEventType = "USER_GUILD_SETTINGS_UPDATE"
|
userGuildSettingsUpdateEventType = "USER_GUILD_SETTINGS_UPDATE"
|
||||||
userNoteUpdateEventType = "USER_NOTE_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.
|
// typingStartEventHandler is an event handler for TypingStart events.
|
||||||
type typingStartEventHandler func(*Session, *TypingStart)
|
type typingStartEventHandler func(*Session, *TypingStart)
|
||||||
|
|
||||||
|
@ -1012,6 +1138,18 @@ func handlerForInterface(handler interface{}) EventHandler {
|
||||||
return relationshipRemoveEventHandler(v)
|
return relationshipRemoveEventHandler(v)
|
||||||
case func(*Session, *Resumed):
|
case func(*Session, *Resumed):
|
||||||
return resumedEventHandler(v)
|
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):
|
case func(*Session, *TypingStart):
|
||||||
return typingStartEventHandler(v)
|
return typingStartEventHandler(v)
|
||||||
case func(*Session, *UserGuildSettingsUpdate):
|
case func(*Session, *UserGuildSettingsUpdate):
|
||||||
|
@ -1067,6 +1205,12 @@ func init() {
|
||||||
registerInterfaceProvider(relationshipAddEventHandler(nil))
|
registerInterfaceProvider(relationshipAddEventHandler(nil))
|
||||||
registerInterfaceProvider(relationshipRemoveEventHandler(nil))
|
registerInterfaceProvider(relationshipRemoveEventHandler(nil))
|
||||||
registerInterfaceProvider(resumedEventHandler(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(typingStartEventHandler(nil))
|
||||||
registerInterfaceProvider(userGuildSettingsUpdateEventHandler(nil))
|
registerInterfaceProvider(userGuildSettingsUpdateEventHandler(nil))
|
||||||
registerInterfaceProvider(userNoteUpdateEventHandler(nil))
|
registerInterfaceProvider(userNoteUpdateEventHandler(nil))
|
||||||
|
|
47
events.go
47
events.go
|
@ -73,6 +73,53 @@ type ChannelPinsUpdate struct {
|
||||||
GuildID string `json:"guild_id,omitempty"`
|
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.
|
// GuildCreate is the data for a GuildCreate event.
|
||||||
type GuildCreate struct {
|
type GuildCreate struct {
|
||||||
*Guild
|
*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
|
MessageTypeChannelFollowAdd MessageType = 12
|
||||||
MessageTypeGuildDiscoveryDisqualified MessageType = 14
|
MessageTypeGuildDiscoveryDisqualified MessageType = 14
|
||||||
MessageTypeGuildDiscoveryRequalified MessageType = 15
|
MessageTypeGuildDiscoveryRequalified MessageType = 15
|
||||||
|
MessageTypeThreadCreated MessageType = 18
|
||||||
MessageTypeReply MessageType = 19
|
MessageTypeReply MessageType = 19
|
||||||
MessageTypeChatInputCommand MessageType = 20
|
MessageTypeChatInputCommand MessageType = 20
|
||||||
|
MessageTypeThreadStarterMessage MessageType = 21
|
||||||
MessageTypeContextMenuCommand MessageType = 23
|
MessageTypeContextMenuCommand MessageType = 23
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -126,11 +128,21 @@ type Message struct {
|
||||||
// To generate a reference to this message, use (*Message).Reference().
|
// To generate a reference to this message, use (*Message).Reference().
|
||||||
MessageReference *MessageReference `json:"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.
|
// 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
|
// 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.
|
// be checked by performing a bitwise AND between this int and the flag.
|
||||||
Flags MessageFlags `json:"flags"`
|
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.
|
// An array of Sticker objects, if any were sent.
|
||||||
StickerItems []*Sticker `json:"sticker_items"`
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebhookExecute executes a webhook.
|
func (s *Session) webhookExecute(webhookID, token string, wait bool, threadID string, data *WebhookParams) (st *Message, err error) {
|
||||||
// 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) {
|
|
||||||
uri := EndpointWebhookToken(webhookID, token)
|
uri := EndpointWebhookToken(webhookID, token)
|
||||||
|
|
||||||
|
v := url.Values{}
|
||||||
if wait {
|
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
|
var response []byte
|
||||||
|
@ -2018,6 +2022,23 @@ func (s *Session) WebhookExecute(webhookID, token string, wait bool, data *Webho
|
||||||
return
|
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.
|
// WebhookMessage gets a webhook message.
|
||||||
// webhookID : The ID of a webhook
|
// webhookID : The ID of a webhook
|
||||||
// token : The auth token for the webhook
|
// token : The auth token for the webhook
|
||||||
|
@ -2164,6 +2185,226 @@ func (s *Session) MessageReactions(channelID, messageID, emojiID string, limit i
|
||||||
return
|
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
|
// Functions specific to application (slash) commands
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
221
state.go
221
state.go
|
@ -40,8 +40,10 @@ type State struct {
|
||||||
// MaxMessageCount represents how many messages per channel the state will store.
|
// MaxMessageCount represents how many messages per channel the state will store.
|
||||||
MaxMessageCount int
|
MaxMessageCount int
|
||||||
TrackChannels bool
|
TrackChannels bool
|
||||||
|
TrackThreads bool
|
||||||
TrackEmojis bool
|
TrackEmojis bool
|
||||||
TrackMembers bool
|
TrackMembers bool
|
||||||
|
TrackThreadMembers bool
|
||||||
TrackRoles bool
|
TrackRoles bool
|
||||||
TrackVoice bool
|
TrackVoice bool
|
||||||
TrackPresences bool
|
TrackPresences bool
|
||||||
|
@ -59,8 +61,10 @@ func NewState() *State {
|
||||||
Guilds: []*Guild{},
|
Guilds: []*Guild{},
|
||||||
},
|
},
|
||||||
TrackChannels: true,
|
TrackChannels: true,
|
||||||
|
TrackThreads: true,
|
||||||
TrackEmojis: true,
|
TrackEmojis: true,
|
||||||
TrackMembers: true,
|
TrackMembers: true,
|
||||||
|
TrackThreadMembers: true,
|
||||||
TrackRoles: true,
|
TrackRoles: true,
|
||||||
TrackVoice: true,
|
TrackVoice: true,
|
||||||
TrackPresences: true,
|
TrackPresences: true,
|
||||||
|
@ -93,6 +97,11 @@ func (s *State) GuildAdd(guild *Guild) error {
|
||||||
s.channelMap[c.ID] = c
|
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 this guild contains a new member slice, we must regenerate the member map so the pointers stay valid
|
||||||
if guild.Members != nil {
|
if guild.Members != nil {
|
||||||
s.createMemberMap(guild)
|
s.createMemberMap(guild)
|
||||||
|
@ -122,6 +131,9 @@ func (s *State) GuildAdd(guild *Guild) error {
|
||||||
if guild.Channels == nil {
|
if guild.Channels == nil {
|
||||||
guild.Channels = g.Channels
|
guild.Channels = g.Channels
|
||||||
}
|
}
|
||||||
|
if guild.Threads == nil {
|
||||||
|
guild.Threads = g.Threads
|
||||||
|
}
|
||||||
if guild.VoiceStates == nil {
|
if guild.VoiceStates == nil {
|
||||||
guild.VoiceStates = g.VoiceStates
|
guild.VoiceStates = g.VoiceStates
|
||||||
}
|
}
|
||||||
|
@ -180,21 +192,12 @@ func (s *State) Guild(guildID string) (*Guild, error) {
|
||||||
return nil, ErrStateNotFound
|
return nil, ErrStateNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// PresenceAdd adds a presence to the current world state, or
|
func (s *State) presenceAdd(guildID string, presence *Presence) error {
|
||||||
// updates it if it already exists.
|
guild, ok := s.guildMap[guildID]
|
||||||
func (s *State) PresenceAdd(guildID string, presence *Presence) error {
|
if !ok {
|
||||||
if s == nil {
|
return ErrStateNotFound
|
||||||
return ErrNilState
|
|
||||||
}
|
}
|
||||||
|
|
||||||
guild, err := s.Guild(guildID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
|
|
||||||
for i, p := range guild.Presences {
|
for i, p := range guild.Presences {
|
||||||
if p.User.ID == presence.User.ID {
|
if p.User.ID == presence.User.ID {
|
||||||
//guild.Presences[i] = presence
|
//guild.Presences[i] = presence
|
||||||
|
@ -233,6 +236,19 @@ func (s *State) PresenceAdd(guildID string, presence *Presence) error {
|
||||||
return nil
|
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.
|
// PresenceRemove removes a presence from the current world state.
|
||||||
func (s *State) PresenceRemove(guildID string, presence *Presence) error {
|
func (s *State) PresenceRemove(guildID string, presence *Presence) error {
|
||||||
if s == nil {
|
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.
|
// TODO: Consider moving Guild state update methods onto *Guild.
|
||||||
|
|
||||||
// MemberAdd adds a member to the current world state, or
|
func (s *State) memberAdd(member *Member) error {
|
||||||
// updates it if it already exists.
|
guild, ok := s.guildMap[member.GuildID]
|
||||||
func (s *State) MemberAdd(member *Member) error {
|
if !ok {
|
||||||
if s == nil {
|
return ErrStateNotFound
|
||||||
return ErrNilState
|
|
||||||
}
|
}
|
||||||
|
|
||||||
guild, err := s.Guild(member.GuildID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
|
|
||||||
members, ok := s.memberMap[member.GuildID]
|
members, ok := s.memberMap[member.GuildID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return ErrStateNotFound
|
return ErrStateNotFound
|
||||||
|
@ -311,10 +318,22 @@ func (s *State) MemberAdd(member *Member) error {
|
||||||
}
|
}
|
||||||
*m = *member
|
*m = *member
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
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.
|
// MemberRemove removes a member from current world state.
|
||||||
func (s *State) MemberRemove(member *Member) error {
|
func (s *State) MemberRemove(member *Member) error {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
|
@ -465,6 +484,9 @@ func (s *State) ChannelAdd(channel *Channel) error {
|
||||||
if channel.PermissionOverwrites == nil {
|
if channel.PermissionOverwrites == nil {
|
||||||
channel.PermissionOverwrites = c.PermissionOverwrites
|
channel.PermissionOverwrites = c.PermissionOverwrites
|
||||||
}
|
}
|
||||||
|
if channel.ThreadMetadata == nil {
|
||||||
|
channel.ThreadMetadata = c.ThreadMetadata
|
||||||
|
}
|
||||||
|
|
||||||
*c = *channel
|
*c = *channel
|
||||||
return nil
|
return nil
|
||||||
|
@ -472,12 +494,18 @@ func (s *State) ChannelAdd(channel *Channel) error {
|
||||||
|
|
||||||
if channel.Type == ChannelTypeDM || channel.Type == ChannelTypeGroupDM {
|
if channel.Type == ChannelTypeDM || channel.Type == ChannelTypeGroupDM {
|
||||||
s.PrivateChannels = append(s.PrivateChannels, channel)
|
s.PrivateChannels = append(s.PrivateChannels, channel)
|
||||||
} else {
|
s.channelMap[channel.ID] = channel
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
guild, ok := s.guildMap[channel.GuildID]
|
guild, ok := s.guildMap[channel.GuildID]
|
||||||
if !ok {
|
if !ok {
|
||||||
return ErrStateNotFound
|
return ErrStateNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if channel.IsThread() {
|
||||||
|
guild.Threads = append(guild.Threads, channel)
|
||||||
|
} else {
|
||||||
guild.Channels = append(guild.Channels, channel)
|
guild.Channels = append(guild.Channels, channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -507,7 +535,10 @@ func (s *State) ChannelRemove(channel *Channel) error {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
delete(s.channelMap, channel.ID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
guild, err := s.Guild(channel.GuildID)
|
guild, err := s.Guild(channel.GuildID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -516,6 +547,14 @@ func (s *State) ChannelRemove(channel *Channel) error {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
defer s.Unlock()
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
for i, c := range guild.Channels {
|
for i, c := range guild.Channels {
|
||||||
if c.ID == channel.ID {
|
if c.ID == channel.ID {
|
||||||
guild.Channels = append(guild.Channels[:i], guild.Channels[i+1:]...)
|
guild.Channels = append(guild.Channels[:i], guild.Channels[i+1:]...)
|
||||||
|
@ -529,6 +568,99 @@ func (s *State) ChannelRemove(channel *Channel) error {
|
||||||
return nil
|
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.
|
// GuildChannel gets a channel by ID from a guild.
|
||||||
// This method is Deprecated, use Channel(channelID)
|
// This method is Deprecated, use Channel(channelID)
|
||||||
func (s *State) GuildChannel(guildID, channelID string) (*Channel, error) {
|
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 {
|
if len(c.Messages) > s.MaxMessageCount {
|
||||||
c.Messages = c.Messages[len(c.Messages)-s.MaxMessageCount:]
|
c.Messages = c.Messages[len(c.Messages)-s.MaxMessageCount:]
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -693,6 +826,7 @@ func (s *State) messageRemoveByID(channelID, messageID string) error {
|
||||||
for i, m := range c.Messages {
|
for i, m := range c.Messages {
|
||||||
if m.ID == messageID {
|
if m.ID == messageID {
|
||||||
c.Messages = append(c.Messages[:i], c.Messages[i+1:]...)
|
c.Messages = append(c.Messages[:i], c.Messages[i+1:]...)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -913,6 +1047,35 @@ func (s *State) OnInterface(se *Session, i interface{}) (err error) {
|
||||||
if s.TrackChannels {
|
if s.TrackChannels {
|
||||||
err = s.ChannelRemove(t.Channel)
|
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:
|
case *MessageCreate:
|
||||||
if s.MaxMessageCount != 0 {
|
if s.MaxMessageCount != 0 {
|
||||||
err = s.MessageAdd(t.Message)
|
err = s.MessageAdd(t.Message)
|
||||||
|
|
93
structs.go
93
structs.go
|
@ -233,6 +233,9 @@ const (
|
||||||
ChannelTypeGuildCategory ChannelType = 4
|
ChannelTypeGuildCategory ChannelType = 4
|
||||||
ChannelTypeGuildNews ChannelType = 5
|
ChannelTypeGuildNews ChannelType = 5
|
||||||
ChannelTypeGuildStore ChannelType = 6
|
ChannelTypeGuildStore ChannelType = 6
|
||||||
|
ChannelTypeGuildNewsThread ChannelType = 10
|
||||||
|
ChannelTypeGuildPublicThread ChannelType = 11
|
||||||
|
ChannelTypeGuildPrivateThread ChannelType = 12
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Channel holds all data related to an individual Discord channel.
|
// 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.
|
// nil if the channel has no pinned messages.
|
||||||
LastPinTimestamp *time.Time `json:"last_pin_timestamp"`
|
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.
|
// Whether the channel is marked as NSFW.
|
||||||
NSFW bool `json:"nsfw"`
|
NSFW bool `json:"nsfw"`
|
||||||
|
|
||||||
|
@ -286,18 +294,26 @@ type Channel struct {
|
||||||
// The user limit of the voice channel.
|
// The user limit of the voice channel.
|
||||||
UserLimit int `json:"user_limit"`
|
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"`
|
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
|
// bots, as well as users with the permission manage_messages or manage_channel, are unaffected
|
||||||
RateLimitPerUser int `json:"rate_limit_per_user"`
|
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"`
|
OwnerID string `json:"owner_id"`
|
||||||
|
|
||||||
// ApplicationID of the DM creator Zeroed if guild channel or not a bot user
|
// ApplicationID of the DM creator Zeroed if guild channel or not a bot user
|
||||||
ApplicationID string `json:"application_id"`
|
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
|
// Mention returns a string which mentions the channel
|
||||||
|
@ -305,6 +321,11 @@ func (c *Channel) Mention() string {
|
||||||
return fmt.Sprintf("<#%s>", c.ID)
|
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.
|
// A ChannelEdit holds Channel Field data for a channel edit.
|
||||||
type ChannelEdit struct {
|
type ChannelEdit struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
|
@ -316,6 +337,13 @@ type ChannelEdit struct {
|
||||||
PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites,omitempty"`
|
PermissionOverwrites []*PermissionOverwrite `json:"permission_overwrites,omitempty"`
|
||||||
ParentID string `json:"parent_id,omitempty"`
|
ParentID string `json:"parent_id,omitempty"`
|
||||||
RateLimitPerUser int `json:"rate_limit_per_user,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
|
// A ChannelFollow holds data returned after following a news channel
|
||||||
|
@ -342,6 +370,56 @@ type PermissionOverwrite struct {
|
||||||
Allow int64 `json:"allow,string"`
|
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
|
// Emoji struct holds data related to Emoji's
|
||||||
type Emoji struct {
|
type Emoji struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
|
@ -507,6 +585,11 @@ type Guild struct {
|
||||||
// update events, and thus is only present in state-cached guilds.
|
// update events, and thus is only present in state-cached guilds.
|
||||||
Channels []*Channel `json:"channels"`
|
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.
|
// A list of voice states for the guild.
|
||||||
// This field is only present in GUILD_CREATE events and websocket
|
// This field is only present in GUILD_CREATE events and websocket
|
||||||
// update events, and thus is only present in state-cached guilds.
|
// update events, and thus is only present in state-cached guilds.
|
||||||
|
@ -1377,6 +1460,10 @@ const (
|
||||||
PermissionMentionEveryone = 0x0000000000020000
|
PermissionMentionEveryone = 0x0000000000020000
|
||||||
PermissionUseExternalEmojis = 0x0000000000040000
|
PermissionUseExternalEmojis = 0x0000000000040000
|
||||||
PermissionUseSlashCommands = 0x0000000080000000
|
PermissionUseSlashCommands = 0x0000000080000000
|
||||||
|
PermissionManageThreads = 0x0000000400000000
|
||||||
|
PermissionCreatePublicThreads = 0x0000000800000000
|
||||||
|
PermissionCreatePrivateThreads = 0x0000001000000000
|
||||||
|
PermissionSendMessagesInThreads = 0x0000004000000000
|
||||||
)
|
)
|
||||||
|
|
||||||
// Constants for the different bit offsets of voice permissions
|
// Constants for the different bit offsets of voice permissions
|
||||||
|
|
Loading…
Reference in a new issue