-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
da485e7
commit 7c42807
Showing
5 changed files
with
357 additions
and
386 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,37 @@ | ||
# Steam Api Utility | ||
# Steam Gameserver REST API | ||
|
||
A commandline utility for managing Steam Gameserver Tokens through Steam Web API. | ||
A REST API for pulling Steam Gameserver Tokens through Steamworks Web API. | ||
|
||
Its primary target is the IGameServersService Interface, and the code has been built on knowledge from two sources. | ||
Its wraps the IGameServersService Interface, and the code has been built on knowledge from two sources. | ||
A [community made API reference](http://steamwebapi.azurewebsites.net/). | ||
And the [Steamworks Documentation Website](https://partner.steamgames.com/doc/webapi/IGameServersService). | ||
|
||
It currently outputs all returned API JSON data as unquoted Semicolon Separated Values with Headers. | ||
It returns tokens as text/plain on the following URL: | ||
|
||
The Utility currently supports three features. | ||
> [GET] /token/{appID}/{memo} | ||
* Create new Gameserver Account with accompanying Token | ||
* List Gameserver Accounts | ||
* Delete Gameserver Account by ID (SteamID) | ||
* **appID** is the Steam Application ID (e.g. 740 for CSGO dedicated server) | ||
* **memo** is a note that uniquely identifies a gameserver | ||
|
||
If the utility receives an X-error_message Response Header, it will log the message to console, and exit. | ||
The library it uses to communicate with Steamworks Web API is [nested in this project](steam/README.md). | ||
|
||
## Errors | ||
|
||
Errors from the Steamworks Web API will be forwarded as JSON objects. | ||
|
||
> { "error": "some error happened" } | ||
## Build | ||
|
||
```sh | ||
# Windows | ||
GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o steam-api.exe main.go | ||
GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o steam-api.exe main.go app.go | ||
|
||
# Linux | ||
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o steam-api main.go | ||
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o steam-api main.go app.go | ||
|
||
# OSX | ||
GOOS=darwin go build -ldflags="-s -w" -o steam-api main.go | ||
GOOS=darwin go build -ldflags="-s -w" -o steam-api main.go app.go | ||
``` | ||
|
||
All binary releases of steam-api are compressed with `upx --brute`. | ||
Optionally, you can cut down binary size with `upx --brute`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"log" | ||
"net/http" | ||
"os" | ||
"strconv" | ||
|
||
"github.com/gorilla/mux" | ||
"github.com/npflan/steam-api/steam" | ||
) | ||
|
||
// App contains references to global necessities | ||
type App struct { | ||
Router *mux.Router | ||
Log Log | ||
} | ||
|
||
// Log is a modifiable endpoint | ||
type Log struct { | ||
Error *log.Logger | ||
Info *log.Logger | ||
} | ||
|
||
const defaultLogFormat = log.Ldate | log.Ltime | log.Lmicroseconds | log.Lshortfile | log.LUTC | ||
|
||
// Run server on specific interface | ||
func (a *App) Run(addr string) { | ||
a.registerRoutes() | ||
// set default log format if no custom format present | ||
if a.Log.Info == nil { | ||
log.New(os.Stdout, "INFO: ", defaultLogFormat) | ||
} | ||
if a.Log.Error == nil { | ||
log.New(os.Stderr, "ERROR: ", defaultLogFormat) | ||
} | ||
log.Fatal(http.ListenAndServe(addr, a.Router)) | ||
} | ||
|
||
func (a *App) registerRoutes() { | ||
a.Router = mux.NewRouter().StrictSlash(true) | ||
a.Router.HandleFunc("/", a.getHome).Methods("GET") | ||
a.Router.HandleFunc("/token/{appID}/{memo}", a.pullToken).Methods("GET") | ||
} | ||
|
||
// RespondWithJSON uses a struct, for a JSON response. | ||
func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) { | ||
response, _ := json.Marshal(payload) | ||
|
||
w.Header().Set("Content-Type", "application/json") | ||
w.WriteHeader(code) | ||
w.Write(response) | ||
} | ||
|
||
// RespondWithText returns text/plain. | ||
func respondWithText(w http.ResponseWriter, code int, payload string) { | ||
w.Header().Set("Content-Type", "text/plain") | ||
w.WriteHeader(code) | ||
w.Write([]byte(payload)) | ||
} | ||
|
||
// RespondWithError standardizes error messages, through the use of RespondWithJSON. | ||
func respondWithError(w http.ResponseWriter, code int, message string) { | ||
respondWithJSON(w, code, map[string]string{"error": message}) | ||
} | ||
|
||
func (a *App) getHome(w http.ResponseWriter, r *http.Request) { | ||
|
||
} | ||
|
||
func (a *App) pullToken(w http.ResponseWriter, r *http.Request) { | ||
vars := mux.Vars(r) | ||
if _, ok := vars["appID"]; !ok { | ||
respondWithError(w, http.StatusBadRequest, "Missing appID") | ||
return | ||
} | ||
if _, ok := vars["memo"]; !ok { | ||
respondWithError(w, http.StatusBadRequest, "Missing memo") | ||
return | ||
} | ||
|
||
appID, err := strconv.Atoi(vars["appID"]) | ||
if err != nil { | ||
respondWithError(w, http.StatusBadRequest, "bad appID") | ||
return | ||
} | ||
|
||
accounts, err := steam.GetAccountList() | ||
if err != nil { | ||
respondWithError(w, http.StatusInternalServerError, "Unable to list existing tokens") | ||
return | ||
} | ||
|
||
// Check for existing account | ||
var account steam.Account | ||
for _, acct := range accounts { | ||
if acct.Memo == vars["memo"] && int(acct.AppID) == appID { | ||
account = acct | ||
break | ||
} | ||
} | ||
|
||
// Create new if not found | ||
if account.SteamID == "" { | ||
account, err = steam.CreateAccount(appID, vars["memo"]) | ||
} | ||
if err != nil { | ||
respondWithError(w, http.StatusInternalServerError, err.Error()) | ||
return | ||
} | ||
|
||
// Refresh token if found and expired | ||
if account.IsExpired == true { | ||
account, err = steam.ResetLoginToken(account.SteamID) | ||
} | ||
if err != nil { | ||
respondWithError(w, http.StatusInternalServerError, err.Error()) | ||
} | ||
|
||
respondWithText(w, http.StatusOK, account.LoginToken) | ||
} |
Oops, something went wrong.