Browse Source

Major rewrite of REST API to include constants for all endpoints

Bruce Marriner 9 years ago
parent
commit
2448f72489
3 changed files with 169 additions and 161 deletions
  1. 165 157
      restapi.go
  2. 3 3
      session.go
  3. 1 1
      util.go

+ 165 - 157
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()
-
-	// 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
-
-	REGIONS = API + "/voice/regions" // VoiceRegions()
-	ICE     = API + "/voice/ice"     // VoiceIce()
-
-//		: 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`,
-*/
-
+	API      = DISCORD + "/api/"
+	GUILDS   = API + "guilds/"
+	CHANNELS = API + "channels/"
+	USERS    = API + "users/"
+	GATEWAY  = API + "gateway"
+
+	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"
+
+	VOICE   = API + "/voice/"
+	REGIONS = API + VOICE + "regions"
+	ICE     = API + VOICE + "ice"
+
+	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,106 +137,153 @@ 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
+}
 
+// 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) {
+
+	_, err = s.Request("POST", LOGOUT, fmt.Sprintf(`{"token": "%s"}`, s.Token))
 	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) {
+// Gateway returns the a websocket Gateway address
+func (s *Session) Gateway() (gateway string, err error) {
+
+	response, err := s.Request("GET", GATEWAY, ``)
 
-	body, err := s.Request("GET", fmt.Sprintf("%s/%s", USERS, userId), ``)
-	err = json.Unmarshal(body, &user)
+	var temp map[string]interface{}
+	err = json.Unmarshal(response, &temp)
+	gateway = temp["url"].(string)
 	return
 }
 
-func (s *Session) VoiceRegions() (vr []VoiceRegion, err error) {
+// VoiceRegions returns the voice server regions
+func (s *Session) VoiceRegions() (st []VoiceRegion, err error) {
 
 	body, err := s.Request("GET", REGIONS, ``)
-	err = json.Unmarshal(body, &vr)
+	err = json.Unmarshal(body, &st)
 	return
 }
 
-func (s *Session) VoiceIce() (ice VoiceIce, err error) {
+// 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, &ice)
+	err = json.Unmarshal(body, &st)
 	return
 }
 
-// Settings returns the settings for a given user
+/***************************************************************************************************
+ * 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) Settings(userId string) (settings Settings, err error) {
+func (s *Session) UserSettings(userId string) (st Settings, err error) {
 
-	body, err := s.Request("GET", fmt.Sprintf("%s/%s/settings", USERS, userId), ``)
-	err = json.Unmarshal(body, &settings)
+	body, err := s.Request("GET", USER_SETTINGS(userId), ``)
+	err = json.Unmarshal(body, &st)
 	return
 }
 
-// PrivateChannels returns an array of Channel structures for all private
+// UserChannels 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)
+// 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
 }
 
-// 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)
+// 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
 }
 
-// add one to get specific server by ID, or enhance the above with an ID field.
-// GET http://discordapp.com/api/guilds/ID#
+/***************************************************************************************************
+ * 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) {
 
-// 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", GUILD(guildId), ``)
+	err = json.Unmarshal(body, &st)
+	return
+}
 
-	body, err := s.Request("GET", fmt.Sprintf("%s/%d/members", GUILDS, serverId), ``)
-	err = json.Unmarshal(body, &members)
+// 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
 }
 
-// Channels returns an array of Channel structures for all channels of a given
-// server.
-func (s *Session) Channels(Id int) (channels []Channel, err error) {
+// 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", fmt.Sprintf("%s/%d/channels", GUILDS, serverId), ``)
-	body, err := s.Request("GET", GUILD_CHANNELS(Id), ``)
-	err = json.Unmarshal(body, &channels)
+	body, err := s.Request("GET", GUILD_CHANNELS(guildId), ``)
+	err = json.Unmarshal(body, &st)
 
 	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#
+/***************************************************************************************************
+ * 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
+}
 
-// 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) {
+// 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
+	var urlStr string = ""
 
 	if limit > 0 {
-		urlStr = fmt.Sprintf("%s/%d/messages?limit=%d", CHANNELS, channelId, limit)
+		urlStr = fmt.Sprintf("?limit=%d", 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)
+			urlStr = fmt.Sprintf("?after=%d", afterId)
 		}
 	}
 
@@ -255,49 +291,21 @@ func (s *Session) Messages(channelId int, limit int, beforeId int, afterId int)
 		if urlStr != "" {
 			urlStr = urlStr + fmt.Sprintf("&before=%d", beforeId)
 		} else {
-			urlStr = fmt.Sprintf("%s/%d/messages?after=%d", CHANNELS, channelId, beforeId)
+			urlStr = fmt.Sprintf("?before=%d", 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
-func (s *Session) Gateway() (gateway string, err error) {
-
-	response, err := s.Request("GET", GATEWAY, ``)
-
-	var temp map[string]interface{}
-	err = json.Unmarshal(response, &temp)
-	gateway = temp["url"].(string)
+	body, err := s.Request("GET", CHANNEL_MESSAGES(channelId)+urlStr, ``)
+	err = json.Unmarshal(body, &st)
 	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) {
-
-	_, err = s.Request("POST", LOGOUT, fmt.Sprintf(`{"token": "%s"}`, s.Token))
+// 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
 }

+ 3 - 3
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
 }

+ 1 - 1
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()))
 }