Skip to content

Instantly share code, notes, and snippets.

@dzhu
Last active November 14, 2020 10:18
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save dzhu/d6999d126d0182973b5c to your computer and use it in GitHub Desktop.
Save dzhu/d6999d126d0182973b5c to your computer and use it in GitHub Desktop.
description of the HTTP endpoints available from Screeps, and a Python wrapper to access them (requires requests library)
import json
from base64 import b64decode
from collections import OrderedDict
from cStringIO import StringIO
from gzip import GzipFile
import requests
## Python before 2.7.10 or so has somewhat broken SSL support that throws a warning; suppress it
import warnings; warnings.filterwarnings('ignore', message='.*true sslcontext object.*')
class ScreepsConnection(object):
def req(self, func, path, **args):
r = func(self.prefix + path, headers={'X-Token': self.token, 'X-Username': self.token}, **args)
self.token = r.headers.get('X-Token', self.token)
try:
return json.loads(r.text, object_pairs_hook=OrderedDict)
except ValueError:
print 'JSON failure:', r.text
return None
def get(self, _path, **args): return self.req(requests.get, _path, params=args)
def post(self, _path, **args): return self.req(requests.post, _path, json=args)
def __init__(self, u=None, p=None, ptr=False):
self.ptr = ptr
self.prefix = 'https://screeps.com/ptr/api/' if ptr else 'https://screeps.com/api/'
self.token = None
if u is not None and p is not None:
self.token = self.post('auth/signin', email=u, password=p)['token']
#### miscellaneous user methods
def me(self):
return self.get('auth/me')
def overview(self, interval=8, statName='energyHarvested'):
return self.get('user/overview', interval=interval, statName=statName)
def user_find(self, username):
return self.get('user/find', username=username)
def memory(self, path=''):
ret = self.get('user/memory', path=path)
if 'data' in ret:
ret['data'] = json.load(GzipFile(fileobj=StringIO(b64decode(ret['data'][3:]))))
return ret
def set_memory(self, path, value):
return self.post('user/memory', path=path, value=value)
def console(self, cmd):
return self.post('user/console', expression=cmd)
#### room info methods
def room_overview(self, room, interval=8):
return self.get('game/room-overview', interval=interval, room=room)
def room_terrain(self, room, encoded=False):
return self.get('game/room-terrain', room=room, encoded=('1' if encoded else None))
def room_status(self, room):
return self.get('game/room-status', room=room)
#### leaderboard methods
## omit season to get current season
def board_list(self, limit=10, offset=0, season=None, mode='world'):
if season is None:
## find current season (the one with max start time among all seasons)
seasons = self.board_seasons['seasons']
season = max(seasons, key=lambda s: s['date'])['_id']
ret = self.get('leaderboard/list', limit=limit, offset=offset, mode=mode, season=season)
for d in ret['list']:
d['username'] = ret['users'][d['user']]['username']
return ret
## omit season to get all seasons
def board_find(self, username, season=None, mode='world'):
return self.get('leaderboard/find', mode=mode, season=season, username=username)
def board_seasons(self):
return self.get('leaderboard/seasons')
#### messaging methods
def msg_index(self):
return self.get('user/messages/index')
def msg_list(self, respondent):
return self.get('user/messages/list', respondent=respondent)
def msg_send(self, respondent, text):
return self.post('user/messages/send', respondent=respondent, text=text)
#### world manipulation methods
def gen_unique_name(self, type):
return self.post('game/gen-unique-object-name', type=type)
def flag_create(self, room, x, y, name=None, color='white', secondaryColor=None):
if name is None:
name = self.gen_unique_name('flag')['name']
if secondaryColor is None:
secondaryColor = color
return self.post('game/create-flag', room=room, x=x, y=y, name=name, color=color, secondaryColor=secondaryColor)
def flag_change_pos(self, _id, room, x, y):
return self.post('game/change-flag', _id=_id, room=room, x=x, y=y)
def flag_change_color(self, _id, color, secondaryColor=None):
if secondaryColor is None:
secondaryColor = color
return self.post('game/change-flag-color', _id=_id, color=color, secondaryColor=secondaryColor)
def create_site(self, typ, room, x, y):
return self.post('game/create-construction', structureType=typ, room=room, x=x, y=y)
#### other methods
def time(self):
return self.get('game/time')['time']
def map_stats(self, rooms, statName):
return self.post('game/map-stats', rooms=rooms, statName=statName)
def history(self, room, tick):
return self.get('../room-history/%s/%s.json' % (room, tick - (tick % 20)))
def activate_ptr(self):
if self.ptr:
return self.post('user/activate-ptr')

Start by sending a request to auth/signin with your e-mail address and password in a JSON object in the POST data. The response JSON object contains a token (a hex string); remember that value. Each time you make a request that requires authentication (the leaderboard and terrain ones, at least, don't), send the most recent token value as both the X-Token and X-Username headers. The response will contain a new token value in the X-Token header with which you should replace your saved token. (You can send the token on every request and just check for a new one in the response, so you don't have to know exactly which calls require authentication.)

