2018-08-06 19:10:54 +00:00
// 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 functions for interacting with the Discord REST/JSON API
// at the lowest level.
package discordgo
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"image"
_ "image/jpeg" // For JPEG decoding
_ "image/png" // For PNG decoding
"io"
"io/ioutil"
"log"
"mime/multipart"
"net/http"
"net/textproto"
"net/url"
"strconv"
"strings"
"time"
)
// All error constants
var (
ErrJSONUnmarshal = errors . New ( "json unmarshal" )
ErrStatusOffline = errors . New ( "You can't set your Status to offline" )
ErrVerificationLevelBounds = errors . New ( "VerificationLevel out of bounds, should be between 0 and 3" )
ErrPruneDaysBounds = errors . New ( "the number of days should be more than or equal to 1" )
ErrGuildNoIcon = errors . New ( "guild does not have an icon set" )
ErrGuildNoSplash = errors . New ( "guild does not have a splash set" )
2018-11-12 23:02:07 +00:00
ErrUnauthorized = errors . New ( "HTTP request was unauthorized. This could be because the provided token was not a bot token. Please add \"Bot \" to the start of your token. https://discordapp.com/developers/docs/reference#authentication-example-bot-token-authorization-header" )
2018-08-06 19:10:54 +00:00
)
// Request is the same as RequestWithBucketID but the bucket id is the same as the urlStr
func ( s * Session ) Request ( method , urlStr string , data interface { } ) ( response [ ] byte , err error ) {
return s . RequestWithBucketID ( method , urlStr , data , strings . SplitN ( urlStr , "?" , 2 ) [ 0 ] )
}
// RequestWithBucketID makes a (GET/POST/...) Requests to Discord REST API with JSON data.
func ( s * Session ) RequestWithBucketID ( method , urlStr string , data interface { } , bucketID string ) ( response [ ] byte , err error ) {
var body [ ] byte
if data != nil {
body , err = json . Marshal ( data )
if err != nil {
return
}
}
return s . request ( method , urlStr , "application/json" , body , bucketID , 0 )
}
// request makes a (GET/POST/...) Requests to Discord REST API.
// Sequence is the sequence number, if it fails with a 502 it will
// retry with sequence+1 until it either succeeds or sequence >= session.MaxRestRetries
func ( s * Session ) request ( method , urlStr , contentType string , b [ ] byte , bucketID string , sequence int ) ( response [ ] byte , err error ) {
if bucketID == "" {
bucketID = strings . SplitN ( urlStr , "?" , 2 ) [ 0 ]
}
return s . RequestWithLockedBucket ( method , urlStr , contentType , b , s . Ratelimiter . LockBucket ( bucketID ) , sequence )
}
// RequestWithLockedBucket makes a request using a bucket that's already been locked
func ( s * Session ) RequestWithLockedBucket ( method , urlStr , contentType string , b [ ] byte , bucket * Bucket , sequence int ) ( response [ ] byte , err error ) {
if s . Debug {
log . Printf ( "API REQUEST %8s :: %s\n" , method , urlStr )
log . Printf ( "API REQUEST PAYLOAD :: [%s]\n" , string ( b ) )
}
req , err := http . NewRequest ( method , urlStr , bytes . NewBuffer ( b ) )
if err != nil {
bucket . Release ( nil )
return
}
// Not used on initial login..
// TODO: Verify if a login, otherwise complain about no-token
if s . Token != "" {
req . Header . Set ( "authorization" , s . Token )
}
req . Header . Set ( "Content-Type" , contentType )
// TODO: Make a configurable static variable.
2018-11-12 23:02:07 +00:00
req . Header . Set ( "User-Agent" , "DiscordBot (https://github.com/bwmarrin/discordgo, v" + VERSION + ")" )
2018-08-06 19:10:54 +00:00
if s . Debug {
for k , v := range req . Header {
log . Printf ( "API REQUEST HEADER :: [%s] = %+v\n" , k , v )
}
}
resp , err := s . Client . Do ( req )
if err != nil {
bucket . Release ( nil )
return
}
defer func ( ) {
err2 := resp . Body . Close ( )
if err2 != nil {
log . Println ( "error closing resp body" )
}
} ( )
err = bucket . Release ( resp . Header )
if err != nil {
return
}
response , err = ioutil . ReadAll ( resp . Body )
if err != nil {
return
}
if s . Debug {
log . Printf ( "API RESPONSE STATUS :: %s\n" , resp . Status )
for k , v := range resp . Header {
log . Printf ( "API RESPONSE HEADER :: [%s] = %+v\n" , k , v )
}
log . Printf ( "API RESPONSE BODY :: [%s]\n\n\n" , response )
}
switch resp . StatusCode {
case http . StatusOK :
case http . StatusCreated :
case http . StatusNoContent :
case http . StatusBadGateway :
// Retry sending request if possible
if sequence < s . MaxRestRetries {
s . log ( LogInformational , "%s Failed (%s), Retrying..." , urlStr , resp . Status )
response , err = s . RequestWithLockedBucket ( method , urlStr , contentType , b , s . Ratelimiter . LockBucketObject ( bucket ) , sequence + 1 )
} else {
err = fmt . Errorf ( "Exceeded Max retries HTTP %s, %s" , resp . Status , response )
}
case 429 : // TOO MANY REQUESTS - Rate limiting
rl := TooManyRequests { }
err = json . Unmarshal ( response , & rl )
if err != nil {
s . log ( LogError , "rate limit unmarshal error, %s" , err )
return
}
s . log ( LogInformational , "Rate Limiting %s, retry in %d" , urlStr , rl . RetryAfter )
s . handleEvent ( rateLimitEventType , RateLimit { TooManyRequests : & rl , URL : urlStr } )
time . Sleep ( rl . RetryAfter * time . Millisecond )
// we can make the above smarter
// this method can cause longer delays than required
response , err = s . RequestWithLockedBucket ( method , urlStr , contentType , b , s . Ratelimiter . LockBucketObject ( bucket ) , sequence )
2018-11-12 23:02:07 +00:00
case http . StatusUnauthorized :
if strings . Index ( s . Token , "Bot " ) != 0 {
s . log ( LogInformational , ErrUnauthorized . Error ( ) )
err = ErrUnauthorized
}
fallthrough
2018-08-06 19:10:54 +00:00
default : // Error condition
err = newRestError ( req , resp , response )
}
return
}
func unmarshal ( data [ ] byte , v interface { } ) error {
err := json . Unmarshal ( data , v )
if err != nil {
return ErrJSONUnmarshal
}
return nil
}
// ------------------------------------------------------------------------------------------------
// Functions specific to Discord Sessions
// ------------------------------------------------------------------------------------------------
// Login asks the Discord server for an authentication token.
//
// 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 ( s * Session ) Login ( email , password string ) ( err error ) {
data := struct {
Email string ` json:"email" `
Password string ` json:"password" `
} { email , password }
response , err := s . RequestWithBucketID ( "POST" , EndpointLogin , data , EndpointLogin )
if err != nil {
return
}
temp := struct {
Token string ` json:"token" `
MFA bool ` json:"mfa" `
} { }
err = unmarshal ( response , & temp )
if err != nil {
return
}
s . Token = temp . Token
s . MFA = temp . MFA
return
}
// Register sends a Register request to Discord, and returns the authentication token
// Note that this account is temporary and should be verified for future use.
// Another option is to save the authentication token external, but this isn't recommended.
func ( s * Session ) Register ( username string ) ( token string , err error ) {
data := struct {
Username string ` json:"username" `
} { username }
response , err := s . RequestWithBucketID ( "POST" , EndpointRegister , data , EndpointRegister )
if err != nil {
return
}
temp := struct {
Token string ` json:"token" `
} { }
err = unmarshal ( response , & temp )
if err != nil {
return
}
token = temp . Token
return
}
// Logout sends a logout request to Discord.
// This does not seem to actually invalidate the token. So you can still
// make API calls even after a Logout. So, it seems almost pointless to
// even use.
func ( s * Session ) Logout ( ) ( err error ) {
2018-11-12 23:02:07 +00:00
// _, err = s.Request("POST", LOGOUT, `{"token": "` + s.Token + `"}`)
2018-08-06 19:10:54 +00:00
if s . Token == "" {
return
}
data := struct {
Token string ` json:"token" `
} { s . Token }
_ , err = s . RequestWithBucketID ( "POST" , EndpointLogout , data , EndpointLogout )
return
}
// ------------------------------------------------------------------------------------------------
// Functions specific to Discord Users
// ------------------------------------------------------------------------------------------------
// User returns the user details of the given userID
// userID : A user ID or "@me" which is a shortcut of current user ID
func ( s * Session ) User ( userID string ) ( st * User , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointUser ( userID ) , nil , EndpointUsers )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// UserAvatar is deprecated. Please use UserAvatarDecode
// userID : A user ID or "@me" which is a shortcut of current user ID
func ( s * Session ) UserAvatar ( userID string ) ( img image . Image , err error ) {
u , err := s . User ( userID )
if err != nil {
return
}
img , err = s . UserAvatarDecode ( u )
return
}
// UserAvatarDecode returns an image.Image of a user's Avatar
// user : The user which avatar should be retrieved
func ( s * Session ) UserAvatarDecode ( u * User ) ( img image . Image , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointUserAvatar ( u . ID , u . Avatar ) , nil , EndpointUserAvatar ( "" , "" ) )
if err != nil {
return
}
img , _ , err = image . Decode ( bytes . NewReader ( body ) )
return
}
// UserUpdate updates a users settings.
func ( s * Session ) UserUpdate ( email , password , username , avatar , newPassword string ) ( st * User , err error ) {
// NOTE: Avatar must be either the hash/id of existing Avatar or
// data:image/png;base64,BASE64_STRING_OF_NEW_AVATAR_PNG
// to set a new avatar.
// If left blank, avatar will be set to null/blank
data := struct {
Email string ` json:"email,omitempty" `
Password string ` json:"password,omitempty" `
Username string ` json:"username,omitempty" `
Avatar string ` json:"avatar,omitempty" `
NewPassword string ` json:"new_password,omitempty" `
} { email , password , username , avatar , newPassword }
body , err := s . RequestWithBucketID ( "PATCH" , EndpointUser ( "@me" ) , data , EndpointUsers )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// UserSettings returns the settings for a given user
func ( s * Session ) UserSettings ( ) ( st * Settings , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointUserSettings ( "@me" ) , nil , EndpointUserSettings ( "" ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// UserUpdateStatus update the user status
// status : The new status (Actual valid status are 'online','idle','dnd','invisible')
func ( s * Session ) UserUpdateStatus ( status Status ) ( st * Settings , err error ) {
if status == StatusOffline {
err = ErrStatusOffline
return
}
data := struct {
Status Status ` json:"status" `
} { status }
body , err := s . RequestWithBucketID ( "PATCH" , EndpointUserSettings ( "@me" ) , data , EndpointUserSettings ( "" ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
2018-11-12 23:02:07 +00:00
// UserConnections returns the user's connections
func ( s * Session ) UserConnections ( ) ( conn [ ] * UserConnection , err error ) {
response , err := s . RequestWithBucketID ( "GET" , EndpointUserConnections ( "@me" ) , nil , EndpointUserConnections ( "@me" ) )
if err != nil {
return nil , err
}
err = unmarshal ( response , & conn )
if err != nil {
return
}
return
}
2018-08-06 19:10:54 +00:00
// UserChannels returns an array of Channel structures for all private
// channels.
func ( s * Session ) UserChannels ( ) ( st [ ] * Channel , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointUserChannels ( "@me" ) , nil , EndpointUserChannels ( "" ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// UserChannelCreate creates a new User (Private) Channel with another User
// recipientID : A user ID for the user to which this channel is opened with.
func ( s * Session ) UserChannelCreate ( recipientID string ) ( st * Channel , err error ) {
data := struct {
RecipientID string ` json:"recipient_id" `
} { recipientID }
body , err := s . RequestWithBucketID ( "POST" , EndpointUserChannels ( "@me" ) , data , EndpointUserChannels ( "" ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// UserGuilds returns an array of UserGuild structures for all guilds.
// limit : The number guilds that can be returned. (max 100)
// beforeID : If provided all guilds returned will be before given ID.
// afterID : If provided all guilds returned will be after given ID.
func ( s * Session ) UserGuilds ( limit int , beforeID , afterID string ) ( st [ ] * UserGuild , err error ) {
v := url . Values { }
if limit > 0 {
v . Set ( "limit" , strconv . Itoa ( limit ) )
}
if afterID != "" {
v . Set ( "after" , afterID )
}
if beforeID != "" {
v . Set ( "before" , beforeID )
}
uri := EndpointUserGuilds ( "@me" )
if len ( v ) > 0 {
2018-11-12 23:02:07 +00:00
uri += "?" + v . Encode ( )
2018-08-06 19:10:54 +00:00
}
body , err := s . RequestWithBucketID ( "GET" , uri , nil , EndpointUserGuilds ( "" ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// UserGuildSettingsEdit Edits the users notification settings for a guild
// guildID : The ID of the guild to edit the settings on
// settings : The settings to update
func ( s * Session ) UserGuildSettingsEdit ( guildID string , settings * UserGuildSettingsEdit ) ( st * UserGuildSettings , err error ) {
body , err := s . RequestWithBucketID ( "PATCH" , EndpointUserGuildSettings ( "@me" , guildID ) , settings , EndpointUserGuildSettings ( "" , guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// UserChannelPermissions returns the permission of a user in a channel.
// userID : The ID of the user to calculate permissions for.
// channelID : The ID of the channel to calculate permission for.
//
// NOTE: This function is now deprecated and will be removed in the future.
// Please see the same function inside state.go
func ( s * Session ) UserChannelPermissions ( userID , channelID string ) ( apermissions int , err error ) {
// Try to just get permissions from state.
apermissions , err = s . State . UserChannelPermissions ( userID , channelID )
if err == nil {
return
}
// Otherwise try get as much data from state as possible, falling back to the network.
channel , err := s . State . Channel ( channelID )
if err != nil || channel == nil {
channel , err = s . Channel ( channelID )
if err != nil {
return
}
}
guild , err := s . State . Guild ( channel . GuildID )
if err != nil || guild == nil {
guild , err = s . Guild ( channel . GuildID )
if err != nil {
return
}
}
if userID == guild . OwnerID {
apermissions = PermissionAll
return
}
member , err := s . State . Member ( guild . ID , userID )
if err != nil || member == nil {
member , err = s . GuildMember ( guild . ID , userID )
if err != nil {
return
}
}
return memberPermissions ( guild , channel , member ) , nil
}
// Calculates the permissions for a member.
// https://support.discordapp.com/hc/en-us/articles/206141927-How-is-the-permission-hierarchy-structured-
func memberPermissions ( guild * Guild , channel * Channel , member * Member ) ( apermissions int ) {
userID := member . User . ID
if userID == guild . OwnerID {
apermissions = PermissionAll
return
}
for _ , role := range guild . Roles {
if role . ID == guild . ID {
apermissions |= role . Permissions
break
}
}
for _ , role := range guild . Roles {
for _ , roleID := range member . Roles {
if role . ID == roleID {
apermissions |= role . Permissions
break
}
}
}
if apermissions & PermissionAdministrator == PermissionAdministrator {
apermissions |= PermissionAll
}
// Apply @everyone overrides from the channel.
for _ , overwrite := range channel . PermissionOverwrites {
if guild . ID == overwrite . ID {
apermissions &= ^ overwrite . Deny
apermissions |= overwrite . Allow
break
}
}
denies := 0
allows := 0
// Member overwrites can override role overrides, so do two passes
for _ , overwrite := range channel . PermissionOverwrites {
for _ , roleID := range member . Roles {
if overwrite . Type == "role" && roleID == overwrite . ID {
denies |= overwrite . Deny
allows |= overwrite . Allow
break
}
}
}
apermissions &= ^ denies
apermissions |= allows
for _ , overwrite := range channel . PermissionOverwrites {
if overwrite . Type == "member" && overwrite . ID == userID {
apermissions &= ^ overwrite . Deny
apermissions |= overwrite . Allow
break
}
}
if apermissions & PermissionAdministrator == PermissionAdministrator {
apermissions |= PermissionAllChannel
}
return apermissions
}
// ------------------------------------------------------------------------------------------------
// Functions specific to Discord Guilds
// ------------------------------------------------------------------------------------------------
// Guild returns a Guild structure of a specific Guild.
// guildID : The ID of a Guild
func ( s * Session ) Guild ( guildID string ) ( st * Guild , err error ) {
if s . StateEnabled {
// Attempt to grab the guild from State first.
st , err = s . State . Guild ( guildID )
2018-11-12 23:02:07 +00:00
if err == nil && ! st . Unavailable {
2018-08-06 19:10:54 +00:00
return
}
}
body , err := s . RequestWithBucketID ( "GET" , EndpointGuild ( guildID ) , nil , EndpointGuild ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// GuildCreate creates a new Guild
// name : A name for the Guild (2-100 characters)
func ( s * Session ) GuildCreate ( name string ) ( st * Guild , err error ) {
data := struct {
Name string ` json:"name" `
} { name }
body , err := s . RequestWithBucketID ( "POST" , EndpointGuildCreate , data , EndpointGuildCreate )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// GuildEdit edits a new Guild
// guildID : The ID of a Guild
// g : A GuildParams struct with the values Name, Region and VerificationLevel defined.
func ( s * Session ) GuildEdit ( guildID string , g GuildParams ) ( st * Guild , err error ) {
// Bounds checking for VerificationLevel, interval: [0, 3]
if g . VerificationLevel != nil {
val := * g . VerificationLevel
if val < 0 || val > 3 {
err = ErrVerificationLevelBounds
return
}
}
//Bounds checking for regions
if g . Region != "" {
isValid := false
regions , _ := s . VoiceRegions ( )
for _ , r := range regions {
if g . Region == r . ID {
isValid = true
}
}
if ! isValid {
var valid [ ] string
for _ , r := range regions {
valid = append ( valid , r . ID )
}
err = fmt . Errorf ( "Region not a valid region (%q)" , valid )
return
}
}
body , err := s . RequestWithBucketID ( "PATCH" , EndpointGuild ( guildID ) , g , EndpointGuild ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// GuildDelete deletes a Guild.
// guildID : The ID of a Guild
func ( s * Session ) GuildDelete ( guildID string ) ( st * Guild , err error ) {
body , err := s . RequestWithBucketID ( "DELETE" , EndpointGuild ( guildID ) , nil , EndpointGuild ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// GuildLeave leaves a Guild.
// guildID : The ID of a Guild
func ( s * Session ) GuildLeave ( guildID string ) ( err error ) {
_ , err = s . RequestWithBucketID ( "DELETE" , EndpointUserGuild ( "@me" , guildID ) , nil , EndpointUserGuild ( "" , guildID ) )
return
}
2019-08-26 21:47:50 +00:00
// GuildBans returns an array of GuildBan structures for all bans of a
2018-08-06 19:10:54 +00:00
// given guild.
// guildID : The ID of a Guild.
func ( s * Session ) GuildBans ( guildID string ) ( st [ ] * GuildBan , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointGuildBans ( guildID ) , nil , EndpointGuildBans ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// GuildBanCreate bans the given user from the given guild.
// guildID : The ID of a Guild.
// userID : The ID of a User
// days : The number of days of previous comments to delete.
func ( s * Session ) GuildBanCreate ( guildID , userID string , days int ) ( err error ) {
return s . GuildBanCreateWithReason ( guildID , userID , "" , days )
}
// GuildBanCreateWithReason bans the given user from the given guild also providing a reaso.
// guildID : The ID of a Guild.
// userID : The ID of a User
// reason : The reason for this ban
// days : The number of days of previous comments to delete.
func ( s * Session ) GuildBanCreateWithReason ( guildID , userID , reason string , days int ) ( err error ) {
uri := EndpointGuildBan ( guildID , userID )
queryParams := url . Values { }
if days > 0 {
queryParams . Set ( "delete-message-days" , strconv . Itoa ( days ) )
}
if reason != "" {
queryParams . Set ( "reason" , reason )
}
if len ( queryParams ) > 0 {
uri += "?" + queryParams . Encode ( )
}
_ , err = s . RequestWithBucketID ( "PUT" , uri , nil , EndpointGuildBan ( guildID , "" ) )
return
}
// GuildBanDelete removes the given user from the guild bans
// guildID : The ID of a Guild.
// userID : The ID of a User
func ( s * Session ) GuildBanDelete ( guildID , userID string ) ( err error ) {
_ , err = s . RequestWithBucketID ( "DELETE" , EndpointGuildBan ( guildID , userID ) , nil , EndpointGuildBan ( guildID , "" ) )
return
}
// GuildMembers returns a list of members for a guild.
// guildID : The ID of a Guild.
// after : The id of the member to return members after
// limit : max number of members to return (max 1000)
func ( s * Session ) GuildMembers ( guildID string , after string , limit int ) ( st [ ] * Member , err error ) {
uri := EndpointGuildMembers ( guildID )
v := url . Values { }
if after != "" {
v . Set ( "after" , after )
}
if limit > 0 {
v . Set ( "limit" , strconv . Itoa ( limit ) )
}
if len ( v ) > 0 {
2018-11-12 23:02:07 +00:00
uri += "?" + v . Encode ( )
2018-08-06 19:10:54 +00:00
}
body , err := s . RequestWithBucketID ( "GET" , uri , nil , EndpointGuildMembers ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// GuildMember returns a member of a guild.
// guildID : The ID of a Guild.
// userID : The ID of a User
func ( s * Session ) GuildMember ( guildID , userID string ) ( st * Member , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointGuildMember ( guildID , userID ) , nil , EndpointGuildMember ( guildID , "" ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
2018-11-12 23:02:07 +00:00
// GuildMemberAdd force joins a user to the guild.
// accessToken : Valid access_token for the user.
// guildID : The ID of a Guild.
// userID : The ID of a User.
// nick : Value to set users nickname to
// roles : A list of role ID's to set on the member.
// mute : If the user is muted.
// deaf : If the user is deafened.
func ( s * Session ) GuildMemberAdd ( accessToken , guildID , userID , nick string , roles [ ] string , mute , deaf bool ) ( err error ) {
data := struct {
AccessToken string ` json:"access_token" `
Nick string ` json:"nick,omitempty" `
Roles [ ] string ` json:"roles,omitempty" `
Mute bool ` json:"mute,omitempty" `
Deaf bool ` json:"deaf,omitempty" `
} { accessToken , nick , roles , mute , deaf }
_ , err = s . RequestWithBucketID ( "PUT" , EndpointGuildMember ( guildID , userID ) , data , EndpointGuildMember ( guildID , "" ) )
if err != nil {
return err
}
return err
}
2018-08-06 19:10:54 +00:00
// GuildMemberDelete removes the given user from the given guild.
// guildID : The ID of a Guild.
// userID : The ID of a User
func ( s * Session ) GuildMemberDelete ( guildID , userID string ) ( err error ) {
return s . GuildMemberDeleteWithReason ( guildID , userID , "" )
}
// GuildMemberDeleteWithReason removes the given user from the given guild.
// guildID : The ID of a Guild.
// userID : The ID of a User
// reason : The reason for the kick
func ( s * Session ) GuildMemberDeleteWithReason ( guildID , userID , reason string ) ( err error ) {
uri := EndpointGuildMember ( guildID , userID )
if reason != "" {
uri += "?reason=" + url . QueryEscape ( reason )
}
_ , err = s . RequestWithBucketID ( "DELETE" , uri , nil , EndpointGuildMember ( guildID , "" ) )
return
}
// GuildMemberEdit edits the roles of a member.
// guildID : The ID of a Guild.
// userID : The ID of a User.
// roles : A list of role ID's to set on the member.
func ( s * Session ) GuildMemberEdit ( guildID , userID string , roles [ ] string ) ( err error ) {
data := struct {
Roles [ ] string ` json:"roles" `
} { roles }
_ , err = s . RequestWithBucketID ( "PATCH" , EndpointGuildMember ( guildID , userID ) , data , EndpointGuildMember ( guildID , "" ) )
if err != nil {
return
}
return
}
// GuildMemberMove moves a guild member from one voice channel to another/none
// guildID : The ID of a Guild.
// userID : The ID of a User.
// channelID : The ID of a channel to move user to, or null?
// NOTE : I am not entirely set on the name of this function and it may change
// prior to the final 1.0.0 release of Discordgo
func ( s * Session ) GuildMemberMove ( guildID , userID , channelID string ) ( err error ) {
data := struct {
ChannelID string ` json:"channel_id" `
} { channelID }
_ , err = s . RequestWithBucketID ( "PATCH" , EndpointGuildMember ( guildID , userID ) , data , EndpointGuildMember ( guildID , "" ) )
if err != nil {
return
}
return
}
// GuildMemberNickname updates the nickname of a guild member
// guildID : The ID of a guild
// userID : The ID of a user
// userID : The ID of a user or "@me" which is a shortcut of the current user ID
func ( s * Session ) GuildMemberNickname ( guildID , userID , nickname string ) ( err error ) {
data := struct {
Nick string ` json:"nick" `
} { nickname }
if userID == "@me" {
userID += "/nick"
}
_ , err = s . RequestWithBucketID ( "PATCH" , EndpointGuildMember ( guildID , userID ) , data , EndpointGuildMember ( guildID , "" ) )
return
}
// GuildMemberRoleAdd adds the specified role to a given member
// guildID : The ID of a Guild.
// userID : The ID of a User.
// roleID : The ID of a Role to be assigned to the user.
func ( s * Session ) GuildMemberRoleAdd ( guildID , userID , roleID string ) ( err error ) {
_ , err = s . RequestWithBucketID ( "PUT" , EndpointGuildMemberRole ( guildID , userID , roleID ) , nil , EndpointGuildMemberRole ( guildID , "" , "" ) )
return
}
// GuildMemberRoleRemove removes the specified role to a given member
// guildID : The ID of a Guild.
// userID : The ID of a User.
// roleID : The ID of a Role to be removed from the user.
func ( s * Session ) GuildMemberRoleRemove ( guildID , userID , roleID string ) ( err error ) {
_ , err = s . RequestWithBucketID ( "DELETE" , EndpointGuildMemberRole ( guildID , userID , roleID ) , nil , EndpointGuildMemberRole ( guildID , "" , "" ) )
return
}
// GuildChannels returns an array of Channel structures for all channels of a
// given guild.
// guildID : The ID of a Guild.
func ( s * Session ) GuildChannels ( guildID string ) ( st [ ] * Channel , err error ) {
body , err := s . request ( "GET" , EndpointGuildChannels ( guildID ) , "" , nil , EndpointGuildChannels ( guildID ) , 0 )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
2018-11-12 23:02:07 +00:00
// GuildChannelCreateData is provided to GuildChannelCreateComplex
type GuildChannelCreateData struct {
Name string ` json:"name" `
Type ChannelType ` json:"type" `
Topic string ` json:"topic,omitempty" `
Bitrate int ` json:"bitrate,omitempty" `
UserLimit int ` json:"user_limit,omitempty" `
PermissionOverwrites [ ] * PermissionOverwrite ` json:"permission_overwrites,omitempty" `
ParentID string ` json:"parent_id,omitempty" `
NSFW bool ` json:"nsfw,omitempty" `
}
2018-08-06 19:10:54 +00:00
2018-11-12 23:02:07 +00:00
// GuildChannelCreateComplex creates a new channel in the given guild
// guildID : The ID of a Guild
// data : A data struct describing the new Channel, Name and Type are mandatory, other fields depending on the type
func ( s * Session ) GuildChannelCreateComplex ( guildID string , data GuildChannelCreateData ) ( st * Channel , err error ) {
2018-08-06 19:10:54 +00:00
body , err := s . RequestWithBucketID ( "POST" , EndpointGuildChannels ( guildID ) , data , EndpointGuildChannels ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
2018-11-12 23:02:07 +00:00
// GuildChannelCreate creates a new channel in the given guild
// guildID : The ID of a Guild.
// name : Name of the channel (2-100 chars length)
// ctype : Type of the channel
func ( s * Session ) GuildChannelCreate ( guildID , name string , ctype ChannelType ) ( st * Channel , err error ) {
return s . GuildChannelCreateComplex ( guildID , GuildChannelCreateData {
Name : name ,
Type : ctype ,
} )
}
2018-08-06 19:10:54 +00:00
// GuildChannelsReorder updates the order of channels in a guild
// guildID : The ID of a Guild.
// channels : Updated channels.
func ( s * Session ) GuildChannelsReorder ( guildID string , channels [ ] * Channel ) ( err error ) {
2018-11-12 23:02:07 +00:00
data := make ( [ ] struct {
ID string ` json:"id" `
Position int ` json:"position" `
} , len ( channels ) )
for i , c := range channels {
data [ i ] . ID = c . ID
data [ i ] . Position = c . Position
}
_ , err = s . RequestWithBucketID ( "PATCH" , EndpointGuildChannels ( guildID ) , data , EndpointGuildChannels ( guildID ) )
2018-08-06 19:10:54 +00:00
return
}
// GuildInvites returns an array of Invite structures for the given guild
// guildID : The ID of a Guild.
func ( s * Session ) GuildInvites ( guildID string ) ( st [ ] * Invite , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointGuildInvites ( guildID ) , nil , EndpointGuildInvites ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// GuildRoles returns all roles for a given guild.
// guildID : The ID of a Guild.
func ( s * Session ) GuildRoles ( guildID string ) ( st [ ] * Role , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointGuildRoles ( guildID ) , nil , EndpointGuildRoles ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return // TODO return pointer
}
// GuildRoleCreate returns a new Guild Role.
// guildID: The ID of a Guild.
func ( s * Session ) GuildRoleCreate ( guildID string ) ( st * Role , err error ) {
body , err := s . RequestWithBucketID ( "POST" , EndpointGuildRoles ( guildID ) , nil , EndpointGuildRoles ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// GuildRoleEdit updates an existing Guild Role with new values
// guildID : The ID of a Guild.
// roleID : The ID of a Role.
// name : The name of the Role.
// color : The color of the role (decimal, not hex).
// hoist : Whether to display the role's users separately.
// perm : The permissions for the role.
// mention : Whether this role is mentionable
func ( s * Session ) GuildRoleEdit ( guildID , roleID , name string , color int , hoist bool , perm int , mention bool ) ( st * Role , err error ) {
// Prevent sending a color int that is too big.
if color > 0xFFFFFF {
err = fmt . Errorf ( "color value cannot be larger than 0xFFFFFF" )
return nil , err
}
data := struct {
Name string ` json:"name" ` // The role's name (overwrites existing)
Color int ` json:"color" ` // The color the role should have (as a decimal, not hex)
Hoist bool ` json:"hoist" ` // Whether to display the role's users separately
Permissions int ` json:"permissions" ` // The overall permissions number of the role (overwrites existing)
Mentionable bool ` json:"mentionable" ` // Whether this role is mentionable
} { name , color , hoist , perm , mention }
body , err := s . RequestWithBucketID ( "PATCH" , EndpointGuildRole ( guildID , roleID ) , data , EndpointGuildRole ( guildID , "" ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// GuildRoleReorder reoders guild roles
// guildID : The ID of a Guild.
// roles : A list of ordered roles.
func ( s * Session ) GuildRoleReorder ( guildID string , roles [ ] * Role ) ( st [ ] * Role , err error ) {
body , err := s . RequestWithBucketID ( "PATCH" , EndpointGuildRoles ( guildID ) , roles , EndpointGuildRoles ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// GuildRoleDelete deletes an existing role.
// guildID : The ID of a Guild.
// roleID : The ID of a Role.
func ( s * Session ) GuildRoleDelete ( guildID , roleID string ) ( err error ) {
_ , err = s . RequestWithBucketID ( "DELETE" , EndpointGuildRole ( guildID , roleID ) , nil , EndpointGuildRole ( guildID , "" ) )
return
}
// GuildPruneCount Returns the number of members that would be removed in a prune operation.
// Requires 'KICK_MEMBER' permission.
// guildID : The ID of a Guild.
// days : The number of days to count prune for (1 or more).
func ( s * Session ) GuildPruneCount ( guildID string , days uint32 ) ( count uint32 , err error ) {
count = 0
if days <= 0 {
err = ErrPruneDaysBounds
return
}
p := struct {
Pruned uint32 ` json:"pruned" `
} { }
2018-11-12 23:02:07 +00:00
uri := EndpointGuildPrune ( guildID ) + "?days=" + strconv . FormatUint ( uint64 ( days ) , 10 )
2018-08-06 19:10:54 +00:00
body , err := s . RequestWithBucketID ( "GET" , uri , nil , EndpointGuildPrune ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & p )
if err != nil {
return
}
count = p . Pruned
return
}
// GuildPrune Begin as prune operation. Requires the 'KICK_MEMBERS' permission.
// Returns an object with one 'pruned' key indicating the number of members that were removed in the prune operation.
// guildID : The ID of a Guild.
// days : The number of days to count prune for (1 or more).
func ( s * Session ) GuildPrune ( guildID string , days uint32 ) ( count uint32 , err error ) {
count = 0
if days <= 0 {
err = ErrPruneDaysBounds
return
}
data := struct {
days uint32
} { days }
p := struct {
Pruned uint32 ` json:"pruned" `
} { }
body , err := s . RequestWithBucketID ( "POST" , EndpointGuildPrune ( guildID ) , data , EndpointGuildPrune ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & p )
if err != nil {
return
}
count = p . Pruned
return
}
// GuildIntegrations returns an array of Integrations for a guild.
// guildID : The ID of a Guild.
2018-11-12 23:02:07 +00:00
func ( s * Session ) GuildIntegrations ( guildID string ) ( st [ ] * Integration , err error ) {
2018-08-06 19:10:54 +00:00
body , err := s . RequestWithBucketID ( "GET" , EndpointGuildIntegrations ( guildID ) , nil , EndpointGuildIntegrations ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// GuildIntegrationCreate creates a Guild Integration.
// guildID : The ID of a Guild.
// integrationType : The Integration type.
// integrationID : The ID of an integration.
func ( s * Session ) GuildIntegrationCreate ( guildID , integrationType , integrationID string ) ( err error ) {
data := struct {
Type string ` json:"type" `
ID string ` json:"id" `
} { integrationType , integrationID }
_ , err = s . RequestWithBucketID ( "POST" , EndpointGuildIntegrations ( guildID ) , data , EndpointGuildIntegrations ( guildID ) )
return
}
// GuildIntegrationEdit edits a Guild Integration.
// guildID : The ID of a Guild.
// integrationType : The Integration type.
// integrationID : The ID of an integration.
// expireBehavior : The behavior when an integration subscription lapses (see the integration object documentation).
// expireGracePeriod : Period (in seconds) where the integration will ignore lapsed subscriptions.
// enableEmoticons : Whether emoticons should be synced for this integration (twitch only currently).
func ( s * Session ) GuildIntegrationEdit ( guildID , integrationID string , expireBehavior , expireGracePeriod int , enableEmoticons bool ) ( err error ) {
data := struct {
ExpireBehavior int ` json:"expire_behavior" `
ExpireGracePeriod int ` json:"expire_grace_period" `
EnableEmoticons bool ` json:"enable_emoticons" `
} { expireBehavior , expireGracePeriod , enableEmoticons }
_ , err = s . RequestWithBucketID ( "PATCH" , EndpointGuildIntegration ( guildID , integrationID ) , data , EndpointGuildIntegration ( guildID , "" ) )
return
}
// GuildIntegrationDelete removes the given integration from the Guild.
// guildID : The ID of a Guild.
// integrationID : The ID of an integration.
func ( s * Session ) GuildIntegrationDelete ( guildID , integrationID string ) ( err error ) {
_ , err = s . RequestWithBucketID ( "DELETE" , EndpointGuildIntegration ( guildID , integrationID ) , nil , EndpointGuildIntegration ( guildID , "" ) )
return
}
// GuildIntegrationSync syncs an integration.
// guildID : The ID of a Guild.
// integrationID : The ID of an integration.
func ( s * Session ) GuildIntegrationSync ( guildID , integrationID string ) ( err error ) {
_ , err = s . RequestWithBucketID ( "POST" , EndpointGuildIntegrationSync ( guildID , integrationID ) , nil , EndpointGuildIntegration ( guildID , "" ) )
return
}
// GuildIcon returns an image.Image of a guild icon.
// guildID : The ID of a Guild.
func ( s * Session ) GuildIcon ( guildID string ) ( img image . Image , err error ) {
g , err := s . Guild ( guildID )
if err != nil {
return
}
if g . Icon == "" {
err = ErrGuildNoIcon
return
}
body , err := s . RequestWithBucketID ( "GET" , EndpointGuildIcon ( guildID , g . Icon ) , nil , EndpointGuildIcon ( guildID , "" ) )
if err != nil {
return
}
img , _ , err = image . Decode ( bytes . NewReader ( body ) )
return
}
// GuildSplash returns an image.Image of a guild splash image.
// guildID : The ID of a Guild.
func ( s * Session ) GuildSplash ( guildID string ) ( img image . Image , err error ) {
g , err := s . Guild ( guildID )
if err != nil {
return
}
if g . Splash == "" {
err = ErrGuildNoSplash
return
}
body , err := s . RequestWithBucketID ( "GET" , EndpointGuildSplash ( guildID , g . Splash ) , nil , EndpointGuildSplash ( guildID , "" ) )
if err != nil {
return
}
img , _ , err = image . Decode ( bytes . NewReader ( body ) )
return
}
// GuildEmbed returns the embed for a Guild.
// guildID : The ID of a Guild.
func ( s * Session ) GuildEmbed ( guildID string ) ( st * GuildEmbed , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointGuildEmbed ( guildID ) , nil , EndpointGuildEmbed ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// GuildEmbedEdit returns the embed for a Guild.
// guildID : The ID of a Guild.
func ( s * Session ) GuildEmbedEdit ( guildID string , enabled bool , channelID string ) ( err error ) {
data := GuildEmbed { enabled , channelID }
_ , err = s . RequestWithBucketID ( "PATCH" , EndpointGuildEmbed ( guildID ) , data , EndpointGuildEmbed ( guildID ) )
return
}
2018-11-12 23:02:07 +00:00
// GuildAuditLog returns the audit log for a Guild.
// guildID : The ID of a Guild.
// userID : If provided the log will be filtered for the given ID.
// beforeID : If provided all log entries returned will be before the given ID.
// actionType : If provided the log will be filtered for the given Action Type.
// limit : The number messages that can be returned. (default 50, min 1, max 100)
func ( s * Session ) GuildAuditLog ( guildID , userID , beforeID string , actionType , limit int ) ( st * GuildAuditLog , err error ) {
uri := EndpointGuildAuditLogs ( guildID )
v := url . Values { }
if userID != "" {
v . Set ( "user_id" , userID )
}
if beforeID != "" {
v . Set ( "before" , beforeID )
}
if actionType > 0 {
v . Set ( "action_type" , strconv . Itoa ( actionType ) )
}
if limit > 0 {
v . Set ( "limit" , strconv . Itoa ( limit ) )
}
if len ( v ) > 0 {
uri = fmt . Sprintf ( "%s?%s" , uri , v . Encode ( ) )
}
body , err := s . RequestWithBucketID ( "GET" , uri , nil , EndpointGuildAuditLogs ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// GuildEmojiCreate creates a new emoji
// guildID : The ID of a Guild.
// name : The Name of the Emoji.
// image : The base64 encoded emoji image, has to be smaller than 256KB.
// roles : The roles for which this emoji will be whitelisted, can be nil.
func ( s * Session ) GuildEmojiCreate ( guildID , name , image string , roles [ ] string ) ( emoji * Emoji , err error ) {
data := struct {
Name string ` json:"name" `
Image string ` json:"image" `
Roles [ ] string ` json:"roles,omitempty" `
} { name , image , roles }
body , err := s . RequestWithBucketID ( "POST" , EndpointGuildEmojis ( guildID ) , data , EndpointGuildEmojis ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & emoji )
return
}
// GuildEmojiEdit modifies an emoji
// guildID : The ID of a Guild.
// emojiID : The ID of an Emoji.
// name : The Name of the Emoji.
// roles : The roles for which this emoji will be whitelisted, can be nil.
func ( s * Session ) GuildEmojiEdit ( guildID , emojiID , name string , roles [ ] string ) ( emoji * Emoji , err error ) {
data := struct {
Name string ` json:"name" `
Roles [ ] string ` json:"roles,omitempty" `
} { name , roles }
body , err := s . RequestWithBucketID ( "PATCH" , EndpointGuildEmoji ( guildID , emojiID ) , data , EndpointGuildEmojis ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & emoji )
return
}
// GuildEmojiDelete deletes an Emoji.
// guildID : The ID of a Guild.
// emojiID : The ID of an Emoji.
func ( s * Session ) GuildEmojiDelete ( guildID , emojiID string ) ( err error ) {
_ , err = s . RequestWithBucketID ( "DELETE" , EndpointGuildEmoji ( guildID , emojiID ) , nil , EndpointGuildEmojis ( guildID ) )
return
}
2018-08-06 19:10:54 +00:00
// ------------------------------------------------------------------------------------------------
// Functions specific to Discord Channels
// ------------------------------------------------------------------------------------------------
// Channel returns a Channel structure of a specific Channel.
// channelID : The ID of the Channel you want returned.
func ( s * Session ) Channel ( channelID string ) ( st * Channel , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointChannel ( channelID ) , nil , EndpointChannel ( channelID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// ChannelEdit edits the given channel
// channelID : The ID of a Channel
// name : The new name to assign the channel.
func ( s * Session ) ChannelEdit ( channelID , name string ) ( * Channel , error ) {
return s . ChannelEditComplex ( channelID , & ChannelEdit {
Name : name ,
} )
}
// ChannelEditComplex edits an existing channel, replacing the parameters entirely with ChannelEdit struct
// channelID : The ID of a Channel
// data : The channel struct to send
func ( s * Session ) ChannelEditComplex ( channelID string , data * ChannelEdit ) ( st * Channel , err error ) {
body , err := s . RequestWithBucketID ( "PATCH" , EndpointChannel ( channelID ) , data , EndpointChannel ( channelID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// ChannelDelete deletes the given channel
// channelID : The ID of a Channel
func ( s * Session ) ChannelDelete ( channelID string ) ( st * Channel , err error ) {
body , err := s . RequestWithBucketID ( "DELETE" , EndpointChannel ( channelID ) , nil , EndpointChannel ( channelID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// ChannelTyping broadcasts to all members that authenticated user is typing in
// the given channel.
// channelID : The ID of a Channel
func ( s * Session ) ChannelTyping ( channelID string ) ( err error ) {
_ , err = s . RequestWithBucketID ( "POST" , EndpointChannelTyping ( channelID ) , nil , EndpointChannelTyping ( channelID ) )
return
}
// ChannelMessages returns an array of Message structures for messages within
// a given channel.
// channelID : The ID of a Channel.
// limit : The number messages that can be returned. (max 100)
// beforeID : If provided all messages returned will be before given ID.
// afterID : If provided all messages returned will be after given ID.
// aroundID : If provided all messages returned will be around given ID.
func ( s * Session ) ChannelMessages ( channelID string , limit int , beforeID , afterID , aroundID string ) ( st [ ] * Message , err error ) {
uri := EndpointChannelMessages ( channelID )
v := url . Values { }
if limit > 0 {
v . Set ( "limit" , strconv . Itoa ( limit ) )
}
if afterID != "" {
v . Set ( "after" , afterID )
}
if beforeID != "" {
v . Set ( "before" , beforeID )
}
if aroundID != "" {
v . Set ( "around" , aroundID )
}
if len ( v ) > 0 {
2018-11-12 23:02:07 +00:00
uri += "?" + v . Encode ( )
2018-08-06 19:10:54 +00:00
}
body , err := s . RequestWithBucketID ( "GET" , uri , nil , EndpointChannelMessages ( channelID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// ChannelMessage gets a single message by ID from a given channel.
// channeld : The ID of a Channel
// messageID : the ID of a Message
func ( s * Session ) ChannelMessage ( channelID , messageID string ) ( st * Message , err error ) {
response , err := s . RequestWithBucketID ( "GET" , EndpointChannelMessage ( channelID , messageID ) , nil , EndpointChannelMessage ( channelID , "" ) )
if err != nil {
return
}
err = unmarshal ( response , & st )
return
}
// ChannelMessageAck acknowledges and marks the given message as read
// channeld : The ID of a Channel
// messageID : the ID of a Message
// lastToken : token returned by last ack
func ( s * Session ) ChannelMessageAck ( channelID , messageID , lastToken string ) ( st * Ack , err error ) {
body , err := s . RequestWithBucketID ( "POST" , EndpointChannelMessageAck ( channelID , messageID ) , & Ack { Token : lastToken } , EndpointChannelMessageAck ( channelID , "" ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// ChannelMessageSend sends a message to the given channel.
// channelID : The ID of a Channel.
// content : The message to send.
func ( s * Session ) ChannelMessageSend ( channelID string , content string ) ( * Message , error ) {
return s . ChannelMessageSendComplex ( channelID , & MessageSend {
Content : content ,
} )
}
var quoteEscaper = strings . NewReplacer ( "\\" , "\\\\" , ` " ` , "\\\"" )
// ChannelMessageSendComplex sends a message to the given channel.
// channelID : The ID of a Channel.
// data : The message struct to send.
func ( s * Session ) ChannelMessageSendComplex ( channelID string , data * MessageSend ) ( st * Message , err error ) {
if data . Embed != nil && data . Embed . Type == "" {
data . Embed . Type = "rich"
}
endpoint := EndpointChannelMessages ( channelID )
// TODO: Remove this when compatibility is not required.
files := data . Files
if data . File != nil {
if files == nil {
files = [ ] * File { data . File }
} else {
err = fmt . Errorf ( "cannot specify both File and Files" )
return
}
}
var response [ ] byte
if len ( files ) > 0 {
body := & bytes . Buffer { }
bodywriter := multipart . NewWriter ( body )
var payload [ ] byte
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
}
response , err = s . request ( "POST" , endpoint , bodywriter . FormDataContentType ( ) , body . Bytes ( ) , endpoint , 0 )
} else {
response , err = s . RequestWithBucketID ( "POST" , endpoint , data , endpoint )
}
if err != nil {
return
}
err = unmarshal ( response , & st )
return
}
// ChannelMessageSendTTS sends a message to the given channel with Text to Speech.
// channelID : The ID of a Channel.
// content : The message to send.
func ( s * Session ) ChannelMessageSendTTS ( channelID string , content string ) ( * Message , error ) {
return s . ChannelMessageSendComplex ( channelID , & MessageSend {
Content : content ,
Tts : true ,
} )
}
// ChannelMessageSendEmbed sends a message to the given channel with embedded data.
// channelID : The ID of a Channel.
// embed : The embed data to send.
func ( s * Session ) ChannelMessageSendEmbed ( channelID string , embed * MessageEmbed ) ( * Message , error ) {
return s . ChannelMessageSendComplex ( channelID , & MessageSend {
Embed : embed ,
} )
}
// ChannelMessageEdit edits an existing message, replacing it entirely with
// the given content.
// channelID : The ID of a Channel
// messageID : The ID of a Message
// content : The contents of the message
func ( s * Session ) ChannelMessageEdit ( channelID , messageID , content string ) ( * Message , error ) {
return s . ChannelMessageEditComplex ( NewMessageEdit ( channelID , messageID ) . SetContent ( content ) )
}
// ChannelMessageEditComplex edits an existing message, replacing it entirely with
// the given MessageEdit struct
func ( s * Session ) ChannelMessageEditComplex ( m * MessageEdit ) ( st * Message , err error ) {
if m . Embed != nil && m . Embed . Type == "" {
m . Embed . Type = "rich"
}
response , err := s . RequestWithBucketID ( "PATCH" , EndpointChannelMessage ( m . Channel , m . ID ) , m , EndpointChannelMessage ( m . Channel , "" ) )
if err != nil {
return
}
err = unmarshal ( response , & st )
return
}
// ChannelMessageEditEmbed edits an existing message with embedded data.
// channelID : The ID of a Channel
// messageID : The ID of a Message
// embed : The embed data to send
func ( s * Session ) ChannelMessageEditEmbed ( channelID , messageID string , embed * MessageEmbed ) ( * Message , error ) {
return s . ChannelMessageEditComplex ( NewMessageEdit ( channelID , messageID ) . SetEmbed ( embed ) )
}
// ChannelMessageDelete deletes a message from the Channel.
func ( s * Session ) ChannelMessageDelete ( channelID , messageID string ) ( err error ) {
_ , err = s . RequestWithBucketID ( "DELETE" , EndpointChannelMessage ( channelID , messageID ) , nil , EndpointChannelMessage ( channelID , "" ) )
return
}
// ChannelMessagesBulkDelete bulk deletes the messages from the channel for the provided messageIDs.
// If only one messageID is in the slice call channelMessageDelete function.
// If the slice is empty do nothing.
// channelID : The ID of the channel for the messages to delete.
// messages : The IDs of the messages to be deleted. A slice of string IDs. A maximum of 100 messages.
func ( s * Session ) ChannelMessagesBulkDelete ( channelID string , messages [ ] string ) ( err error ) {
if len ( messages ) == 0 {
return
}
if len ( messages ) == 1 {
err = s . ChannelMessageDelete ( channelID , messages [ 0 ] )
return
}
if len ( messages ) > 100 {
messages = messages [ : 100 ]
}
data := struct {
Messages [ ] string ` json:"messages" `
} { messages }
_ , err = s . RequestWithBucketID ( "POST" , EndpointChannelMessagesBulkDelete ( channelID ) , data , EndpointChannelMessagesBulkDelete ( channelID ) )
return
}
// ChannelMessagePin pins a message within a given channel.
// channelID: The ID of a channel.
// messageID: The ID of a message.
func ( s * Session ) ChannelMessagePin ( channelID , messageID string ) ( err error ) {
_ , err = s . RequestWithBucketID ( "PUT" , EndpointChannelMessagePin ( channelID , messageID ) , nil , EndpointChannelMessagePin ( channelID , "" ) )
return
}
// ChannelMessageUnpin unpins a message within a given channel.
// channelID: The ID of a channel.
// messageID: The ID of a message.
func ( s * Session ) ChannelMessageUnpin ( channelID , messageID string ) ( err error ) {
_ , err = s . RequestWithBucketID ( "DELETE" , EndpointChannelMessagePin ( channelID , messageID ) , nil , EndpointChannelMessagePin ( channelID , "" ) )
return
}
// ChannelMessagesPinned returns an array of Message structures for pinned messages
// within a given channel
// channelID : The ID of a Channel.
func ( s * Session ) ChannelMessagesPinned ( channelID string ) ( st [ ] * Message , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointChannelMessagesPins ( channelID ) , nil , EndpointChannelMessagesPins ( channelID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// ChannelFileSend sends a file to the given channel.
// channelID : The ID of a Channel.
// name: The name of the file.
// io.Reader : A reader for the file contents.
func ( s * Session ) ChannelFileSend ( channelID , name string , r io . Reader ) ( * Message , error ) {
return s . ChannelMessageSendComplex ( channelID , & MessageSend { File : & File { Name : name , Reader : r } } )
}
// ChannelFileSendWithMessage sends a file to the given channel with an message.
// DEPRECATED. Use ChannelMessageSendComplex instead.
// channelID : The ID of a Channel.
// content: Optional Message content.
// name: The name of the file.
// io.Reader : A reader for the file contents.
func ( s * Session ) ChannelFileSendWithMessage ( channelID , content string , name string , r io . Reader ) ( * Message , error ) {
return s . ChannelMessageSendComplex ( channelID , & MessageSend { File : & File { Name : name , Reader : r } , Content : content } )
}
// ChannelInvites returns an array of Invite structures for the given channel
// channelID : The ID of a Channel
func ( s * Session ) ChannelInvites ( channelID string ) ( st [ ] * Invite , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointChannelInvites ( channelID ) , nil , EndpointChannelInvites ( channelID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// ChannelInviteCreate creates a new invite for the given channel.
// channelID : The ID of a Channel
// i : An Invite struct with the values MaxAge, MaxUses and Temporary defined.
func ( s * Session ) ChannelInviteCreate ( channelID string , i Invite ) ( st * Invite , err error ) {
data := struct {
MaxAge int ` json:"max_age" `
MaxUses int ` json:"max_uses" `
Temporary bool ` json:"temporary" `
2018-11-12 23:02:07 +00:00
Unique bool ` json:"unique" `
} { i . MaxAge , i . MaxUses , i . Temporary , i . Unique }
2018-08-06 19:10:54 +00:00
body , err := s . RequestWithBucketID ( "POST" , EndpointChannelInvites ( channelID ) , data , EndpointChannelInvites ( channelID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// ChannelPermissionSet creates a Permission Override for the given channel.
// NOTE: This func name may changed. Using Set instead of Create because
// you can both create a new override or update an override with this function.
func ( s * Session ) ChannelPermissionSet ( channelID , targetID , targetType string , allow , deny int ) ( err error ) {
data := struct {
ID string ` json:"id" `
Type string ` json:"type" `
Allow int ` json:"allow" `
Deny int ` json:"deny" `
} { targetID , targetType , allow , deny }
_ , err = s . RequestWithBucketID ( "PUT" , EndpointChannelPermission ( channelID , targetID ) , data , EndpointChannelPermission ( channelID , "" ) )
return
}
// ChannelPermissionDelete deletes a specific permission override for the given channel.
// NOTE: Name of this func may change.
func ( s * Session ) ChannelPermissionDelete ( channelID , targetID string ) ( err error ) {
_ , err = s . RequestWithBucketID ( "DELETE" , EndpointChannelPermission ( channelID , targetID ) , nil , EndpointChannelPermission ( channelID , "" ) )
return
}
// ------------------------------------------------------------------------------------------------
// Functions specific to Discord Invites
// ------------------------------------------------------------------------------------------------
// Invite returns an Invite structure of the given invite
// inviteID : The invite code
func ( s * Session ) Invite ( inviteID string ) ( st * Invite , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointInvite ( inviteID ) , nil , EndpointInvite ( "" ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
2018-11-12 23:02:07 +00:00
// InviteWithCounts returns an Invite structure of the given invite including approximate member counts
// inviteID : The invite code
func ( s * Session ) InviteWithCounts ( inviteID string ) ( st * Invite , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointInvite ( inviteID ) + "?with_counts=true" , nil , EndpointInvite ( "" ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
2018-08-06 19:10:54 +00:00
// InviteDelete deletes an existing invite
// inviteID : the code of an invite
func ( s * Session ) InviteDelete ( inviteID string ) ( st * Invite , err error ) {
body , err := s . RequestWithBucketID ( "DELETE" , EndpointInvite ( inviteID ) , nil , EndpointInvite ( "" ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// InviteAccept accepts an Invite to a Guild or Channel
// inviteID : The invite code
func ( s * Session ) InviteAccept ( inviteID string ) ( st * Invite , err error ) {
body , err := s . RequestWithBucketID ( "POST" , EndpointInvite ( inviteID ) , nil , EndpointInvite ( "" ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// ------------------------------------------------------------------------------------------------
// Functions specific to Discord Voice
// ------------------------------------------------------------------------------------------------
// VoiceRegions returns the voice server regions
func ( s * Session ) VoiceRegions ( ) ( st [ ] * VoiceRegion , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointVoiceRegions , nil , EndpointVoiceRegions )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// VoiceICE returns the voice server ICE information
func ( s * Session ) VoiceICE ( ) ( st * VoiceICE , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointVoiceIce , nil , EndpointVoiceIce )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// ------------------------------------------------------------------------------------------------
// Functions specific to Discord Websockets
// ------------------------------------------------------------------------------------------------
// Gateway returns the websocket Gateway address
func ( s * Session ) Gateway ( ) ( gateway string , err error ) {
response , err := s . RequestWithBucketID ( "GET" , EndpointGateway , nil , EndpointGateway )
if err != nil {
return
}
temp := struct {
URL string ` json:"url" `
} { }
err = unmarshal ( response , & temp )
if err != nil {
return
}
gateway = temp . URL
// Ensure the gateway always has a trailing slash.
// MacOS will fail to connect if we add query params without a trailing slash on the base domain.
if ! strings . HasSuffix ( gateway , "/" ) {
gateway += "/"
}
return
}
// GatewayBot returns the websocket Gateway address and the recommended number of shards
func ( s * Session ) GatewayBot ( ) ( st * GatewayBotResponse , err error ) {
response , err := s . RequestWithBucketID ( "GET" , EndpointGatewayBot , nil , EndpointGatewayBot )
if err != nil {
return
}
err = unmarshal ( response , & st )
if err != nil {
return
}
// Ensure the gateway always has a trailing slash.
// MacOS will fail to connect if we add query params without a trailing slash on the base domain.
if ! strings . HasSuffix ( st . URL , "/" ) {
st . URL += "/"
}
return
}
// Functions specific to Webhooks
// WebhookCreate returns a new Webhook.
// channelID: The ID of a Channel.
// name : The name of the webhook.
// avatar : The avatar of the webhook.
func ( s * Session ) WebhookCreate ( channelID , name , avatar string ) ( st * Webhook , err error ) {
data := struct {
Name string ` json:"name" `
Avatar string ` json:"avatar,omitempty" `
} { name , avatar }
body , err := s . RequestWithBucketID ( "POST" , EndpointChannelWebhooks ( channelID ) , data , EndpointChannelWebhooks ( channelID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// ChannelWebhooks returns all webhooks for a given channel.
// channelID: The ID of a channel.
func ( s * Session ) ChannelWebhooks ( channelID string ) ( st [ ] * Webhook , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointChannelWebhooks ( channelID ) , nil , EndpointChannelWebhooks ( channelID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// GuildWebhooks returns all webhooks for a given guild.
// guildID: The ID of a Guild.
func ( s * Session ) GuildWebhooks ( guildID string ) ( st [ ] * Webhook , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointGuildWebhooks ( guildID ) , nil , EndpointGuildWebhooks ( guildID ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// Webhook returns a webhook for a given ID
// webhookID: The ID of a webhook.
func ( s * Session ) Webhook ( webhookID string ) ( st * Webhook , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointWebhook ( webhookID ) , nil , EndpointWebhooks )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// WebhookWithToken returns a webhook for a given ID
// webhookID: The ID of a webhook.
// token : The auth token for the webhook.
func ( s * Session ) WebhookWithToken ( webhookID , token string ) ( st * Webhook , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointWebhookToken ( webhookID , token ) , nil , EndpointWebhookToken ( "" , "" ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// WebhookEdit updates an existing Webhook.
// webhookID: The ID of a webhook.
// name : The name of the webhook.
// avatar : The avatar of the webhook.
2018-11-12 23:02:07 +00:00
func ( s * Session ) WebhookEdit ( webhookID , name , avatar , channelID string ) ( st * Role , err error ) {
2018-08-06 19:10:54 +00:00
data := struct {
2018-11-12 23:02:07 +00:00
Name string ` json:"name,omitempty" `
Avatar string ` json:"avatar,omitempty" `
ChannelID string ` json:"channel_id,omitempty" `
} { name , avatar , channelID }
2018-08-06 19:10:54 +00:00
body , err := s . RequestWithBucketID ( "PATCH" , EndpointWebhook ( webhookID ) , data , EndpointWebhooks )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// WebhookEditWithToken updates an existing Webhook with an auth token.
// webhookID: The ID of a webhook.
// token : The auth token for the webhook.
// name : The name of the webhook.
// avatar : The avatar of the webhook.
func ( s * Session ) WebhookEditWithToken ( webhookID , token , name , avatar string ) ( st * Role , err error ) {
data := struct {
Name string ` json:"name,omitempty" `
Avatar string ` json:"avatar,omitempty" `
} { name , avatar }
body , err := s . RequestWithBucketID ( "PATCH" , EndpointWebhookToken ( webhookID , token ) , data , EndpointWebhookToken ( "" , "" ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// WebhookDelete deletes a webhook for a given ID
// webhookID: The ID of a webhook.
func ( s * Session ) WebhookDelete ( webhookID string ) ( err error ) {
_ , err = s . RequestWithBucketID ( "DELETE" , EndpointWebhook ( webhookID ) , nil , EndpointWebhooks )
return
}
// WebhookDeleteWithToken deletes a webhook for a given ID with an auth token.
// webhookID: The ID of a webhook.
// token : The auth token for the webhook.
func ( s * Session ) WebhookDeleteWithToken ( webhookID , token string ) ( st * Webhook , err error ) {
body , err := s . RequestWithBucketID ( "DELETE" , EndpointWebhookToken ( webhookID , token ) , nil , EndpointWebhookToken ( "" , "" ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// WebhookExecute executes a webhook.
// webhookID: The ID of a webhook.
// token : The auth token for the webhook
2019-08-26 21:47:50 +00:00
// wait : Wait for server to confirm the message arrival
//
// If `wait` is `false`, the returned *Message is always empty, because server
// does not provide the response data.
func ( s * Session ) WebhookExecute ( webhookID , token string , wait bool , data * WebhookParams ) ( st * Message , err error ) {
2018-08-06 19:10:54 +00:00
uri := EndpointWebhookToken ( webhookID , token )
if wait {
uri += "?wait=true"
}
2019-08-26 21:47:50 +00:00
var response [ ] byte
if data . File != nil {
body := & bytes . Buffer { }
bodywriter := multipart . NewWriter ( body )
2018-08-06 19:10:54 +00:00
2019-08-26 21:47:50 +00:00
var payload [ ] byte
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
}
{
file := data . File
h := make ( textproto . MIMEHeader )
h . Set ( "Content-Disposition" , fmt . Sprintf ( ` form-data; name="file"; filename="%s" ` , 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
}
response , err = s . request ( "POST" , uri , bodywriter . FormDataContentType ( ) , body . Bytes ( ) , EndpointWebhookToken ( "" , "" ) , 0 )
} else {
response , err = s . RequestWithBucketID ( "POST" , uri , data , EndpointWebhookToken ( "" , "" ) )
}
if err != nil {
return
}
if ! wait {
return
}
err = unmarshal ( response , & st )
2018-08-06 19:10:54 +00:00
return
}
// MessageReactionAdd creates an emoji reaction to a message.
// channelID : The channel ID.
// messageID : The message ID.
// emojiID : Either the unicode emoji for the reaction, or a guild emoji identifier.
func ( s * Session ) MessageReactionAdd ( channelID , messageID , emojiID string ) error {
_ , err := s . RequestWithBucketID ( "PUT" , EndpointMessageReaction ( channelID , messageID , emojiID , "@me" ) , nil , EndpointMessageReaction ( channelID , "" , "" , "" ) )
return err
}
// MessageReactionRemove deletes an emoji reaction to a message.
// channelID : The channel ID.
// messageID : The message ID.
// emojiID : Either the unicode emoji for the reaction, or a guild emoji identifier.
// userID : @me or ID of the user to delete the reaction for.
func ( s * Session ) MessageReactionRemove ( channelID , messageID , emojiID , userID string ) error {
_ , err := s . RequestWithBucketID ( "DELETE" , EndpointMessageReaction ( channelID , messageID , emojiID , userID ) , nil , EndpointMessageReaction ( channelID , "" , "" , "" ) )
return err
}
// MessageReactionsRemoveAll deletes all reactions from a message
// channelID : The channel ID
// messageID : The message ID.
func ( s * Session ) MessageReactionsRemoveAll ( channelID , messageID string ) error {
_ , err := s . RequestWithBucketID ( "DELETE" , EndpointMessageReactionsAll ( channelID , messageID ) , nil , EndpointMessageReactionsAll ( channelID , messageID ) )
return err
}
// MessageReactions gets all the users reactions for a specific emoji.
// channelID : The channel ID.
// messageID : The message ID.
// emojiID : Either the unicode emoji for the reaction, or a guild emoji identifier.
// limit : max number of users to return (max 100)
func ( s * Session ) MessageReactions ( channelID , messageID , emojiID string , limit int ) ( st [ ] * User , err error ) {
uri := EndpointMessageReactions ( channelID , messageID , emojiID )
v := url . Values { }
if limit > 0 {
v . Set ( "limit" , strconv . Itoa ( limit ) )
}
if len ( v ) > 0 {
2018-11-12 23:02:07 +00:00
uri += "?" + v . Encode ( )
2018-08-06 19:10:54 +00:00
}
body , err := s . RequestWithBucketID ( "GET" , uri , nil , EndpointMessageReaction ( channelID , "" , "" , "" ) )
if err != nil {
return
}
err = unmarshal ( body , & st )
return
}
// ------------------------------------------------------------------------------------------------
// Functions specific to user notes
// ------------------------------------------------------------------------------------------------
// UserNoteSet sets the note for a specific user.
func ( s * Session ) UserNoteSet ( userID string , message string ) ( err error ) {
data := struct {
Note string ` json:"note" `
} { message }
_ , err = s . RequestWithBucketID ( "PUT" , EndpointUserNotes ( userID ) , data , EndpointUserNotes ( "" ) )
return
}
// ------------------------------------------------------------------------------------------------
// Functions specific to Discord Relationships (Friends list)
// ------------------------------------------------------------------------------------------------
// RelationshipsGet returns an array of all the relationships of the user.
func ( s * Session ) RelationshipsGet ( ) ( r [ ] * Relationship , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointRelationships ( ) , nil , EndpointRelationships ( ) )
if err != nil {
return
}
err = unmarshal ( body , & r )
return
}
// relationshipCreate creates a new relationship. (I.e. send or accept a friend request, block a user.)
// relationshipType : 1 = friend, 2 = blocked, 3 = incoming friend req, 4 = sent friend req
func ( s * Session ) relationshipCreate ( userID string , relationshipType int ) ( err error ) {
data := struct {
Type int ` json:"type" `
} { relationshipType }
_ , err = s . RequestWithBucketID ( "PUT" , EndpointRelationship ( userID ) , data , EndpointRelationships ( ) )
return
}
// RelationshipFriendRequestSend sends a friend request to a user.
// userID: ID of the user.
func ( s * Session ) RelationshipFriendRequestSend ( userID string ) ( err error ) {
err = s . relationshipCreate ( userID , 4 )
return
}
// RelationshipFriendRequestAccept accepts a friend request from a user.
// userID: ID of the user.
func ( s * Session ) RelationshipFriendRequestAccept ( userID string ) ( err error ) {
err = s . relationshipCreate ( userID , 1 )
return
}
// RelationshipUserBlock blocks a user.
// userID: ID of the user.
func ( s * Session ) RelationshipUserBlock ( userID string ) ( err error ) {
err = s . relationshipCreate ( userID , 2 )
return
}
// RelationshipDelete removes the relationship with a user.
// userID: ID of the user.
func ( s * Session ) RelationshipDelete ( userID string ) ( err error ) {
_ , err = s . RequestWithBucketID ( "DELETE" , EndpointRelationship ( userID ) , nil , EndpointRelationships ( ) )
return
}
// RelationshipsMutualGet returns an array of all the users both @me and the given user is friends with.
// userID: ID of the user.
func ( s * Session ) RelationshipsMutualGet ( userID string ) ( mf [ ] * User , err error ) {
body , err := s . RequestWithBucketID ( "GET" , EndpointRelationshipsMutual ( userID ) , nil , EndpointRelationshipsMutual ( userID ) )
if err != nil {
return
}
err = unmarshal ( body , & mf )
return
}