diff --git a/restapi.go b/restapi.go index d6b8b39..bd0a7f8 100644 --- a/restapi.go +++ b/restapi.go @@ -15,84 +15,75 @@ import ( "fmt" "io/ioutil" "net/http" - "strconv" + sv "strconv" "time" ) -// Constants of known Discord API Endpoints +// Constants of all known Discord API Endpoints // Please let me know if you know of any others. const ( - // Base URLS DISCORD = "http://discordapp.com" - API = DISCORD + "/api" - GUILDS = API + "/guilds" // Guilds() - CHANNELS = API + "/channels" // Channels() - USERS = API + "/users" // Users() - LOGIN = API + "/auth/login" // Login() - LOGOUT = API + "/auth/logout" // Logout() - GATEWAY = API + "/gateway" // Gateway() + API = DISCORD + "/api/" + GUILDS = API + "guilds/" + CHANNELS = API + "channels/" + USERS = API + "users/" + GATEWAY = API + "gateway" - // Authenticated User Info - AU = USERS + "/@me" - AU_SETTINGS = AU + "/settings" // Call Settings with @me - AU_CHANNELS = AU + "/channels" // Call Channel with @me - AU_GUILDS = AU + "/guilds" // Call Guilds with @me + AUTH = API + "auth/" + LOGIN = API + AUTH + "login" + LOGOUT = API + AUTH + "logout" + VERIFY = API + AUTH + "verify" + VERIFY_RESEND = API + AUTH + "verify/resend" + FORGOT_PASSWORD = API + AUTH + "forgot" + RESET_PASSWORD = API + AUTH + "reset" + REGISTER = API + AUTH + "register" - REGIONS = API + "/voice/regions" // VoiceRegions() - ICE = API + "/voice/ice" // VoiceIce() + VOICE = API + "/voice/" + REGIONS = API + VOICE + "regions" + ICE = API + VOICE + "ice" -// : guildId => `/guilds/${guildId}/channels`, -// GUILD_CHANNELS: guildId => `/guilds/${guildId}/channels`, - -// TODO: Test below -// AU_DEVICES = AU + "/devices" -// AU_CONNECTIONS = AU + "/connections" -// REGISTER = API + "/auth/register" -// INVITE = API + "/invite" -// TRACK = API + "/track" -// SSO = API + "/sso" -// VERIFY = API + "/auth/verify" -// VERIFY_RESEND = API + "/auth/verify/resend" -// FORGOT_PASSWORD = API + "/auth/forgot" -// RESET_PASSWORD = API + "/auth/reset" -// REPORT = API + "/report" -// INTEGRATIONS = API + "/integrations" - -// Need a way to handle these here so the variables can be inserted. -// Maybe defined as functions? -/* - INTEGRATIONS_JOIN: integrationId => `/integrations/${integrationId}/join`, - AVATAR: (userId, hash) => `/users/${userId}/avatars/${hash}.jpg`, - MESSAGES: channelId => `/channels/${channelId}/messages`, - INSTANT_INVITES: channelId => `/channels/${channelId}/invites`, - TYPING: channelId => `/channels/${channelId}/typing`, - CHANNEL_PERMISSIONS: channelId => `/channels/${channelId}/permissions`, - TUTORIAL: `/tutorial`, - TUTORIAL_INDICATORS: `/tutorial/indicators`, - USER_CHANNELS: userId => `/users/${userId}/channels`, - GUILD_CHANNELS: guildId => `/guilds/${guildId}/channels`, - GUILD_MEMBERS: guildId => `/guilds/${guildId}/members`, - GUILD_INTEGRATIONS: guildId => `/guilds/${guildId}/integrations`, - GUILD_BANS: guildId => `/guilds/${guildId}/bans`, - GUILD_ROLES: guildId => `/guilds/${guildId}/roles`, - GUILD_INSTANT_INVITES: guildId => `/guilds/${guildId}/invites`, - GUILD_EMBED: guildId => `/guilds/${guildId}/embed`, - GUILD_PRUNE: guildId => `/guilds/${guildId}/prune`, - GUILD_ICON: (guildId, hash) => `/guilds/${guildId}/icons/${hash}.jpg`, -*/ + TUTORIAL = API + "tutorial/" + TUTORIAL_INDICATORS = TUTORIAL + "indicators" + INVITE = API + "invite" + TRACK = API + "track" + SSO = API + "sso" + REPORT = API + "report" + INTEGRATIONS = API + "integrations" ) -// Almost like the constants above :) Dynamic Variables? +// Almost like the constants above :) Except can't be constants var ( - GUILD_CHANNELS = func(i int) (s string) { - s = GUILDS + "/" + strconv.Itoa(i) + "/channels" - return - } + USER = func(userId string) string { return USERS + userId } + USER_AVATAR = func(userId, hash string) string { return USERS + userId + "/avatars/" + hash + ".jpg" } + USER_SETTINGS = func(userId string) string { return USERS + userId + "/settings" } + USER_GUILDS = func(userId string) string { return USERS + userId + "/guilds" } + USER_CHANNELS = func(userId string) string { return USERS + userId + "/channels" } + USER_DEVICES = func(userId string) string { return USERS + userId + "/devices" } + USER_CONNECTIONS = func(userId string) string { return USERS + userId + "/connections" } + + GUILD = func(guildId int) string { return GUILDS + sv.Itoa(guildId) } + GUILD_CHANNELS = func(guildId int) string { return GUILDS + sv.Itoa(guildId) + "/channels" } + GUILD_MEMBERS = func(guildId int) string { return GUILDS + sv.Itoa(guildId) + "/members" } + GUILD_INTEGRATIONS = func(guildId int) string { return GUILDS + sv.Itoa(guildId) + "/integrations" } + GUILD_BANS = func(guildId int) string { return GUILDS + sv.Itoa(guildId) + "/bans" } + GUILD_ROLES = func(guildId int) string { return GUILDS + sv.Itoa(guildId) + "/roles" } + GUILD_INVITES = func(guildId int) string { return GUILDS + sv.Itoa(guildId) + "/invites" } + GUILD_EMBED = func(guildId int) string { return GUILDS + sv.Itoa(guildId) + "/embed" } + GUILD_PRUNE = func(guildId int) string { return GUILDS + sv.Itoa(guildId) + "/prune" } + GUILD_ICON = func(guildId int, hash string) string { return GUILDS + sv.Itoa(guildId) + "/icons/" + hash + ".jpg" } + + CHANNEL = func(channelId int) string { return CHANNELS + sv.Itoa(channelId) } + CHANNEL_MESSAGES = func(channelId int) string { return CHANNELS + sv.Itoa(channelId) + "/messages" } + CHANNEL_PERMISSIONS = func(channelId int) string { return CHANNELS + sv.Itoa(channelId) + "/permissions" } + CHANNEL_INVITES = func(channelId int) string { return CHANNELS + sv.Itoa(channelId) + "/invites" } + CHANNEL_TYPING = func(channelId int) string { return CHANNELS + sv.Itoa(channelId) + "/typing" } + + INTEGRATIONS_JOIN = func(intId int) string { return API + "integrations/" + sv.Itoa(intId) + "/join" } ) // Request makes a (GET/POST/?) Requests to Discord REST API. -// All the other functions in this file use this function. +// All the other Discord REST Calls in this file use this function. func (s *Session) Request(method, urlStr, body string) (response []byte, err error) { if s.Debug { @@ -129,17 +120,15 @@ func (s *Session) Request(method, urlStr, body string) (response []byte, err err } if s.Debug { - var prettyJSON bytes.Buffer - error := json.Indent(&prettyJSON, response, "", "\t") - if error != nil { - fmt.Print("JSON parse error: ", error) - return - } - fmt.Println("RESPONSE ::\n" + string(prettyJSON.Bytes())) + printJSON(response) } return } +/*************************************************************************************************** + * Functions specific to this session. + */ + // Login asks the Discord server for an authentication token func (s *Session) Login(email string, password string) (token string, err error) { @@ -148,139 +137,20 @@ func (s *Session) Login(email string, password string) (token string, err error) var temp map[string]interface{} err = json.Unmarshal(response, &temp) token = temp["token"].(string) - return } -// Returns the user details of the given userId -// session : An active session connection to Discord -// user : A user Id or name -func (s *Session) Users(userId string) (user User, err error) { +// 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) { - body, err := s.Request("GET", fmt.Sprintf("%s/%s", USERS, userId), ``) - err = json.Unmarshal(body, &user) + _, err = s.Request("POST", LOGOUT, fmt.Sprintf(`{"token": "%s"}`, s.Token)) return } -func (s *Session) VoiceRegions() (vr []VoiceRegion, err error) { - - body, err := s.Request("GET", REGIONS, ``) - err = json.Unmarshal(body, &vr) - return -} - -func (s *Session) VoiceIce() (ice VoiceIce, err error) { - - body, err := s.Request("GET", ICE, ``) - err = json.Unmarshal(body, &ice) - return -} - -// Settings returns the settings for a given user -// This seems to only return a result for "@me" -func (s *Session) Settings(userId string) (settings Settings, err error) { - - body, err := s.Request("GET", fmt.Sprintf("%s/%s/settings", USERS, userId), ``) - err = json.Unmarshal(body, &settings) - return -} - -// PrivateChannels returns an array of Channel structures for all private -// channels for a user -func (s *Session) PrivateChannels(userId string) (channels []Channel, err error) { - - body, err := s.Request("GET", fmt.Sprintf("%s/%s/channels", USERS, userId), ``) - err = json.Unmarshal(body, &channels) - - return -} - -// Guilds returns an array of Guild structures for all servers for a user -func (s *Session) Guilds(userId string) (servers []Guild, err error) { - - body, err := s.Request("GET", fmt.Sprintf("%s/%s/guilds", USERS, userId), ``) - err = json.Unmarshal(body, &servers) - - return -} - -// add one to get specific server by ID, or enhance the above with an ID field. -// GET http://discordapp.com/api/guilds/ID# - -// Members returns an array of Member structures for all members of a given -// server. -func (s *Session) Members(serverId int) (members []Member, err error) { - - body, err := s.Request("GET", fmt.Sprintf("%s/%d/members", GUILDS, serverId), ``) - err = json.Unmarshal(body, &members) - - return -} - -// Channels returns an array of Channel structures for all channels of a given -// server. -func (s *Session) Channels(Id int) (channels []Channel, err error) { - - // body, err := s.Request("GET", fmt.Sprintf("%s/%d/channels", GUILDS, serverId), ``) - body, err := s.Request("GET", GUILD_CHANNELS(Id), ``) - err = json.Unmarshal(body, &channels) - - return -} - -// update above or add a way to get channel by ID. ChannelByName could be handy -// too you know. -// http://discordapp.com/api/channels/ID# - -// Messages returns an array of Message structures for messaages within a given -// channel. limit, beforeId, and afterId can be used to control what messages -// are returned. -func (s *Session) Messages(channelId int, limit int, beforeId int, afterId int) (messages []Message, err error) { - - var urlStr string - - if limit > 0 { - urlStr = fmt.Sprintf("%s/%d/messages?limit=%d", CHANNELS, channelId, limit) - } - - if afterId > 0 { - if urlStr != "" { - urlStr = urlStr + fmt.Sprintf("&after=%d", afterId) - } else { - urlStr = fmt.Sprintf("%s/%d/messages?after=%d", CHANNELS, channelId, afterId) - } - } - - if beforeId > 0 { - if urlStr != "" { - urlStr = urlStr + fmt.Sprintf("&before=%d", beforeId) - } else { - urlStr = fmt.Sprintf("%s/%d/messages?after=%d", CHANNELS, channelId, beforeId) - } - } - - if urlStr == "" { - urlStr = fmt.Sprintf("%s/%d/messages", CHANNELS, channelId) - } - - body, err := s.Request("GET", urlStr, ``) - err = json.Unmarshal(body, &messages) - - return -} - -// SendMessage sends a message to the given channel. -func (s *Session) SendMessage(channelId int, content string) (message Message, err error) { - - var urlStr string = fmt.Sprintf("%s/%d/messages", CHANNELS, channelId) - response, err := s.Request("POST", urlStr, fmt.Sprintf(`{"content":"%s"}`, content)) - err = json.Unmarshal(response, &message) - - return -} - -// Returns the a websocket Gateway address -// session : An active session connection to Discord +// Gateway returns the a websocket Gateway address func (s *Session) Gateway() (gateway string, err error) { response, err := s.Request("GET", GATEWAY, ``) @@ -291,13 +161,151 @@ func (s *Session) Gateway() (gateway string, err error) { return } -// Close ends a session and logs out from the Discord REST API. -// 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) { +// VoiceRegions returns the voice server regions +func (s *Session) VoiceRegions() (st []VoiceRegion, err error) { - _, err = s.Request("POST", LOGOUT, fmt.Sprintf(`{"token": "%s"}`, s.Token)) + body, err := s.Request("GET", REGIONS, ``) + err = json.Unmarshal(body, &st) + return +} + +// VoiceIce returns the voice server ICE information +func (s *Session) VoiceIce() (st VoiceIce, err error) { + + body, err := s.Request("GET", ICE, ``) + err = json.Unmarshal(body, &st) + return +} + +/*************************************************************************************************** + * Functions related to a specific user + */ + +// 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.Request("GET", USER(userId), ``) + err = json.Unmarshal(body, &st) + return +} + +// UserSettings returns the settings for a given user +// userId : A user Id or "@me" which is a shortcut of current user ID +// This seems to only return a result for "@me" +func (s *Session) UserSettings(userId string) (st Settings, err error) { + + body, err := s.Request("GET", USER_SETTINGS(userId), ``) + err = json.Unmarshal(body, &st) + return +} + +// UserChannels returns an array of Channel structures for all private +// channels for a user +// userId : A user Id or "@me" which is a shortcut of current user ID +func (s *Session) UserChannels(userId string) (st []Channel, err error) { + + body, err := s.Request("GET", USER_CHANNELS(userId), ``) + err = json.Unmarshal(body, &st) + return +} + +// UserGuilds returns an array of Guild structures for all guilds for a given user +// userId : A user Id or "@me" which is a shortcut of current user ID +func (s *Session) UserGuilds(userId string) (st []Guild, err error) { + + body, err := s.Request("GET", USER_GUILDS(userId), ``) + err = json.Unmarshal(body, &st) + return +} + +/*************************************************************************************************** + * Functions related to a specific guild + */ + +// Guild returns a Guild structure of a specific Guild. +// guildId : The ID of the Guild you want returend. +func (s *Session) Guild(guildId int) (st []Guild, err error) { + + body, err := s.Request("GET", GUILD(guildId), ``) + err = json.Unmarshal(body, &st) + return +} + +// GuildMembers returns an array of Member structures for all members of a +// given guild. +// guildId : The ID of a Guild. +func (s *Session) GuildMembers(guildId int) (st []Member, err error) { + + body, err := s.Request("GET", GUILD_MEMBERS(guildId), ``) + err = json.Unmarshal(body, &st) + 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 int) (st []Channel, err error) { + + body, err := s.Request("GET", GUILD_CHANNELS(guildId), ``) + err = json.Unmarshal(body, &st) return } + +/*************************************************************************************************** + * Functions related to a specific channel + */ + +// Channel returns a Channel strucutre of a specific Channel. +// channelId : The ID of the Channel you want returend. +func (s *Session) Channel(channelId int) (st Channel, err error) { + body, err := s.Request("GET", CHANNEL(channelId), ``) + err = json.Unmarshal(body, &st) + return +} + +// ChannelMessages returns an array of Message structures for messaages within +// a given channel. +// channelId : The ID of a Channel. +// limit : The number messages that can be returned. +// beforeId : If provided all messages returned will be before given ID. +// afterId : If provided all messages returned will be after given ID. +func (s *Session) ChannelMessages(channelId int, limit int, beforeId int, afterId int) (st []Message, err error) { + + var urlStr string = "" + + if limit > 0 { + urlStr = fmt.Sprintf("?limit=%d", limit) + } + + if afterId > 0 { + if urlStr != "" { + urlStr = urlStr + fmt.Sprintf("&after=%d", afterId) + } else { + urlStr = fmt.Sprintf("?after=%d", afterId) + } + } + + if beforeId > 0 { + if urlStr != "" { + urlStr = urlStr + fmt.Sprintf("&before=%d", beforeId) + } else { + urlStr = fmt.Sprintf("?before=%d", beforeId) + } + } + + body, err := s.Request("GET", CHANNEL_MESSAGES(channelId)+urlStr, ``) + err = json.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 int, content string) (st Message, err error) { + + response, err := s.Request("POST", CHANNEL_MESSAGES(channelId), fmt.Sprintf(`{"content":"%s"}`, content)) + err = json.Unmarshal(response, &st) + return +} diff --git a/session.go b/session.go index d3ea66d..157186c 100644 --- a/session.go +++ b/session.go @@ -65,16 +65,16 @@ type Session struct { * Reference the client.go file for more documentation. */ func (s *Session) Self() (user User, err error) { - user, err = s.Users("@me") + user, err = s.User("@me") return } func (s *Session) MyPrivateChannels() (channels []Channel, err error) { - channels, err = s.PrivateChannels("@me") + channels, err = s.UserChannels("@me") return } func (s *Session) MyGuilds() (servers []Guild, err error) { - servers, err = s.Guilds("@me") + servers, err = s.UserGuilds("@me") return } diff --git a/util.go b/util.go index 149ac61..6e00040 100644 --- a/util.go +++ b/util.go @@ -16,5 +16,5 @@ func printJSON(body []byte) { if error != nil { fmt.Print("JSON parse error: ", error) } - fmt.Println("RESPONSE ::\n" + string(prettyJSON.Bytes())) + fmt.Println(string(prettyJSON.Bytes())) }