Example request parameters are given below, along with schemata for the server's responses.

Memory and console endpoints are from bzy-xyz.

You can access the PTR by changing screeps.com to screeps.com/ptr in all URLs.

Enumeration values

When an endpoint takes interval or statName as an argument, the valid values are the ones listed below.

  • interval: 8, 180, 1440
  • statName: creepsLost, creepsProduced, energyConstruction, energyControl, energyCreeps, energyHarvested

Authentication

  • [POST] https://screeps.com/api/auth/signin
    • post data: { email, password }
    • response: { ok, token }

Room information

  • https://screeps.com/api/game/room-overview?interval=8&room=E1N8

    • { ok, owner: { username, badge: { type, color1, color2, color3, param, flip } }, stats: { energyHarvested: [ { value, endTime } ], energyConstruction: [ { value, endTime } ], energyCreeps: [ { value, endTime } ], energyControl: [ { value, endTime } ], creepsProduced: [ { value, endTime } ], creepsLost: [ { value, endTime } ] }, statsMax: { energy1440, energyCreeps1440, energy8, energyControl8, creepsLost180, energyHarvested8, energy180, energyConstruction180, creepsProduced8, energyControl1440, energyCreeps8, energyHarvested1440, creepsLost1440, energyConstruction1440, energyHarvested180, creepsProduced180, creepsProduced1440, energyCreeps180, energyControl180, energyConstruction8, creepsLost8 } }
  • https://screeps.com/api/game/room-terrain?room=E1N8

    • { ok, terrain: [ { room, x, y, type } ] }
    • type in each element of terrain can be "wall" or "swamp"
  • https://screeps.com/api/game/room-terrain?room=E1N8&encoded=1

    • { ok, terrain: [ { _id, room, terrain, type } ] }
    • terrain is a string of digits, giving the terrain left-to-right and top-to-bottom
    • 0: plain, 1: wall, 2: swamp, 3: also wall
    • encoded argument can be anything non-empty
  • https://screeps.com/api/game/room-status?room=E1N8

    • { _id, status, novice }
    • status can at least be "normal" or "out of borders"
    • if the room is in a novice area, novice will contain the Unix timestamp of the end of the protection (otherwise it is absent)

