Switch to discordgo upstream again (#1759)
* Switch to upstream discordgo again * Fix discord api changespull/1760/head
parent
9c203327c0
commit
e4c0ca0f48
@ -0,0 +1,5 @@
|
|||||||
|
# IDE-specific metadata
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Environment variables. Useful for examples.
|
||||||
|
.env
|
@ -0,0 +1,19 @@
|
|||||||
|
linters:
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
# - staticcheck
|
||||||
|
# - unused
|
||||||
|
- golint
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
staticcheck:
|
||||||
|
go: "1.13"
|
||||||
|
|
||||||
|
checks: ["all"]
|
||||||
|
|
||||||
|
unused:
|
||||||
|
go: "1.13"
|
||||||
|
|
||||||
|
issues:
|
||||||
|
include:
|
||||||
|
- EXC0002
|
@ -0,0 +1,241 @@
|
|||||||
|
package discordgo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ComponentType is type of component.
|
||||||
|
type ComponentType uint
|
||||||
|
|
||||||
|
// MessageComponent types.
|
||||||
|
const (
|
||||||
|
ActionsRowComponent ComponentType = 1
|
||||||
|
ButtonComponent ComponentType = 2
|
||||||
|
SelectMenuComponent ComponentType = 3
|
||||||
|
TextInputComponent ComponentType = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// MessageComponent is a base interface for all message components.
|
||||||
|
type MessageComponent interface {
|
||||||
|
json.Marshaler
|
||||||
|
Type() ComponentType
|
||||||
|
}
|
||||||
|
|
||||||
|
type unmarshalableMessageComponent struct {
|
||||||
|
MessageComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON is a helper function to unmarshal MessageComponent object.
|
||||||
|
func (umc *unmarshalableMessageComponent) UnmarshalJSON(src []byte) error {
|
||||||
|
var v struct {
|
||||||
|
Type ComponentType `json:"type"`
|
||||||
|
}
|
||||||
|
err := json.Unmarshal(src, &v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Type {
|
||||||
|
case ActionsRowComponent:
|
||||||
|
umc.MessageComponent = &ActionsRow{}
|
||||||
|
case ButtonComponent:
|
||||||
|
umc.MessageComponent = &Button{}
|
||||||
|
case SelectMenuComponent:
|
||||||
|
umc.MessageComponent = &SelectMenu{}
|
||||||
|
case TextInputComponent:
|
||||||
|
umc.MessageComponent = &TextInput{}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown component type: %d", v.Type)
|
||||||
|
}
|
||||||
|
return json.Unmarshal(src, umc.MessageComponent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageComponentFromJSON is a helper function for unmarshaling message components
|
||||||
|
func MessageComponentFromJSON(b []byte) (MessageComponent, error) {
|
||||||
|
var u unmarshalableMessageComponent
|
||||||
|
err := u.UnmarshalJSON(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal into MessageComponent: %w", err)
|
||||||
|
}
|
||||||
|
return u.MessageComponent, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActionsRow is a container for components within one row.
|
||||||
|
type ActionsRow struct {
|
||||||
|
Components []MessageComponent `json:"components"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON is a method for marshaling ActionsRow to a JSON object.
|
||||||
|
func (r ActionsRow) MarshalJSON() ([]byte, error) {
|
||||||
|
type actionsRow ActionsRow
|
||||||
|
|
||||||
|
return json.Marshal(struct {
|
||||||
|
actionsRow
|
||||||
|
Type ComponentType `json:"type"`
|
||||||
|
}{
|
||||||
|
actionsRow: actionsRow(r),
|
||||||
|
Type: r.Type(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON is a helper function to unmarshal Actions Row.
|
||||||
|
func (r *ActionsRow) UnmarshalJSON(data []byte) error {
|
||||||
|
var v struct {
|
||||||
|
RawComponents []unmarshalableMessageComponent `json:"components"`
|
||||||
|
}
|
||||||
|
err := json.Unmarshal(data, &v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.Components = make([]MessageComponent, len(v.RawComponents))
|
||||||
|
for i, v := range v.RawComponents {
|
||||||
|
r.Components[i] = v.MessageComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type is a method to get the type of a component.
|
||||||
|
func (r ActionsRow) Type() ComponentType {
|
||||||
|
return ActionsRowComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
// ButtonStyle is style of button.
|
||||||
|
type ButtonStyle uint
|
||||||
|
|
||||||
|
// Button styles.
|
||||||
|
const (
|
||||||
|
// PrimaryButton is a button with blurple color.
|
||||||
|
PrimaryButton ButtonStyle = 1
|
||||||
|
// SecondaryButton is a button with grey color.
|
||||||
|
SecondaryButton ButtonStyle = 2
|
||||||
|
// SuccessButton is a button with green color.
|
||||||
|
SuccessButton ButtonStyle = 3
|
||||||
|
// DangerButton is a button with red color.
|
||||||
|
DangerButton ButtonStyle = 4
|
||||||
|
// LinkButton is a special type of button which navigates to a URL. Has grey color.
|
||||||
|
LinkButton ButtonStyle = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
// ComponentEmoji represents button emoji, if it does have one.
|
||||||
|
type ComponentEmoji struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
Animated bool `json:"animated,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button represents button component.
|
||||||
|
type Button struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
Style ButtonStyle `json:"style"`
|
||||||
|
Disabled bool `json:"disabled"`
|
||||||
|
Emoji ComponentEmoji `json:"emoji"`
|
||||||
|
|
||||||
|
// NOTE: Only button with LinkButton style can have link. Also, URL is mutually exclusive with CustomID.
|
||||||
|
URL string `json:"url,omitempty"`
|
||||||
|
CustomID string `json:"custom_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON is a method for marshaling Button to a JSON object.
|
||||||
|
func (b Button) MarshalJSON() ([]byte, error) {
|
||||||
|
type button Button
|
||||||
|
|
||||||
|
if b.Style == 0 {
|
||||||
|
b.Style = PrimaryButton
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(struct {
|
||||||
|
button
|
||||||
|
Type ComponentType `json:"type"`
|
||||||
|
}{
|
||||||
|
button: button(b),
|
||||||
|
Type: b.Type(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type is a method to get the type of a component.
|
||||||
|
func (Button) Type() ComponentType {
|
||||||
|
return ButtonComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectMenuOption represents an option for a select menu.
|
||||||
|
type SelectMenuOption struct {
|
||||||
|
Label string `json:"label,omitempty"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Emoji ComponentEmoji `json:"emoji"`
|
||||||
|
// Determines whenever option is selected by default or not.
|
||||||
|
Default bool `json:"default"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectMenu represents select menu component.
|
||||||
|
type SelectMenu struct {
|
||||||
|
CustomID string `json:"custom_id,omitempty"`
|
||||||
|
// The text which will be shown in the menu if there's no default options or all options was deselected and component was closed.
|
||||||
|
Placeholder string `json:"placeholder"`
|
||||||
|
// This value determines the minimal amount of selected items in the menu.
|
||||||
|
MinValues *int `json:"min_values,omitempty"`
|
||||||
|
// This value determines the maximal amount of selected items in the menu.
|
||||||
|
// If MaxValues or MinValues are greater than one then the user can select multiple items in the component.
|
||||||
|
MaxValues int `json:"max_values,omitempty"`
|
||||||
|
Options []SelectMenuOption `json:"options"`
|
||||||
|
Disabled bool `json:"disabled"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type is a method to get the type of a component.
|
||||||
|
func (SelectMenu) Type() ComponentType {
|
||||||
|
return SelectMenuComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON is a method for marshaling SelectMenu to a JSON object.
|
||||||
|
func (m SelectMenu) MarshalJSON() ([]byte, error) {
|
||||||
|
type selectMenu SelectMenu
|
||||||
|
|
||||||
|
return json.Marshal(struct {
|
||||||
|
selectMenu
|
||||||
|
Type ComponentType `json:"type"`
|
||||||
|
}{
|
||||||
|
selectMenu: selectMenu(m),
|
||||||
|
Type: m.Type(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextInput represents text input component.
|
||||||
|
type TextInput struct {
|
||||||
|
CustomID string `json:"custom_id"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
Style TextInputStyle `json:"style"`
|
||||||
|
Placeholder string `json:"placeholder,omitempty"`
|
||||||
|
Value string `json:"value,omitempty"`
|
||||||
|
Required bool `json:"required,omitempty"`
|
||||||
|
MinLength int `json:"min_length,omitempty"`
|
||||||
|
MaxLength int `json:"max_length,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type is a method to get the type of a component.
|
||||||
|
func (TextInput) Type() ComponentType {
|
||||||
|
return TextInputComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON is a method for marshaling TextInput to a JSON object.
|
||||||
|
func (m TextInput) MarshalJSON() ([]byte, error) {
|
||||||
|
type inputText TextInput
|
||||||
|
|
||||||
|
return json.Marshal(struct {
|
||||||
|
inputText
|
||||||
|
Type ComponentType `json:"type"`
|
||||||
|
}{
|
||||||
|
inputText: inputText(m),
|
||||||
|
Type: m.Type(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextInputStyle is style of text in TextInput component.
|
||||||
|
type TextInputStyle uint
|
||||||
|
|
||||||
|
// Text styles
|
||||||
|
const (
|
||||||
|
TextInputShort TextInputStyle = 1
|
||||||
|
TextInputParagraph TextInputStyle = 2
|
||||||
|
)
|
@ -0,0 +1,60 @@
|
|||||||
|
// Discordgo - Discord bindings for Go
|
||||||
|
// Available at https://github.com/bwmarrin/discordgo
|
||||||
|
|
||||||
|
// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file contains high level helper functions and easy entry points for the
|
||||||
|
// entire discordgo package. These functions are being developed and are very
|
||||||
|
// experimental at this point. They will most likely change so please use the
|
||||||
|
// low level functions if that's a problem.
|
||||||
|
|
||||||
|
// Package discordgo provides Discord binding for Go
|
||||||
|
package discordgo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
|
||||||
|
const VERSION = "0.24.0"
|
||||||
|
|
||||||
|
// New creates a new Discord session with provided token.
|
||||||
|
// If the token is for a bot, it must be prefixed with "Bot "
|
||||||
|
// e.g. "Bot ..."
|
||||||
|
// Or if it is an OAuth2 token, it must be prefixed with "Bearer "
|
||||||
|
// e.g. "Bearer ..."
|
||||||
|
func New(token string) (s *Session, err error) {
|
||||||
|
|
||||||
|
// Create an empty Session interface.
|
||||||
|
s = &Session{
|
||||||
|
State: NewState(),
|
||||||
|
Ratelimiter: NewRatelimiter(),
|
||||||
|
StateEnabled: true,
|
||||||
|
Compress: true,
|
||||||
|
ShouldReconnectOnError: true,
|
||||||
|
ShardID: 0,
|
||||||
|
ShardCount: 1,
|
||||||
|
MaxRestRetries: 3,
|
||||||
|
Client: &http.Client{Timeout: (20 * time.Second)},
|
||||||
|
UserAgent: "DiscordBot (https://github.com/bwmarrin/discordgo, v" + VERSION + ")",
|
||||||
|
sequence: new(int64),
|
||||||
|
LastHeartbeatAck: time.Now().UTC(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initilize the Identify Package with defaults
|
||||||
|
// These can be modified prior to calling Open()
|
||||||
|
s.Identify.Compress = true
|
||||||
|
s.Identify.LargeThreshold = 250
|
||||||
|
s.Identify.GuildSubscriptions = true
|
||||||
|
s.Identify.Properties.OS = runtime.GOOS
|
||||||
|
s.Identify.Properties.Browser = "DiscordGo v" + VERSION
|
||||||
|
s.Identify.Intents = IntentsAllWithoutPrivileged
|
||||||
|
s.Identify.Token = token
|
||||||
|
s.Token = token
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
@ -0,0 +1,210 @@
|
|||||||
|
// Discordgo - Discord bindings for Go
|
||||||
|
// Available at https://github.com/bwmarrin/discordgo
|
||||||
|
|
||||||
|
// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file contains variables for all known Discord end points. All functions
|
||||||
|
// throughout the Discordgo package use these variables for all connections
|
||||||
|
// to Discord. These are all exported and you may modify them if needed.
|
||||||
|
|
||||||
|
package discordgo
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// APIVersion is the Discord API version used for the REST and Websocket API.
|
||||||
|
var APIVersion = "9"
|
||||||
|
|
||||||
|
// Known Discord API Endpoints.
|
||||||
|
var (
|
||||||
|
EndpointStatus = "https://status.discord.com/api/v2/"
|
||||||
|
EndpointSm = EndpointStatus + "scheduled-maintenances/"
|
||||||
|
EndpointSmActive = EndpointSm + "active.json"
|
||||||
|
EndpointSmUpcoming = EndpointSm + "upcoming.json"
|
||||||
|
|
||||||
|
EndpointDiscord = "https://discord.com/"
|
||||||
|
EndpointAPI = EndpointDiscord + "api/v" + APIVersion + "/"
|
||||||
|
EndpointGuilds = EndpointAPI + "guilds/"
|
||||||
|
EndpointChannels = EndpointAPI + "channels/"
|
||||||
|
EndpointUsers = EndpointAPI + "users/"
|
||||||
|
EndpointGateway = EndpointAPI + "gateway"
|
||||||
|
EndpointGatewayBot = EndpointGateway + "/bot"
|
||||||
|
EndpointWebhooks = EndpointAPI + "webhooks/"
|
||||||
|
EndpointStickers = EndpointAPI + "stickers/"
|
||||||
|
|
||||||
|
EndpointCDN = "https://cdn.discordapp.com/"
|
||||||
|
EndpointCDNAttachments = EndpointCDN + "attachments/"
|
||||||
|
EndpointCDNAvatars = EndpointCDN + "avatars/"
|
||||||
|
EndpointCDNIcons = EndpointCDN + "icons/"
|
||||||
|
EndpointCDNSplashes = EndpointCDN + "splashes/"
|
||||||
|
EndpointCDNChannelIcons = EndpointCDN + "channel-icons/"
|
||||||
|
EndpointCDNBanners = EndpointCDN + "banners/"
|
||||||
|
EndpointCDNGuilds = EndpointCDN + "guilds/"
|
||||||
|
|
||||||
|
EndpointVoice = EndpointAPI + "/voice/"
|
||||||
|
EndpointVoiceRegions = EndpointVoice + "regions"
|
||||||
|
|
||||||
|
// TODO: EndpointUserGuildMember
|
||||||
|
|
||||||
|
EndpointUser = func(uID string) string { return EndpointUsers + uID }
|
||||||
|
EndpointUserAvatar = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".png" }
|
||||||
|
EndpointUserAvatarAnimated = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".gif" }
|
||||||
|
EndpointDefaultUserAvatar = func(uDiscriminator string) string {
|
||||||
|
uDiscriminatorInt, _ := strconv.Atoi(uDiscriminator)
|
||||||
|
return EndpointCDN + "embed/avatars/" + strconv.Itoa(uDiscriminatorInt%5) + ".png"
|
||||||
|
}
|
||||||
|
EndpointUserBanner = func(uID, cID string) string {
|
||||||
|
return EndpointCDNBanners + uID + "/" + cID + ".png"
|
||||||
|
}
|
||||||
|
EndpointUserBannerAnimated = func(uID, cID string) string {
|
||||||
|
return EndpointCDNBanners + uID + "/" + cID + ".gif"
|
||||||
|
}
|
||||||
|
|
||||||
|
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 }
|
||||||
|
EndpointGuildThreads = func(gID string) string { return EndpointGuild(gID) + "/threads" }
|
||||||
|
EndpointGuildActiveThreads = func(gID string) string { return EndpointGuildThreads(gID) + "/active" }
|
||||||
|
EndpointGuildPreview = func(gID string) string { return EndpointGuilds + gID + "/preview" }
|
||||||
|
EndpointGuildChannels = func(gID string) string { return EndpointGuilds + gID + "/channels" }
|
||||||
|
EndpointGuildMembers = func(gID string) string { return EndpointGuilds + gID + "/members" }
|
||||||
|
EndpointGuildMember = func(gID, uID string) string { return EndpointGuilds + gID + "/members/" + uID }
|
||||||
|
EndpointGuildMemberRole = func(gID, uID, rID string) string { return EndpointGuilds + gID + "/members/" + uID + "/roles/" + rID }
|
||||||
|
EndpointGuildBans = func(gID string) string { return EndpointGuilds + gID + "/bans" }
|
||||||
|
EndpointGuildBan = func(gID, uID string) string { return EndpointGuilds + gID + "/bans/" + uID }
|
||||||
|
EndpointGuildIntegrations = func(gID string) string { return EndpointGuilds + gID + "/integrations" }
|
||||||
|
EndpointGuildIntegration = func(gID, iID string) string { return EndpointGuilds + gID + "/integrations/" + iID }
|
||||||
|
EndpointGuildRoles = func(gID string) string { return EndpointGuilds + gID + "/roles" }
|
||||||
|
EndpointGuildRole = func(gID, rID string) string { return EndpointGuilds + gID + "/roles/" + rID }
|
||||||
|
EndpointGuildInvites = func(gID string) string { return EndpointGuilds + gID + "/invites" }
|
||||||
|
EndpointGuildWidget = func(gID string) string { return EndpointGuilds + gID + "/widget" }
|
||||||
|
EndpointGuildEmbed = EndpointGuildWidget
|
||||||
|
EndpointGuildPrune = func(gID string) string { return EndpointGuilds + gID + "/prune" }
|
||||||
|
EndpointGuildIcon = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".png" }
|
||||||
|
EndpointGuildIconAnimated = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".gif" }
|
||||||
|
EndpointGuildSplash = func(gID, hash string) string { return EndpointCDNSplashes + gID + "/" + hash + ".png" }
|
||||||
|
EndpointGuildWebhooks = func(gID string) string { return EndpointGuilds + gID + "/webhooks" }
|
||||||
|
EndpointGuildAuditLogs = func(gID string) string { return EndpointGuilds + gID + "/audit-logs" }
|
||||||
|
EndpointGuildEmojis = func(gID string) string { return EndpointGuilds + gID + "/emojis" }
|
||||||
|
EndpointGuildEmoji = func(gID, eID string) string { return EndpointGuilds + gID + "/emojis/" + eID }
|
||||||
|
EndpointGuildBanner = func(gID, hash string) string { return EndpointCDNBanners + gID + "/" + hash + ".png" }
|
||||||
|
EndpointGuildStickers = func(gID string) string { return EndpointGuilds + gID + "/stickers" }
|
||||||
|
EndpointGuildSticker = func(gID, sID string) string { return EndpointGuilds + gID + "/stickers/" + sID }
|
||||||
|
EndpointGuildScheduledEvents = func(gID string) string { return EndpointGuilds + gID + "/scheduled-events" }
|
||||||
|
EndpointGuildScheduledEvent = func(gID, eID string) string { return EndpointGuilds + gID + "/scheduled-events/" + eID }
|
||||||
|
EndpointGuildScheduledEventUsers = func(gID, eID string) string { return EndpointGuildScheduledEvent(gID, eID) + "/users" }
|
||||||
|
EndpointGuildTemplate = func(tID string) string { return EndpointGuilds + "/templates/" + tID }
|
||||||
|
EndpointGuildTemplates = func(gID string) string { return EndpointGuilds + gID + "/templates" }
|
||||||
|
EndpointGuildTemplateSync = func(gID, tID string) string { return EndpointGuilds + gID + "/templates/" + tID }
|
||||||
|
EndpointGuildMemberAvatar = func(gId, uID, aID string) string {
|
||||||
|
return EndpointCDNGuilds + gId + "/users/" + uID + "/avatars/" + aID + ".png"
|
||||||
|
}
|
||||||
|
EndpointGuildMemberAvatarAnimated = func(gId, uID, aID string) string {
|
||||||
|
return EndpointCDNGuilds + gId + "/users/" + uID + "/avatars/" + aID + ".gif"
|
||||||
|
}
|
||||||
|
|
||||||
|
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" }
|
||||||
|
|
||||||
|
EndpointSticker = func(sID string) string { return EndpointStickers + sID }
|
||||||
|
EndpointNitroStickersPacks = EndpointAPI + "/sticker-packs"
|
||||||
|
|
||||||
|
EndpointChannelWebhooks = func(cID string) string { return EndpointChannel(cID) + "/webhooks" }
|
||||||
|
EndpointWebhook = func(wID string) string { return EndpointWebhooks + wID }
|
||||||
|
EndpointWebhookToken = func(wID, token string) string { return EndpointWebhooks + wID + "/" + token }
|
||||||
|
EndpointWebhookMessage = func(wID, token, messageID string) string {
|
||||||
|
return EndpointWebhookToken(wID, token) + "/messages/" + messageID
|
||||||
|
}
|
||||||
|
|
||||||
|
EndpointMessageReactionsAll = func(cID, mID string) string {
|
||||||
|
return EndpointChannelMessage(cID, mID) + "/reactions"
|
||||||
|
}
|
||||||
|
EndpointMessageReactions = func(cID, mID, eID string) string {
|
||||||
|
return EndpointChannelMessage(cID, mID) + "/reactions/" + eID
|
||||||
|
}
|
||||||
|
EndpointMessageReaction = func(cID, mID, eID, uID string) string {
|
||||||
|
return EndpointMessageReactions(cID, mID, eID) + "/" + uID
|
||||||
|
}
|
||||||
|
|
||||||
|
EndpointApplicationGlobalCommands = func(aID string) string {
|
||||||
|
return EndpointApplication(aID) + "/commands"
|
||||||
|
}
|
||||||
|
EndpointApplicationGlobalCommand = func(aID, cID string) string {
|
||||||
|
return EndpointApplicationGlobalCommands(aID) + "/" + cID
|
||||||
|
}
|
||||||
|
|
||||||
|
EndpointApplicationGuildCommands = func(aID, gID string) string {
|
||||||
|
return EndpointApplication(aID) + "/guilds/" + gID + "/commands"
|
||||||
|
}
|
||||||
|
EndpointApplicationGuildCommand = func(aID, gID, cID string) string {
|
||||||
|
return EndpointApplicationGuildCommands(aID, gID) + "/" + cID
|
||||||
|
}
|
||||||
|
EndpointApplicationCommandPermissions = func(aID, gID, cID string) string {
|
||||||
|
return EndpointApplicationGuildCommand(aID, gID, cID) + "/permissions"
|
||||||
|
}
|
||||||
|
EndpointApplicationCommandsGuildPermissions = func(aID, gID string) string {
|
||||||
|
return EndpointApplicationGuildCommands(aID, gID) + "/permissions"
|
||||||
|
}
|
||||||
|
EndpointInteraction = func(aID, iToken string) string {
|
||||||
|
return EndpointAPI + "interactions/" + aID + "/" + iToken
|
||||||
|
}
|
||||||
|
EndpointInteractionResponse = func(iID, iToken string) string {
|
||||||
|
return EndpointInteraction(iID, iToken) + "/callback"
|
||||||
|
}
|
||||||
|
EndpointInteractionResponseActions = func(aID, iToken string) string {
|
||||||
|
return EndpointWebhookMessage(aID, iToken, "@original")
|
||||||
|
}
|
||||||
|
EndpointFollowupMessage = func(aID, iToken string) string {
|
||||||
|
return EndpointWebhookToken(aID, iToken)
|
||||||
|
}
|
||||||
|
EndpointFollowupMessageActions = func(aID, iToken, mID string) string {
|
||||||
|
return EndpointWebhookMessage(aID, iToken, mID)
|
||||||
|
}
|
||||||
|
|
||||||
|
EndpointGuildCreate = EndpointAPI + "guilds"
|
||||||
|
|
||||||
|
EndpointInvite = func(iID string) string { return EndpointAPI + "invites/" + iID }
|
||||||
|
|
||||||
|
EndpointEmoji = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".png" }
|
||||||
|
EndpointEmojiAnimated = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".gif" }
|
||||||
|
|
||||||
|
EndpointApplications = EndpointAPI + "applications"
|
||||||
|
EndpointApplication = func(aID string) string { return EndpointApplications + "/" + aID }
|
||||||
|
|
||||||
|
EndpointOAuth2 = EndpointAPI + "oauth2/"
|
||||||
|
EndpointOAuth2Applications = EndpointOAuth2 + "applications"
|
||||||
|
EndpointOAuth2Application = func(aID string) string { return EndpointOAuth2Applications + "/" + aID }
|
||||||
|
EndpointOAuth2ApplicationsBot = func(aID string) string { return EndpointOAuth2Applications + "/" + aID + "/bot" }
|
||||||
|
EndpointOAuth2ApplicationAssets = func(aID string) string { return EndpointOAuth2Applications + "/" + aID + "/assets" }
|
||||||
|
|
||||||
|
// TODO: Deprecated, remove in the next release
|
||||||
|
EndpointOauth2 = EndpointOAuth2
|
||||||
|
EndpointOauth2Applications = EndpointOAuth2Applications
|
||||||
|
EndpointOauth2Application = EndpointOAuth2Application
|
||||||
|
EndpointOauth2ApplicationsBot = EndpointOAuth2ApplicationsBot
|
||||||
|
EndpointOauth2ApplicationAssets = EndpointOAuth2ApplicationAssets
|
||||||
|
)
|
@ -0,0 +1,568 @@
|
|||||||
|
package discordgo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InteractionDeadline is the time allowed to respond to an interaction.
|
||||||
|
const InteractionDeadline = time.Second * 3
|
||||||
|
|
||||||
|
// ApplicationCommandType represents the type of application command.
|
||||||
|
type ApplicationCommandType uint8
|
||||||
|
|
||||||
|
// Application command types
|
||||||
|
const (
|
||||||
|
// ChatApplicationCommand is default command type. They are slash commands (i.e. called directly from the chat).
|
||||||
|
ChatApplicationCommand ApplicationCommandType = 1
|
||||||
|
// UserApplicationCommand adds command to user context menu.
|
||||||
|
UserApplicationCommand ApplicationCommandType = 2
|
||||||
|
// MessageApplicationCommand adds command to message context menu.
|
||||||
|
MessageApplicationCommand ApplicationCommandType = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// ApplicationCommand represents an application's slash command.
|
||||||
|
type ApplicationCommand struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
ApplicationID string `json:"application_id,omitempty"`
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
Type ApplicationCommandType `json:"type,omitempty"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
DefaultPermission *bool `json:"default_permission,omitempty"`
|
||||||
|
|
||||||
|
// NOTE: Chat commands only. Otherwise it mustn't be set.
|
||||||
|
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Options []*ApplicationCommandOption `json:"options"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplicationCommandOptionType indicates the type of a slash command's option.
|
||||||
|
type ApplicationCommandOptionType uint8
|
||||||
|
|
||||||
|
// Application command option types.
|
||||||
|
const (
|
||||||
|
ApplicationCommandOptionSubCommand ApplicationCommandOptionType = 1
|
||||||
|
ApplicationCommandOptionSubCommandGroup ApplicationCommandOptionType = 2
|
||||||
|
ApplicationCommandOptionString ApplicationCommandOptionType = 3
|
||||||
|
ApplicationCommandOptionInteger ApplicationCommandOptionType = 4
|
||||||
|
ApplicationCommandOptionBoolean ApplicationCommandOptionType = 5
|
||||||
|
ApplicationCommandOptionUser ApplicationCommandOptionType = 6
|
||||||
|
ApplicationCommandOptionChannel ApplicationCommandOptionType = 7
|
||||||
|
ApplicationCommandOptionRole ApplicationCommandOptionType = 8
|
||||||
|
ApplicationCommandOptionMentionable ApplicationCommandOptionType = 9
|
||||||
|
ApplicationCommandOptionNumber ApplicationCommandOptionType = 10
|
||||||
|
ApplicationCommandOptionAttachment ApplicationCommandOptionType = 11
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t ApplicationCommandOptionType) String() string {
|
||||||
|
switch t {
|
||||||
|
case ApplicationCommandOptionSubCommand:
|
||||||
|
return "SubCommand"
|
||||||
|
case ApplicationCommandOptionSubCommandGroup:
|
||||||
|
return "SubCommandGroup"
|
||||||
|
case ApplicationCommandOptionString:
|
||||||
|
return "String"
|
||||||
|
case ApplicationCommandOptionInteger:
|
||||||
|
return "Integer"
|
||||||
|
case ApplicationCommandOptionBoolean:
|
||||||
|
return "Boolean"
|
||||||
|
case ApplicationCommandOptionUser:
|
||||||
|
return "User"
|
||||||
|
case ApplicationCommandOptionChannel:
|
||||||
|
return "Channel"
|
||||||
|
case ApplicationCommandOptionRole:
|
||||||
|
return "Role"
|
||||||
|
case ApplicationCommandOptionMentionable:
|
||||||
|
return "Mentionable"
|
||||||
|
case ApplicationCommandOptionNumber:
|
||||||
|
return "Number"
|
||||||
|
case ApplicationCommandOptionAttachment:
|
||||||
|
return "Attachment"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("ApplicationCommandOptionType(%d)", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplicationCommandOption represents an option/subcommand/subcommands group.
|
||||||
|
type ApplicationCommandOption struct {
|
||||||
|
Type ApplicationCommandOptionType `json:"type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
// NOTE: This feature was on the API, but at some point developers decided to remove it.
|
||||||
|
// So I commented it, until it will be officially on the docs.
|
||||||
|
// Default bool `json:"default"`
|
||||||
|
|
||||||
|
ChannelTypes []ChannelType `json:"channel_types"`
|
||||||
|
Required bool `json:"required"`
|
||||||
|
Options []*ApplicationCommandOption `json:"options"`
|
||||||
|
|
||||||
|
// NOTE: mutually exclusive with Choices.
|
||||||
|
Autocomplete bool `json:"autocomplete"`
|
||||||
|
Choices []*ApplicationCommandOptionChoice `json:"choices"`
|
||||||
|
// Minimal value of number/integer option.
|
||||||
|
MinValue *float64 `json:"min_value,omitempty"`
|
||||||
|
// Maximum value of number/integer option.
|
||||||
|
MaxValue float64 `json:"max_value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplicationCommandOptionChoice represents a slash command option choice.
|
||||||
|
type ApplicationCommandOptionChoice struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value interface{} `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplicationCommandPermissions represents a single user or role permission for a command.
|
||||||
|
type ApplicationCommandPermissions struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Type ApplicationCommandPermissionType `json:"type"`
|
||||||
|
Permission bool `json:"permission"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplicationCommandPermissionsList represents a list of ApplicationCommandPermissions, needed for serializing to JSON.
|
||||||
|
type ApplicationCommandPermissionsList struct {
|
||||||
|
Permissions []*ApplicationCommandPermissions `json:"permissions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GuildApplicationCommandPermissions represents all permissions for a single guild command.
|
||||||
|
type GuildApplicationCommandPermissions struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
ApplicationID string `json:"application_id"`
|
||||||
|
GuildID string `json:"guild_id"`
|
||||||
|
Permissions []*ApplicationCommandPermissions `json:"permissions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplicationCommandPermissionType indicates whether a permission is user or role based.
|
||||||
|
type ApplicationCommandPermissionType uint8
|
||||||
|
|
||||||
|
// Application command permission types.
|
||||||
|
const (
|
||||||
|
ApplicationCommandPermissionTypeRole ApplicationCommandPermissionType = 1
|
||||||
|
ApplicationCommandPermissionTypeUser ApplicationCommandPermissionType = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// InteractionType indicates the type of an interaction event.
|
||||||
|
type InteractionType uint8
|
||||||
|
|
||||||
|
// Interaction types
|
||||||
|
const (
|
||||||
|
InteractionPing InteractionType = 1
|
||||||
|
InteractionApplicationCommand InteractionType = 2
|
||||||
|
InteractionMessageComponent InteractionType = 3
|
||||||
|
InteractionApplicationCommandAutocomplete InteractionType = 4
|
||||||
|
InteractionModalSubmit InteractionType = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t InteractionType) String() string {
|
||||||
|
switch t {
|
||||||
|
case InteractionPing:
|
||||||
|
return "Ping"
|
||||||
|
case InteractionApplicationCommand:
|
||||||
|
return "ApplicationCommand"
|
||||||
|
case InteractionMessageComponent:
|
||||||
|
return "MessageComponent"
|
||||||
|
case InteractionModalSubmit:
|
||||||
|
return "ModalSubmit"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("InteractionType(%d)", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interaction represents data of an interaction.
|
||||||
|
type Interaction struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Type InteractionType `json:"type"`
|
||||||
|
Data InteractionData `json:"data"`
|
||||||
|
GuildID string `json:"guild_id"`
|
||||||
|
ChannelID string `json:"channel_id"`
|
||||||
|
|
||||||
|
// The message on which interaction was used.
|
||||||
|
// NOTE: this field is only filled when a button click triggered the interaction. Otherwise it will be nil.
|
||||||
|
Message *Message `json:"message"`
|
||||||
|
|
||||||
|
// The member who invoked this interaction.
|
||||||
|
// NOTE: this field is only filled when the slash command was invoked in a guild;
|
||||||
|
// if it was invoked in a DM, the `User` field will be filled instead.
|
||||||
|
// Make sure to check for `nil` before using this field.
|
||||||
|
Member *Member `json:"member"`
|
||||||
|
// The user who invoked this interaction.
|
||||||
|
// NOTE: this field is only filled when the slash command was invoked in a DM;
|
||||||
|
// if it was invoked in a guild, the `Member` field will be filled instead.
|
||||||
|
// Make sure to check for `nil` before using this field.
|
||||||
|
User *User `json:"user"`
|
||||||
|
|
||||||
|
// The user's discord client locale.
|
||||||
|
Locale Locale `json:"locale"`
|
||||||
|
// The guild's locale. This defaults to EnglishUS
|
||||||
|
// NOTE: this field is only filled when the interaction was invoked in a guild.
|
||||||
|
GuildLocale *Locale `json:"guild_locale"`
|
||||||
|
|
||||||
|
Token string `json:"token"`
|
||||||
|
Version int `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type interaction Interaction
|
||||||
|
|
||||||
|
type rawInteraction struct {
|
||||||
|
interaction
|
||||||
|
Data json.RawMessage `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON is a method for unmarshalling JSON object to Interaction.
|
||||||
|
func (i *Interaction) UnmarshalJSON(raw []byte) error {
|
||||||
|
var tmp rawInteraction
|
||||||
|
err := json.Unmarshal(raw, &tmp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*i = Interaction(tmp.interaction)
|
||||||
|
|
||||||
|
switch tmp.Type {
|
||||||
|
case InteractionApplicationCommand, InteractionApplicationCommandAutocomplete:
|
||||||
|
v := ApplicationCommandInteractionData{}
|
||||||
|
err = json.Unmarshal(tmp.Data, &v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Data = v
|
||||||
|
case InteractionMessageComponent:
|
||||||
|
v := MessageComponentInteractionData{}
|
||||||
|
err = json.Unmarshal(tmp.Data, &v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Data = v
|
||||||
|
case InteractionModalSubmit:
|
||||||
|
v := ModalSubmitInteractionData{}
|
||||||
|
err = json.Unmarshal(tmp.Data, &v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.Data = v
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageComponentData is helper function to assert the inner InteractionData to MessageComponentInteractionData.
|
||||||
|
// Make sure to check that the Type of the interaction is InteractionMessageComponent before calling.
|
||||||
|
func (i Interaction) MessageComponentData() (data MessageComponentInteractionData) {
|
||||||
|
if i.Type != InteractionMessageComponent {
|
||||||
|
panic("MessageComponentData called on interaction of type " + i.Type.String())
|
||||||
|
}
|
||||||
|
return i.Data.(MessageComponentInteractionData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplicationCommandData is helper function to assert the inner InteractionData to ApplicationCommandInteractionData.
|
||||||
|
// Make sure to check that the Type of the interaction is InteractionApplicationCommand before calling.
|
||||||
|
func (i Interaction) ApplicationCommandData() (data ApplicationCommandInteractionData) {
|
||||||
|
if i.Type != InteractionApplicationCommand && i.Type != InteractionApplicationCommandAutocomplete {
|
||||||
|
panic("ApplicationCommandData called on interaction of type " + i.Type.String())
|
||||||
|
}
|
||||||
|
return i.Data.(ApplicationCommandInteractionData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModalSubmitData is helper function to assert the inner InteractionData to ModalSubmitInteractionData.
|
||||||
|
// Make sure to check that the Type of the interaction is InteractionModalSubmit before calling.
|
||||||
|
func (i Interaction) ModalSubmitData() (data ModalSubmitInteractionData) {
|
||||||
|
if i.Type != InteractionModalSubmit {
|
||||||
|
panic("ModalSubmitData called on interaction of type " + i.Type.String())
|
||||||
|
}
|
||||||
|
return i.Data.(ModalSubmitInteractionData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InteractionData is a common interface for all types of interaction data.
|
||||||
|
type InteractionData interface {
|
||||||
|
Type() InteractionType
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplicationCommandInteractionData contains the data of application command interaction.
|
||||||
|
type ApplicationCommandInteractionData struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Resolved *ApplicationCommandInteractionDataResolved `json:"resolved"`
|
||||||
|
|
||||||
|
// Slash command options
|
||||||
|
Options []*ApplicationCommandInteractionDataOption `json:"options"`
|
||||||
|
// Target (user/message) id on which context menu command was called.
|
||||||
|
// The details are stored in Resolved according to command type.
|
||||||
|
TargetID string `json:"target_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplicationCommandInteractionDataResolved contains resolved data of command execution.
|
||||||
|
// Partial Member objects are missing user, deaf and mute fields.
|
||||||
|
// Partial Channel objects only have id, name, type and permissions fields.
|
||||||
|
type ApplicationCommandInteractionDataResolved struct {
|
||||||
|
Users map[string]*User `json:"users"`
|
||||||
|
Members map[string]*Member `json:"members"`
|
||||||
|
Roles map[string]*Role `json:"roles"`
|
||||||
|
Channels map[string]*Channel `json:"channels"`
|
||||||
|
Messages map[string]*Message `json:"messages"`
|
||||||
|
Attachments map[string]*MessageAttachment `json:"attachments"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns the type of interaction data.
|
||||||
|
func (ApplicationCommandInteractionData) Type() InteractionType {
|
||||||
|
return InteractionApplicationCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
// MessageComponentInteractionData contains the data of message component interaction.
|
||||||
|
type MessageComponentInteractionData struct {
|
||||||
|
CustomID string `json:"custom_id"`
|
||||||
|
ComponentType ComponentType `json:"component_type"`
|
||||||
|
|
||||||
|
// NOTE: Only filled when ComponentType is SelectMenuComponent (3). Otherwise is nil.
|
||||||
|
Values []string `json:"values"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns the type of interaction data.
|
||||||
|
func (MessageComponentInteractionData) Type() InteractionType {
|
||||||
|
return InteractionMessageComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModalSubmitInteractionData contains the data of modal submit interaction.
|
||||||
|
type ModalSubmitInteractionData struct {
|
||||||
|
CustomID string `json:"custom_id"`
|
||||||
|
Components []MessageComponent `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns the type of interaction data.
|
||||||
|
func (ModalSubmitInteractionData) Type() InteractionType {
|
||||||
|
return InteractionModalSubmit
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON is a helper function to correctly unmarshal Components.
|
||||||
|
func (d *ModalSubmitInteractionData) UnmarshalJSON(data []byte) error {
|
||||||
|
type modalSubmitInteractionData ModalSubmitInteractionData
|
||||||
|
var v struct {
|
||||||
|
modalSubmitInteractionData
|
||||||
|
RawComponents []unmarshalableMessageComponent `json:"components"`
|
||||||
|
}
|
||||||
|
err := json.Unmarshal(data, &v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*d = ModalSubmitInteractionData(v.modalSubmitInteractionData)
|
||||||
|
d.Components = make([]MessageComponent, len(v.RawComponents))
|
||||||
|
for i, v := range v.RawComponents {
|
||||||
|
d.Components[i] = v.MessageComponent
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplicationCommandInteractionDataOption represents an option of a slash command.
|
||||||
|
type ApplicationCommandInteractionDataOption struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type ApplicationCommandOptionType `json:"type"`
|
||||||
|
// NOTE: Contains the value specified by Type.
|
||||||
|
Value interface{} `json:"value,omitempty"`
|
||||||
|
Options []*ApplicationCommandInteractionDataOption `json:"options,omitempty"`
|
||||||
|
|
||||||
|
// NOTE: autocomplete interaction only.
|
||||||
|
Focused bool `json:"focused,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntValue is a utility function for casting option value to integer
|
||||||
|
func (o ApplicationCommandInteractionDataOption) IntValue() int64 {
|
||||||
|
if o.Type != ApplicationCommandOptionInteger {
|
||||||
|
panic("IntValue called on data option of type " + o.Type.String())
|
||||||
|
}
|
||||||
|
return int64(o.Value.(float64))
|
||||||
|
}
|
||||||
|
|
||||||
|
// UintValue is a utility function for casting option value to unsigned integer
|
||||||
|
func (o ApplicationCommandInteractionDataOption) UintValue() uint64 {
|
||||||
|
if o.Type != ApplicationCommandOptionInteger {
|
||||||
|
panic("UintValue called on data option of type " + o.Type.String())
|
||||||
|
}
|
||||||
|
return uint64(o.Value.(float64))
|
||||||
|
}
|
||||||
|
|
||||||
|
// FloatValue is a utility function for casting option value to float
|
||||||
|
func (o ApplicationCommandInteractionDataOption) FloatValue() float64 {
|
||||||
|
if o.Type != ApplicationCommandOptionNumber {
|
||||||
|
panic("FloatValue called on data option of type " + o.Type.String())
|
||||||
|
}
|
||||||
|
return o.Value.(float64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringValue is a utility function for casting option value to string
|
||||||
|
func (o ApplicationCommandInteractionDataOption) StringValue() string {
|
||||||
|
if o.Type != ApplicationCommandOptionString {
|
||||||
|
panic("StringValue called on data option of type " + o.Type.String())
|
||||||
|
}
|
||||||
|
return o.Value.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoolValue is a utility function for casting option value to bool
|
||||||
|
func (o ApplicationCommandInteractionDataOption) BoolValue() bool {
|
||||||
|
if o.Type != ApplicationCommandOptionBoolean {
|
||||||
|
panic("BoolValue called on data option of type " + o.Type.String())
|
||||||
|
}
|
||||||
|
return o.Value.(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChannelValue is a utility function for casting option value to channel object.
|
||||||
|
// s : Session object, if not nil, function additionally fetches all channel's data
|
||||||
|
func (o ApplicationCommandInteractionDataOption) ChannelValue(s *Session) *Channel {
|
||||||
|
if o.Type != ApplicationCommandOptionChannel {
|
||||||
|
panic("ChannelValue called on data option of type " + o.Type.String())
|
||||||
|
}
|
||||||
|
chanID := o.Value.(string)
|
||||||
|
|
||||||
|
if s == nil {
|
||||||
|
return &Channel{ID: chanID}
|
||||||
|
}
|
||||||
|
|
||||||
|
ch, err := s.State.Channel(chanID)
|
||||||
|
if err != nil {
|
||||||
|
ch, err = s.Channel(chanID)
|
||||||
|
if err != nil {
|
||||||
|
return &Channel{ID: chanID}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoleValue is a utility function for casting option value to role object.
|
||||||
|
// s : Session object, if not nil, function additionally fetches all role's data
|
||||||
|
func (o ApplicationCommandInteractionDataOption) RoleValue(s *Session, gID string) *Role {
|
||||||
|
if o.Type != ApplicationCommandOptionRole && o.Type != ApplicationCommandOptionMentionable {
|
||||||
|
panic("RoleValue called on data option of type " + o.Type.String())
|
||||||
|
}
|
||||||
|
roleID := o.Value.(string)
|
||||||
|
|
||||||
|
if s == nil || gID == "" {
|
||||||
|
return &Role{ID: roleID}
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := s.State.Role(roleID, gID)
|
||||||
|
if err != nil {
|
||||||
|
roles, err := s.GuildRoles(gID)
|
||||||
|
if err == nil {
|
||||||
|
for _, r = range roles {
|
||||||
|
if r.ID == roleID {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Role{ID: roleID}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserValue is a utility function for casting option value to user object.
|
||||||
|
// s : Session object, if not nil, function additionally fetches all user's data
|
||||||
|
func (o ApplicationCommandInteractionDataOption) UserValue(s *Session) *User {
|
||||||
|
if o.Type != ApplicationCommandOptionUser && o.Type != ApplicationCommandOptionMentionable {
|
||||||
|
panic("UserValue called on data option of type " + o.Type.String())
|
||||||
|
}
|
||||||
|
userID := o.Value.(string)
|
||||||
|
|
||||||
|
if s == nil {
|
||||||
|
return &User{ID: userID}
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := s.User(userID)
|
||||||
|
if err != nil {
|
||||||
|
return &User{ID: userID}
|
||||||
|
}
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// InteractionResponseType is type of interaction response.
|
||||||
|
type InteractionResponseType uint8
|
||||||
|
|
||||||
|
// Interaction response types.
|
||||||
|
const (
|
||||||
|
// InteractionResponsePong is for ACK ping event.
|
||||||
|
InteractionResponsePong InteractionResponseType = 1
|
||||||
|
// InteractionResponseChannelMessageWithSource is for responding with a message, showing the user's input.
|
||||||
|
InteractionResponseChannelMessageWithSource InteractionResponseType = 4
|
||||||
|
// InteractionResponseDeferredChannelMessageWithSource acknowledges that the event was received, and that a follow-up will come later.
|
||||||
|
InteractionResponseDeferredChannelMessageWithSource InteractionResponseType = 5
|
||||||
|
// InteractionResponseDeferredMessageUpdate acknowledges that the message component interaction event was received, and message will be updated later.
|
||||||
|
InteractionResponseDeferredMessageUpdate InteractionResponseType = 6
|
||||||
|
// InteractionResponseUpdateMessage is for updating the message to which message component was attached.
|
||||||
|
InteractionResponseUpdateMessage InteractionResponseType = 7
|
||||||
|
// InteractionApplicationCommandAutocompleteResult shows autocompletion results. Autocomplete interaction only.
|
||||||
|
InteractionApplicationCommandAutocompleteResult InteractionResponseType = 8
|
||||||
|
// InteractionResponseModal is for responding to an interaction with a modal window.
|
||||||
|
InteractionResponseModal InteractionResponseType = 9
|
||||||
|
)
|
||||||
|
|
||||||
|
// InteractionResponse represents a response for an interaction event.
|
||||||
|
type InteractionResponse struct {
|
||||||
|
Type InteractionResponseType `json:"type,omitempty"`
|
||||||
|
Data *InteractionResponseData `json:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InteractionResponseData is response data for an interaction.
|
||||||
|
type InteractionResponseData struct {
|
||||||
|
TTS bool `json:"tts"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
Components []MessageComponent `json:"components"`
|
||||||
|
Embeds []*MessageEmbed `json:"embeds,omitempty"`
|
||||||
|
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
||||||
|
Flags uint64 `json:"flags,omitempty"`
|
||||||
|
Files []*File `json:"-"`
|
||||||
|
|
||||||
|
// NOTE: autocomplete interaction only.
|
||||||
|
Choices []*ApplicationCommandOptionChoice `json:"choices,omitempty"`
|
||||||
|
|
||||||
|
// NOTE: modal interaction only.
|
||||||
|
|
||||||
|
CustomID string `json:"custom_id,omitempty"`
|
||||||
|
Title string `json:"title,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyInteraction implements message verification of the discord interactions api
|
||||||
|
// signing algorithm, as documented here:
|
||||||
|
// https://discord.com/developers/docs/interactions/receiving-and-responding#security-and-authorization
|
||||||
|
func VerifyInteraction(r *http.Request, key ed25519.PublicKey) bool {
|
||||||
|
var msg bytes.Buffer
|
||||||
|
|
||||||
|
signature := r.Header.Get("X-Signature-Ed25519")
|
||||||
|
if signature == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := hex.DecodeString(signature)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sig) != ed25519.SignatureSize {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp := r.Header.Get("X-Signature-Timestamp")
|
||||||
|
if timestamp == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.WriteString(timestamp)
|
||||||
|
|
||||||
|
defer r.Body.Close()
|
||||||
|
var body bytes.Buffer
|
||||||
|
|
||||||
|
// at the end of the function, copy the original body back into the request
|
||||||
|
defer func() {
|
||||||
|
r.Body = ioutil.NopCloser(&body)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// copy body into buffers
|
||||||
|
_, err = io.Copy(&msg, io.TeeReader(r.Body, &body))
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return ed25519.Verify(key, msg.Bytes(), sig)
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
package discordgo
|
||||||
|
|
||||||
|
// Locale represents the accepted languages for Discord.
|
||||||
|
// https://discord.com/developers/docs/reference#locales
|
||||||
|
type Locale string
|
||||||
|
|
||||||
|
// String returns the human-readable string of the locale
|
||||||
|
func (l Locale) String() string {
|
||||||
|
if name, ok := Locales[l]; ok {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return Unknown.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// All defined locales in Discord
|
||||||
|
const (
|
||||||
|
EnglishUS Locale = "en-US"
|
||||||
|
EnglishGB Locale = "en-GB"
|
||||||
|
Bulgarian Locale = "bg"
|
||||||
|
ChineseCN Locale = "zh-CN"
|
||||||
|
ChineseTW Locale = "zh-TW"
|
||||||
|
Croatian Locale = "hr"
|
||||||
|
Czech Locale = "cs"
|
||||||
|
Danish Locale = "da"
|
||||||
|
Dutch Locale = "nl"
|
||||||
|
Finnish Locale = "fi"
|
||||||
|
French Locale = "fr"
|
||||||
|
German Locale = "de"
|
||||||
|
Greek Locale = "el"
|
||||||
|
Hindi Locale = "hi"
|
||||||
|
Hungarian Locale = "hu"
|
||||||
|
Italian Locale = "it"
|
||||||
|
Japanese Locale = "ja"
|
||||||
|
Korean Locale = "ko"
|
||||||
|
Lithuanian Locale = "lt"
|
||||||
|
Norwegian Locale = "no"
|
||||||
|
Polish Locale = "pl"
|
||||||
|
PortugueseBR Locale = "pt-BR"
|
||||||
|
Romanian Locale = "ro"
|
||||||
|
Russian Locale = "ru"
|
||||||
|
SpanishES Locale = "es-ES"
|
||||||
|
Swedish Locale = "sv-SE"
|
||||||
|
Thai Locale = "th"
|
||||||
|
Turkish Locale = "tr"
|
||||||
|
Ukrainian Locale = "uk"
|
||||||
|
Vietnamese Locale = "vi"
|
||||||
|
Unknown Locale = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
// Locales is a map of all the languages codes to their names.
|
||||||
|
var Locales = map[Locale]string{
|
||||||
|
EnglishUS: "English (United States)",
|
||||||
|
EnglishGB: "English (Great Britain)",
|
||||||
|
Bulgarian: "Bulgarian",
|
||||||
|
ChineseCN: "Chinese (China)",
|
||||||
|
ChineseTW: "Chinese (Taiwan)",
|
||||||
|
Croatian: "Croatian",
|
||||||
|
Czech: "Czech",
|
||||||
|
Danish: "Danish",
|
||||||
|
Dutch: "Dutch",
|
||||||
|
Finnish: "Finnish",
|
||||||
|
French: "French",
|
||||||
|
German: "German",
|
||||||
|
Greek: "Greek",
|
||||||
|
Hindi: "Hindi",
|
||||||
|
Hungarian: "Hungarian",
|
||||||
|
Italian: "Italian",
|
||||||
|
Japanese: "Japanese",
|
||||||
|
Korean: "Korean",
|
||||||
|
Lithuanian: "Lithuanian",
|
||||||
|
Norwegian: "Norwegian",
|
||||||
|
Polish: "Polish",
|
||||||
|
PortugueseBR: "Portuguese (Brazil)",
|
||||||
|
Romanian: "Romanian",
|
||||||
|
Russian: "Russian",
|
||||||
|
SpanishES: "Spanish (Spain)",
|
||||||
|
Swedish: "Swedish",
|
||||||
|
Thai: "Thai",
|
||||||
|
Turkish: "Turkish",
|
||||||
|
Ukrainian: "Ukrainian",
|
||||||
|
Vietnamese: "Vietnamese",
|
||||||
|
Unknown: "unknown",
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,110 @@
|
|||||||
|
package discordgo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/textproto"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SnowflakeTimestamp returns the creation time of a Snowflake ID relative to the creation of Discord.
|
||||||
|
func SnowflakeTimestamp(ID string) (t time.Time, err error) {
|
||||||
|
i, err := strconv.ParseInt(ID, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
timestamp := (i >> 22) + 1420070400000
|
||||||
|
t = time.Unix(0, timestamp*1000000)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultipartBodyWithJSON returns the contentType and body for a discord request
|
||||||
|
// data : The object to encode for payload_json in the multipart request
|
||||||
|
// files : Files to include in the request
|
||||||
|
func MultipartBodyWithJSON(data interface{}, files []*File) (requestContentType string, requestBody []byte, err error) {
|
||||||
|
body := &bytes.Buffer{}
|
||||||
|
bodywriter := multipart.NewWriter(body)
|
||||||
|
|
||||||
|
payload, err := json.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var p io.Writer
|
||||||
|
|
||||||
|
h := make(textproto.MIMEHeader)
|
||||||
|
h.Set("Content-Disposition", `form-data; name="payload_json"`)
|
||||||
|
h.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
p, err = bodywriter.CreatePart(h)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = p.Write(payload); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, file := range files {
|
||||||
|
h := make(textproto.MIMEHeader)
|
||||||
|
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file%d"; filename="%s"`, i, quoteEscaper.Replace(file.Name)))
|
||||||
|
contentType := file.ContentType
|
||||||
|
if contentType == "" {
|
||||||
|
contentType = "application/octet-stream"
|
||||||
|
}
|
||||||
|
h.Set("Content-Type", contentType)
|
||||||
|
|
||||||
|
p, err = bodywriter.CreatePart(h)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = io.Copy(p, file.Reader); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bodywriter.Close()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return bodywriter.FormDataContentType(), body.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func avatarURL(avatarHash, defaultAvatarURL, staticAvatarURL, animatedAvatarURL, size string) string {
|
||||||
|
var URL string
|
||||||
|
if avatarHash == "" {
|
||||||
|
URL = defaultAvatarURL
|
||||||
|
} else if strings.HasPrefix(avatarHash, "a_") {
|
||||||
|
URL = animatedAvatarURL
|
||||||
|
} else {
|
||||||
|
URL = staticAvatarURL
|
||||||
|
}
|
||||||
|
|
||||||
|
if size != "" {
|
||||||
|
return URL + "?size=" + size
|
||||||
|
}
|
||||||
|
return URL
|
||||||
|
}
|
||||||
|
|
||||||
|
func bannerURL(bannerHash, staticBannerURL, animatedBannerURL, size string) string {
|
||||||
|
var URL string
|
||||||
|
if bannerHash == "" {
|
||||||
|
return ""
|
||||||
|
} else if strings.HasPrefix(bannerHash, "a_") {
|
||||||
|
URL = animatedBannerURL
|
||||||
|
} else {
|
||||||
|
URL = staticBannerURL
|
||||||
|
}
|
||||||
|
|
||||||
|
if size != "" {
|
||||||
|
return URL + "?size=" + size
|
||||||
|
}
|
||||||
|
return URL
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package discordgo
|
||||||
|
|
||||||
|
// Webhook stores the data for a webhook.
|
||||||
|
type Webhook struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Type WebhookType `json:"type"`
|
||||||
|
GuildID string `json:"guild_id"`
|
||||||
|
ChannelID string `json:"channel_id"`
|
||||||
|
User *User `json:"user"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Avatar string `json:"avatar"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
|
||||||
|
// ApplicationID is the bot/OAuth2 application that created this webhook
|
||||||
|
ApplicationID string `json:"application_id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WebhookType is the type of Webhook (see WebhookType* consts) in the Webhook struct
|
||||||
|
// https://discord.com/developers/docs/resources/webhook#webhook-object-webhook-types
|
||||||
|
type WebhookType int
|
||||||
|
|
||||||
|
// Valid WebhookType values
|
||||||
|
const (
|
||||||
|
WebhookTypeIncoming WebhookType = 1
|
||||||
|
WebhookTypeChannelFollower WebhookType = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// WebhookParams is a struct for webhook params, used in the WebhookExecute command.
|
||||||
|
type WebhookParams struct {
|
||||||
|
Content string `json:"content,omitempty"`
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
AvatarURL string `json:"avatar_url,omitempty"`
|
||||||
|
TTS bool `json:"tts,omitempty"`
|
||||||
|
Files []*File `json:"-"`
|
||||||
|
Components []MessageComponent `json:"components"`
|
||||||
|
Embeds []*MessageEmbed `json:"embeds,omitempty"`
|
||||||
|
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
||||||
|
// NOTE: Works only for followup messages.
|
||||||
|
Flags uint64 `json:"flags,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WebhookEdit stores data for editing of a webhook message.
|
||||||
|
type WebhookEdit struct {
|
||||||
|
Content string `json:"content,omitempty"`
|
||||||
|
Components []MessageComponent `json:"components"`
|
||||||
|
Embeds []*MessageEmbed `json:"embeds,omitempty"`
|
||||||
|
Files []*File `json:"-"`
|
||||||
|
AllowedMentions *MessageAllowedMentions `json:"allowed_mentions,omitempty"`
|
||||||
|
}
|
@ -1,2 +0,0 @@
|
|||||||
# IDE-specific metadata
|
|
||||||
.idea/
|
|
@ -1,161 +0,0 @@
|
|||||||
// Discordgo - Discord bindings for Go
|
|
||||||
// Available at https://github.com/matterbridge/discordgo
|
|
||||||
|
|
||||||
// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// This file contains high level helper functions and easy entry points for the
|
|
||||||
// entire discordgo package. These functions are being developed and are very
|
|
||||||
// experimental at this point. They will most likely change so please use the
|
|
||||||
// low level functions if that's a problem.
|
|
||||||
|
|
||||||
// Package discordgo provides Discord binding for Go
|
|
||||||
package discordgo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"runtime"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
|
|
||||||
const VERSION = "0.23.0"
|
|
||||||
|
|
||||||
// ErrMFA will be risen by New when the user has 2FA.
|
|
||||||
var ErrMFA = errors.New("account has 2FA enabled")
|
|
||||||
|
|
||||||
// New creates a new Discord session and will automate some startup
|
|
||||||
// tasks if given enough information to do so. Currently you can pass zero
|
|
||||||
// arguments and it will return an empty Discord session.
|
|
||||||
// There are 3 ways to call New:
|
|
||||||
// With a single auth token - All requests will use the token blindly
|
|
||||||
// (just tossing it into the HTTP Authorization header);
|
|
||||||
// no verification of the token will be done and requests may fail.
|
|
||||||
// IF THE TOKEN IS FOR A BOT, IT MUST BE PREFIXED WITH `BOT `
|
|
||||||
// eg: `"Bot <token>"`
|
|
||||||
// IF IT IS AN OAUTH2 ACCESS TOKEN, IT MUST BE PREFIXED WITH `Bearer `
|
|
||||||
// eg: `"Bearer <token>"`
|
|
||||||
// With an email and password - Discord will sign in with the provided
|
|
||||||
// credentials.
|
|
||||||
// With an email, password and auth token - Discord will verify the auth
|
|
||||||
// token, if it is invalid it will sign in with the provided
|
|
||||||
// credentials. This is the Discord recommended way to sign in.
|
|
||||||
//
|
|
||||||
// NOTE: While email/pass authentication is supported by DiscordGo it is
|
|
||||||
// HIGHLY DISCOURAGED by Discord. Please only use email/pass to obtain a token
|
|
||||||
// and then use that authentication token for all future connections.
|
|
||||||
// Also, doing any form of automation with a user (non Bot) account may result
|
|
||||||
// in that account being permanently banned from Discord.
|
|
||||||
func New(args ...interface{}) (s *Session, err error) {
|
|
||||||
|
|
||||||
// Create an empty Session interface.
|
|
||||||
s = &Session{
|
|
||||||
State: NewState(),
|
|
||||||
Ratelimiter: NewRatelimiter(),
|
|
||||||
StateEnabled: true,
|
|
||||||
Compress: true,
|
|
||||||
ShouldReconnectOnError: true,
|
|
||||||
ShardID: 0,
|
|
||||||
ShardCount: 1,
|
|
||||||
MaxRestRetries: 3,
|
|
||||||
Client: &http.Client{Timeout: (20 * time.Second)},
|
|
||||||
UserAgent: "DiscordBot (https://github.com/matterbridge/discordgo, v" + VERSION + ")",
|
|
||||||
sequence: new(int64),
|
|
||||||
LastHeartbeatAck: time.Now().UTC(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initilize the Identify Package with defaults
|
|
||||||
// These can be modified prior to calling Open()
|
|
||||||
s.Identify.Compress = true
|
|
||||||
s.Identify.LargeThreshold = 250
|
|
||||||
s.Identify.GuildSubscriptions = true
|
|
||||||
s.Identify.Properties.OS = runtime.GOOS
|
|
||||||
s.Identify.Properties.Browser = "DiscordGo v" + VERSION
|
|
||||||
s.Identify.Intents = MakeIntent(IntentsAllWithoutPrivileged)
|
|
||||||
|
|
||||||
// If no arguments are passed return the empty Session interface.
|
|
||||||
if args == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variables used below when parsing func arguments
|
|
||||||
var auth, pass string
|
|
||||||
|
|
||||||
// Parse passed arguments
|
|
||||||
for _, arg := range args {
|
|
||||||
|
|
||||||
switch v := arg.(type) {
|
|
||||||
|
|
||||||
case []string:
|
|
||||||
if len(v) > 3 {
|
|
||||||
err = fmt.Errorf("too many string parameters provided")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// First string is either token or username
|
|
||||||
if len(v) > 0 {
|
|
||||||
auth = v[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// If second string exists, it must be a password.
|
|
||||||
if len(v) > 1 {
|
|
||||||
pass = v[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// If third string exists, it must be an auth token.
|
|
||||||
if len(v) > 2 {
|
|
||||||
s.Identify.Token = v[2]
|
|
||||||
s.Token = v[2] // TODO: Remove, Deprecated - Kept for backwards compatibility.
|
|
||||||
}
|
|
||||||
|
|
||||||
case string:
|
|
||||||
// First string must be either auth token or username.
|
|
||||||
// Second string must be a password.
|
|
||||||
// Only 2 input strings are supported.
|
|
||||||
|
|
||||||
if auth == "" {
|
|
||||||
auth = v
|
|
||||||
} else if pass == "" {
|
|
||||||
pass = v
|
|
||||||
} else if s.Token == "" {
|
|
||||||
s.Identify.Token = v
|
|
||||||
s.Token = v // TODO: Remove, Deprecated - Kept for backwards compatibility.
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("too many string parameters provided")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// case Config:
|
|
||||||
// TODO: Parse configuration struct
|
|
||||||
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf("unsupported parameter type provided")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If only one string was provided, assume it is an auth token.
|
|
||||||
// Otherwise get auth token from Discord, if a token was specified
|
|
||||||
// Discord will verify it for free, or log the user in if it is
|
|
||||||
// invalid.
|
|
||||||
if pass == "" {
|
|
||||||
s.Identify.Token = auth
|
|
||||||
s.Token = auth // TODO: Remove, Deprecated - Kept for backwards compatibility.
|
|
||||||
} else {
|
|
||||||
err = s.Login(auth, pass)
|
|
||||||
// TODO: Remove last s.Token part, Deprecated - Kept for backwards compatibility.
|
|
||||||
if err != nil || s.Identify.Token == "" || s.Token == "" {
|
|
||||||
if s.MFA {
|
|
||||||
err = ErrMFA
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("Unable to fetch discord authentication token. %v", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,153 +0,0 @@
|
|||||||
// Discordgo - Discord bindings for Go
|
|
||||||
// Available at https://github.com/bwmarrin/discordgo
|
|
||||||
|
|
||||||
// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// This file contains variables for all known Discord end points. All functions
|
|
||||||
// throughout the Discordgo package use these variables for all connections
|
|
||||||
// to Discord. These are all exported and you may modify them if needed.
|
|
||||||
|
|
||||||
package discordgo
|
|
||||||
|
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
// APIVersion is the Discord API version used for the REST and Websocket API.
|
|
||||||
var APIVersion = "8"
|
|
||||||
|
|
||||||
// Known Discord API Endpoints.
|
|
||||||
var (
|
|
||||||
EndpointStatus = "https://status.discord.com/api/v2/"
|
|
||||||
EndpointSm = EndpointStatus + "scheduled-maintenances/"
|
|
||||||
EndpointSmActive = EndpointSm + "active.json"
|
|
||||||
EndpointSmUpcoming = EndpointSm + "upcoming.json"
|
|
||||||
|
|
||||||
EndpointDiscord = "https://discord.com/"
|
|
||||||
EndpointAPI = EndpointDiscord + "api/v" + APIVersion + "/"
|
|
||||||
EndpointGuilds = EndpointAPI + "guilds/"
|
|
||||||
EndpointChannels = EndpointAPI + "channels/"
|
|
||||||
EndpointUsers = EndpointAPI + "users/"
|
|
||||||
EndpointGateway = EndpointAPI + "gateway"
|
|
||||||
EndpointGatewayBot = EndpointGateway + "/bot"
|
|
||||||
EndpointWebhooks = EndpointAPI + "webhooks/"
|
|
||||||
|
|
||||||
EndpointCDN = "https://cdn.discordapp.com/"
|
|
||||||
EndpointCDNAttachments = EndpointCDN + "attachments/"
|
|
||||||
EndpointCDNAvatars = EndpointCDN + "avatars/"
|
|
||||||
EndpointCDNIcons = EndpointCDN + "icons/"
|
|
||||||
EndpointCDNSplashes = EndpointCDN + "splashes/"
|
|
||||||
EndpointCDNChannelIcons = EndpointCDN + "channel-icons/"
|
|
||||||
EndpointCDNBanners = EndpointCDN + "banners/"
|
|
||||||
|
|
||||||
EndpointAuth = EndpointAPI + "auth/"
|
|
||||||
EndpointLogin = EndpointAuth + "login"
|
|
||||||
EndpointLogout = EndpointAuth + "logout"
|
|
||||||
EndpointVerify = EndpointAuth + "verify"
|
|
||||||
EndpointVerifyResend = EndpointAuth + "verify/resend"
|
|
||||||
EndpointForgotPassword = EndpointAuth + "forgot"
|
|
||||||
EndpointResetPassword = EndpointAuth + "reset"
|
|
||||||
EndpointRegister = EndpointAuth + "register"
|
|
||||||
|
|
||||||
EndpointVoice = EndpointAPI + "/voice/"
|
|
||||||
EndpointVoiceRegions = EndpointVoice + "regions"
|
|
||||||
EndpointVoiceIce = EndpointVoice + "ice"
|
|
||||||
|
|
||||||
EndpointTutorial = EndpointAPI + "tutorial/"
|
|
||||||
EndpointTutorialIndicators = EndpointTutorial + "indicators"
|
|
||||||
|
|
||||||
EndpointTrack = EndpointAPI + "track"
|
|
||||||
EndpointSso = EndpointAPI + "sso"
|
|
||||||
EndpointReport = EndpointAPI + "report"
|
|
||||||
EndpointIntegrations = EndpointAPI + "integrations"
|
|
||||||
|
|
||||||
EndpointUser = func(uID string) string { return EndpointUsers + uID }
|
|
||||||
EndpointUserAvatar = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".png" }
|
|
||||||
EndpointUserAvatarAnimated = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".gif" }
|
|
||||||
EndpointDefaultUserAvatar = func(uDiscriminator string) string {
|
|
||||||
uDiscriminatorInt, _ := strconv.Atoi(uDiscriminator)
|
|
||||||
return EndpointCDN + "embed/avatars/" + strconv.Itoa(uDiscriminatorInt%5) + ".png"
|
|
||||||
}
|
|
||||||
EndpointUserSettings = func(uID string) string { return EndpointUsers + uID + "/settings" }
|
|
||||||
EndpointUserGuilds = func(uID string) string { return EndpointUsers + uID + "/guilds" }
|
|
||||||
EndpointUserGuild = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID }
|
|
||||||
EndpointUserGuildSettings = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID + "/settings" }
|
|
||||||
EndpointUserChannels = func(uID string) string { return EndpointUsers + uID + "/channels" }
|
|
||||||
EndpointUserDevices = func(uID string) string { return EndpointUsers + uID + "/devices" }
|
|
||||||
EndpointUserConnections = func(uID string) string { return EndpointUsers + uID + "/connections" }
|
|
||||||
EndpointUserNotes = func(uID string) string { return EndpointUsers + "@me/notes/" + uID }
|
|
||||||
|
|
||||||
EndpointGuild = func(gID string) string { return EndpointGuilds + gID }
|
|
||||||
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 }
|
|
||||||
EndpointGuildIntegrationSync = func(gID, iID string) string { return EndpointGuilds + gID + "/integrations/" + iID + "/sync" }
|
|
||||||
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" }
|
|
||||||
|
|
||||||
EndpointChannel = func(cID string) string { return EndpointChannels + cID }
|
|
||||||
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 }
|
|
||||||
EndpointChannelMessageAck = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID + "/ack" }
|
|
||||||
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" }
|
|
||||||
|
|
||||||
EndpointGroupIcon = func(cID, hash string) string { return EndpointCDNChannelIcons + cID + "/" + hash + ".png" }
|
|
||||||
|
|
||||||
EndpointChannelWebhooks = func(cID string) string { return EndpointChannel(cID) + "/webhooks" }
|
|
||||||
EndpointWebhook = func(wID string) string { return EndpointWebhooks + wID }
|
|
||||||
EndpointWebhookToken = func(wID, token string) string { return EndpointWebhooks + wID + "/" + token }
|
|
||||||
|
|
||||||
EndpointMessageReactionsAll = func(cID, mID string) string {
|
|
||||||
return EndpointChannelMessage(cID, mID) + "/reactions"
|
|
||||||
}
|
|
||||||
EndpointMessageReactions = func(cID, mID, eID string) string {
|
|
||||||
return EndpointChannelMessage(cID, mID) + "/reactions/" + eID
|
|
||||||
}
|
|
||||||
EndpointMessageReaction = func(cID, mID, eID, uID string) string {
|
|
||||||
return EndpointMessageReactions(cID, mID, eID) + "/" + uID
|
|
||||||
}
|
|
||||||
|
|
||||||
EndpointRelationships = func() string { return EndpointUsers + "@me" + "/relationships" }
|
|
||||||
EndpointRelationship = func(uID string) string { return EndpointRelationships() + "/" + uID }
|
|
||||||
EndpointRelationshipsMutual = func(uID string) string { return EndpointUsers + uID + "/relationships" }
|
|
||||||
|
|
||||||
EndpointGuildCreate = EndpointAPI + "guilds"
|
|
||||||
|
|
||||||
EndpointInvite = func(iID string) string { return EndpointAPI + "invite/" + iID }
|
|
||||||
|
|
||||||
EndpointIntegrationsJoin = func(iID string) string { return EndpointAPI + "integrations/" + iID + "/join" }
|
|
||||||
|
|
||||||
EndpointEmoji = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".png" }
|
|
||||||
EndpointEmojiAnimated = func(eID string) string { return EndpointCDN + "emojis/" + eID + ".gif" }
|
|
||||||
|
|
||||||
EndpointOauth2 = EndpointAPI + "oauth2/"
|
|
||||||
EndpointApplications = EndpointOauth2 + "applications"
|
|
||||||
EndpointApplication = func(aID string) string { return EndpointApplications + "/" + aID }
|
|
||||||
EndpointApplicationsBot = func(aID string) string { return EndpointApplications + "/" + aID + "/bot" }
|
|
||||||
EndpointApplicationAssets = func(aID string) string { return EndpointApplications + "/" + aID + "/assets" }
|
|
||||||
)
|
|
@ -1,54 +0,0 @@
|
|||||||
package discordgo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/ed25519"
|
|
||||||
"encoding/hex"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// VerifyInteraction implements message verification of the discord interactions api
|
|
||||||
// signing algorithm, as documented here:
|
|
||||||
// https://discord.com/developers/docs/interactions/slash-commands#security-and-authorization
|
|
||||||
func VerifyInteraction(r *http.Request, key ed25519.PublicKey) bool {
|
|
||||||
var msg bytes.Buffer
|
|
||||||
|
|
||||||
signature := r.Header.Get("X-Signature-Ed25519")
|
|
||||||
if signature == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
sig, err := hex.DecodeString(signature)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(sig) != ed25519.SignatureSize {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
timestamp := r.Header.Get("X-Signature-Timestamp")
|
|
||||||
if timestamp == "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.WriteString(timestamp)
|
|
||||||
|
|
||||||
defer r.Body.Close()
|
|
||||||
var body bytes.Buffer
|
|
||||||
|
|
||||||
// at the end of the function, copy the original body back into the request
|
|
||||||
defer func() {
|
|
||||||
r.Body = ioutil.NopCloser(&body)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// copy body into buffers
|
|
||||||
_, err = io.Copy(&msg, io.TeeReader(r.Body, &body))
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return ed25519.Verify(key, msg.Bytes(), sig)
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,17 +0,0 @@
|
|||||||
package discordgo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SnowflakeTimestamp returns the creation time of a Snowflake ID relative to the creation of Discord.
|
|
||||||
func SnowflakeTimestamp(ID string) (t time.Time, err error) {
|
|
||||||
i, err := strconv.ParseInt(ID, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
timestamp := (i >> 22) + 1420070400000
|
|
||||||
t = time.Unix(0, timestamp*1000000)
|
|
||||||
return
|
|
||||||
}
|
|
Loading…
Reference in New Issue