Leaderboard

  • https://screeps.com/api/leaderboard/seasons

    • { ok, seasons: [ { _id, name, date } ] }
    • the _id returned here is used for the season name in the other leaderboard calls
  • https://screeps.com/api/leaderboard/find?mode=world&season=2015-09&username=danny

    • { ok, _id, season, user, score, rank }
    • user (not _id) is the user's _id, as returned by me and user/find
    • rank is 0-based
  • https://screeps.com/api/leaderboard/find?mode=world&username=danny

    • { ok, list: [ _id, season, user, score, rank ] }
    • lists ranks in all seasons
  • https://screeps.com/api/leaderboard/list?limit=10&mode=world&offset=10&season=2015-09

    • { ok, list: [ { _id, season, user, score, rank } ], count, users: { <user's _id>: { _id, username, badge: { type, color1, color2, color3, param, flip }, gcl } } }

Messages

  • https://screeps.com/api/user/messages/index

    • { ok, messages: [ { _id, message: { _id, user, respondent, date, type, text, unread } } ], users: { <user's _id>: { _id, username, badge: { type, color1, color2, color3, param, flip } } } }
  • https://screeps.com/api/user/messages/list?respondent=55605a6654db1fa952de8d90

    • { ok, messages: [ { _id, date, type, text, unread } ] }
  • [POST] https://screeps.com/api/user/messages/send

    • post data: { respondent, text }
    • respondent is the long _id of the user, not the username
    • response: { ok }
  • https://screeps.com/api/user/messages/unread-count

    • { ok, count }

User information

  • https://screeps.com/api/auth/me

    • { ok, _id, email, username, cpu, badge: { type, color1, color2, color3, param, flip }, password, notifyPrefs: { sendOnline, errorsInterval, disabledOnMessages, disabled, interval }, gcl, credits, lastChargeTime, lastTweetTime, github: { id, username }, twitter: { username, followers_count } }
  • https://screeps.com/api/user/find?username=danny

    • { ok, user: { _id, username, badge: { type, color1, color2, color3, param, flip }, gcl } }
  • https://screeps.com/api/user/overview?interval=1440&statName=energyControl

    • { ok, rooms: [ <room name> ], stats: { <room name>: [ { value, endTime } ] }, statsMax }
  • https://screeps.com/api/user/respawn-prohibited-rooms

    • { ok, rooms: [ <room name> ] }
  • https://screeps.com/api/user/world-status

    • { ok, status }
    • status is usually "normal"; it's unknown what else it can be
  • https://screeps.com/api/user/world-start-room

    • { ok, room: [ <room name> ] }
    • room is an array, but seems to always contain only one element
  • https://screeps.com/api/xsolla/user

    • { ok, active }
    • active is the Unix timestamp of the end of your current subscribed time
  • [POST] https://screeps.com/api/user/console

    • post data: { expression }
    • response: { ok, result: { ok, n }, ops: [ { user, expression, _id } ], insertedCount, insertedIds: [ <mongodb id> ] }
  • https://screeps.com/api/user/memory?path=flags.Flag1

    • { ok, data }
    • data is the string gz: followed by base64-encoded gzipped JSON encoding of the requested memory path
    • the path may be empty or absent to retrieve all of Memory
  • [POST] https://screeps.com/api/user/memory

    • post data: { path, value }
    • response: { ok, result: { ok, n }, ops: [ { user, expression, hidden } ], data, insertedCount, insertedIds }

Manipulating the game world

  • [POST] https://screeps.com/api/game/gen-unique-object-name

    • post data: { type }
    • response: { ok, name }
    • type can be at least "flag" or "spawn"
  • [POST] https://screeps.com/api/game/create-flag

    • post data: { room, x, y, name, color, secondaryColor }
    • response: { ok, result: { nModified, ok, upserted: [ { index, _id } ], n }, connection: { host, id, port } }
    • if the name is new, result.upserted[0]._id is the game id of the created flag
    • if not, this moves the flag and the response does not contain the id (but the id doesn't change)
    • connection looks like some internal MongoDB thing that is irrelevant to us
  • [POST] https://screeps.com/api/game/change-flag

    • post data: { _id, room, x, y }
    • response: { ok, result: { nModified, ok, n }, connection: { host, id, port } }
  • [POST] https://screeps.com/api/game/change-flag-color

    • post data: { _id, color, secondaryColor }
    • response: { ok, result: { nModified, ok, n }, connection: { host, id, port } }
  • [POST] https://screeps.com/api/game/add-object-intent

    • post data: { _id, room, name, intent }
    • response: { ok, result: { nModified, ok, upserted: [ { index, _id } ], n }, connection: { host, id, port } }
    • _id is the game id of the object to affect (except for destroying structures), room is the name of the room it's in
    • this method is used for a variety of actions, depending on the name and intent parameters
      • remove flag: name = "remove", intent = {}
      • destroy structure: _id = "room", name = "destroyStructure", intent = [ {id: <structure id>, roomName, <room name>, user: <user id>} ]
        • can destroy multiple structures at once
      • suicide creep: name = "suicide", intent = {id: <creep id>}
      • unclaim controller: name = "unclaim", intent = {id: <controller id>}
        • intent can be an empty object for suicide and unclaim, but the web interface sends the id in it, as described
      • remove construction site: name = "remove", intent = {}
  • [POST] https://screeps.com/api/game/set-notify-when-attacked

    • post data: { _id, enabled }
    • enabled is either true or false (literal values, not strings)
    • response: { ok, result: { ok, nModified, n }, connection: { id, host, port } }
  • [POST] https://screeps.com/api/game/create-construction

    • post data: { room, structureType, x, y}
    • structureType is the same value as one of the in-game STRUCTURE_* constants ('road', 'spawn', etc.)
    • { ok, result: { ok, n }, ops: [ { type, room, x, y, structureType, user, progress, progressTotal, _id } ], insertedCount, insertedIds }

Other

  • https://screeps.com/api/game/time

    • { ok, time }
  • [GET/POST] https://screeps.com/api/user/code

  • [POST] https://screeps.com/api/game/map-stats

    • post data: { rooms: [ <room name> ], statName: "..."}
    • statName can be "owner0", "claim0", or a stat (see the enumeration above) followed by an interval
    • if it is owner0 or claim0, there's no separate stat block in the response
    • response: { ok, stats: { <room name>: { status, novice, own: { user, level }, <stat>: [ { user, value } ] } }, users: { <user's _id>: { _id, username, badge: { type, color1, color2, color3, param, flip } } } }
    • status and novice are as per the room-status call
    • level can be 0 to indicate a reserved room
  • https://screeps.com/room-history/E1N8/12340.json

    • { timestamp, room, base, ticks: { <time>: <tick update object> } }
    • the number in the URL is the tick to retrieve information for (from 5e5 ticks ago to about 50 ticks ago)
      • note that the web interface only shows up to 3e5 ticks ago
    • the tick must be a multiple of 20; the response includes information for the 20 ticks starting then
    • for the first tick in the result, the tick update object maps from object ids to full object descriptions
    • for later ticks, the update includes only changed attributes
    • timestamp is milliseconds since the Unix epoch
  • [POST] https://screeps.com/user/activate-ptr

    • {ok, result: { ok, nModified, n }, connection: { id, host, port } }
    • only works on the PTR
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment