From 8772b9bb331ffe3ebefc664e333a1c82e3b7fddb Mon Sep 17 00:00:00 2001 From: admon84 Date: Thu, 1 Apr 2021 09:12:10 -0600 Subject: [PATCH 1/9] Changing ptr boolean to prefix string - offers more flexibility to API path prefixes like 'ptr' and 'season' --- screepsapi/screepsapi.py | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/screepsapi/screepsapi.py b/screepsapi/screepsapi.py index 95a2b53..8fec7c5 100644 --- a/screepsapi/screepsapi.py +++ b/screepsapi/screepsapi.py @@ -24,7 +24,7 @@ class API(object): def req(self, func, path, **args): - r = func(self.prefix + path, headers={'X-Token': self.token, 'X-Username': self.token}, **args) + r = func(self.url + path, headers={'X-Token': self.token, 'X-Username': self.token}, **args) r.raise_for_status() if 'X-Token' in r.headers and len(r.headers['X-Token']) >= 40: self.token = r.headers['X-Token'] @@ -37,15 +37,15 @@ def req(self, func, path, **args): 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, token=None, ptr=False, host=None, secure=False): - self.ptr = ptr + def __init__(self, u=None, p=None, token=None, host=None, prefix=None, secure=False): self.host = host + self.prefix = prefix self.secure = secure - if host is not None: - self.prefix = 'https://' if secure else 'http://' - self.prefix += host + '/api/' - else: - self.prefix = 'https://screeps.com/ptr/api/' if ptr else 'https://screeps.com/api/' + + self.url = 'https://' if secure else 'http://' + self.url += host if host else 'screeps.com' + self.url += prefix if prefix + self.url += '/api/' self.token = None if u is not None and p is not None: @@ -259,12 +259,12 @@ def activate_ptr(self): class Socket(object): - def __init__(self, user=None, password=None, ptr=False, logging=False, host=None, secure=None, token=None): + def __init__(self, user=None, password=None, logging=False, host=None, prefix=None, secure=None, token=None): self.settings = {} self.user = user self.password = password - self.ptr = ptr self.host = host + self.prefix = prefix self.secure = secure self.logging = False self.token = None @@ -361,7 +361,14 @@ def on_message(self, ws, message): self.process_rawdata(ws, data) def connect(self): - screepsConnection = API(u=self.user,p=self.password,ptr=self.ptr,host=self.host,secure=self.secure, token=self.atoken) + screepsConnection = API( + u=self.user, + p=self.password, + host=self.host, + prefix=self.prefix, + secure=self.secure, + token=self.atoken + ) me = screepsConnection.me() self.user_id = me['_id'] self.token = screepsConnection.token @@ -373,13 +380,10 @@ def connect(self): logging.getLogger('websocket').addHandler(logging.NullHandler()) websocket.enableTrace(False) - if self.host: - url = 'wss://' if self.secure else 'ws://' - url += self.host + '/socket/websocket' - elif not self.ptr: - url = 'wss://screeps.com/socket/websocket' - else: - url = 'wss://screeps.com/ptr/socket/websocket' + url = 'wss://' if self.secure else 'ws://' + url += self.host if self.host else 'screeps.com' + url += self.prefix if self.prefix + url += '/socket/websocket' self.ws = websocket.WebSocketApp(url=url, on_message=lambda ws, message: self.on_message(ws,message), From 4a5d5095331bfdb7e96dadce9ae19c288053355a Mon Sep 17 00:00:00 2001 From: admon84 Date: Thu, 1 Apr 2021 20:41:54 -0600 Subject: [PATCH 2/9] Started adding room-object api endpoint --- docs/Endpoints.md | 4 ++++ screepsapi/screepsapi.py | 37 +++++++++++++++++++++---------------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/docs/Endpoints.md b/docs/Endpoints.md index 03f0eef..6bf3b2e 100644 --- a/docs/Endpoints.md +++ b/docs/Endpoints.md @@ -32,6 +32,10 @@ are the ones listed below. - `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-objects?room=E1N8` + - `{ ok, objects: [ { _id, room, type, x, y } ], users: { : { _id, username, badge: { type, color1, color2, color3, param, flip } } } }` + - `objects` contains a list of all game objects in the room (creeps, structures, sources, mineral, etc) + - `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" diff --git a/screepsapi/screepsapi.py b/screepsapi/screepsapi.py index 8fec7c5..6c35a54 100644 --- a/screepsapi/screepsapi.py +++ b/screepsapi/screepsapi.py @@ -23,6 +23,8 @@ import warnings; warnings.filterwarnings('ignore', message='.*true sslcontext object.*') class API(object): + DEFAULT_SHARD = 'shard0' + def req(self, func, path, **args): r = func(self.url + path, headers={'X-Token': self.token, 'X-Username': self.token}, **args) r.raise_for_status() @@ -61,20 +63,20 @@ def me(self): def overview(self, interval=8, statName='energyHarvested'): return self.get('user/overview', interval=interval, statName=statName) - def stats(self,id,interval=8): - return self.get('user/stats',id=id,interval=interval) + def stats(self, id, interval=8): + return self.get('user/stats', id=id, interval=interval) - def user_find(self, username=None, user_id=None, shard='shard0'): + def user_find(self, username=None, user_id=None, shard=self.DEFAULT_SHARD): if username is not None: return self.get('user/find', username=username, shard=shard) if user_id is not None: return self.get('user/find', id=user_id, shard=shard) return False - def user_rooms(self, userid, shard='shard0'): + def user_rooms(self, userid, shard=self.DEFAULT_SHARD): return self.get('user/rooms', id=userid, shard=shard) - def memory(self, path='', shard='shard0'): + def memory(self, path='', shard=self.DEFAULT_SHARD): ret = self.get('user/memory', path=path, shard=shard) if 'data' in ret: try: @@ -85,10 +87,10 @@ def memory(self, path='', shard='shard0'): ret['data'] = json.loads(gzip_string) return ret - def set_memory(self, path, value, shard='shard0'): + def set_memory(self, path, value, shard=self.DEFAULT_SHARD): return self.post('user/memory', path=path, value=value, shard=shard) - def get_segment(self, segment, shard='shard0'): + def get_segment(self, segment, shard=self.DEFAULT_SHARD): ret = self.get('user/memory-segment', segment=segment, shard=shard) if 'data' in ret and ret['data'][:3] == 'gz:': try: @@ -100,41 +102,44 @@ def get_segment(self, segment, shard='shard0'): return ret - def set_segment(self, segment, data, shard='shard0'): + def set_segment(self, segment, data, shard=self.DEFAULT_SHARD): return self.post('user/memory-segment', segment=segment, data=data, shard=shard) - def console(self, cmd, shard='shard0'): + def console(self, cmd, shard=self.DEFAULT_SHARD): return self.post('user/console', expression=cmd, shard=shard) #### room info methods - def room_overview(self, room, interval=8, shard='shard0'): + def room_overview(self, room, interval=8, shard=self.DEFAULT_SHARD): return self.get('game/room-overview', interval=interval, room=room, shard=shard) - def room_terrain(self, room, encoded=False, shard='shard0'): + def room_objects(self, room, shard=self.DEFAULT_SHARD): + return self.get('game/room-objects', room=room, shard=shard) + + def room_terrain(self, room, encoded=False, shard=self.DEFAULT_SHARD): if encoded: return self.get('game/room-terrain', room=room, shard=shard, encoded=('1' if encoded else None)) else: return self.get('game/room-terrain', room=room, shard=shard) - def room_status(self, room, shard='shard0'): + def room_status(self, room, shard=self.DEFAULT_SHARD): return self.get('game/room-status', room=room, shard=shard) #### market info methods - def orders_index(self, shard='shard0'): + def orders_index(self, shard=self.DEFAULT_SHARD): return self.get('game/market/orders-index', shard=shard) - def my_orders(self, shard='shard0'): + def my_orders(self, shard=self.DEFAULT_SHARD): return self.get('game/market/my-orders', shard=shard) - def market_order_by_type(self, resourceType, shard='shard0'): + def market_order_by_type(self, resourceType, shard=self.DEFAULT_SHARD): return self.get('game/market/orders', resourceType=resourceType, shard=shard) - def market_history(self, page=None, shard='shard0'): + def market_history(self, page=None, shard=self.DEFAULT_SHARD): return self.get('user/money-history', page=page, shard=shard) From d10d73b884aba6180a8f756d7b2a1a645681e26a Mon Sep 17 00:00:00 2001 From: admon84 Date: Tue, 6 Apr 2021 16:57:08 -0600 Subject: [PATCH 3/9] Adding more of the Screeps Unnofficial API endpoints to the list of available methods --- screepsapi/screepsapi.py | 280 ++++++++++++++++++++++++++++----------- 1 file changed, 201 insertions(+), 79 deletions(-) diff --git a/screepsapi/screepsapi.py b/screepsapi/screepsapi.py index 6c35a54..851cea8 100644 --- a/screepsapi/screepsapi.py +++ b/screepsapi/screepsapi.py @@ -1,15 +1,6 @@ # Copyright @dzhu, @tedivm # https://github.com/screepers/python-screeps -from base64 import b64decode -from collections import OrderedDict -try: - from cStringIO import StringIO -except ImportError: - from io import StringIO - -from io import BytesIO -from gzip import GzipFile import json import logging import requests @@ -17,13 +8,20 @@ import sys import websocket import zlib +import time +from base64 import b64decode +from collections import OrderedDict +from io import BytesIO +from gzip import GzipFile +try: + from cStringIO import StringIO +except ImportError: + from io import StringIO -## 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.*') +DEFAULT_SHARD = 'shard0' class API(object): - DEFAULT_SHARD = 'shard0' def req(self, func, path, **args): r = func(self.url + path, headers={'X-Token': self.token, 'X-Username': self.token}, **args) @@ -39,7 +37,7 @@ def req(self, func, path, **args): 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, token=None, host=None, prefix=None, secure=False): + def __init__(self, u=None, p=None, token=None, host=None, prefix=None, secure=True): self.host = host self.prefix = prefix self.secure = secure @@ -51,32 +49,94 @@ def __init__(self, u=None, p=None, token=None, host=None, prefix=None, secure=Fa self.token = None if u is not None and p is not None: - self.token = self.post('auth/signin', email=u, password=p)['token'] + signin = self.signin(email=u, password=p) + self.token = signin['token'] elif token is not None: self.token = token - #### miscellaneous user methods + + #### auth methods def me(self): return self.get('auth/me') + + def signin(self, email=None, password=None): + return self.post('auth/signin', email=email, password=password) + + def queryToken(self, token=None): + return self.get('auth/query-token', token=token) + + def servers_list(self): + return self.post('servers/list') + + def version(self): + return self.post('version') + + + #### register methods + + def check_email(self, email): + return self.get('register/check-email', email=email) + + def check_username(self, username): + return self.get('register/check-username', username=username) + + def set_username(self, username): + return self.post('register/set-username', username=username) + + def register(self, username, email, password, modules) + return self.post('register/submit', username=username, email=email, password=password, modules=modules) + + + #### 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_unread(self): + return self.get('user/messages/unread-count') + + def msg_send(self, respondent, text): + return self.post('user/messages/send', respondent=respondent, text=text) + + def msg_mark_read(self, msg_id): + return self.post('user/messages/send', id=msg_id) + + + #### user methods def overview(self, interval=8, statName='energyHarvested'): return self.get('user/overview', interval=interval, statName=statName) - def stats(self, id, interval=8): - return self.get('user/stats', id=id, interval=interval) - - def user_find(self, username=None, user_id=None, shard=self.DEFAULT_SHARD): + def user_find(self, username=None, user_id=None, shard=DEFAULT_SHARD): if username is not None: return self.get('user/find', username=username, shard=shard) if user_id is not None: return self.get('user/find', id=user_id, shard=shard) return False - def user_rooms(self, userid, shard=self.DEFAULT_SHARD): - return self.get('user/rooms', id=userid, shard=shard) + def user_rooms(self, user_id, shard=DEFAULT_SHARD): + return self.get('user/rooms', id=user_id, shard=shard) + + def stats(self, user_id, interval=8): + return self.get('user/stats', id=user_id, interval=interval) + + def set_user_email(self, email): + return self.post('user/email', email=email) + + def user_name(self): + return self.get('user/name') - def memory(self, path='', shard=self.DEFAULT_SHARD): + def user_notify_prefs(self, prefs): + return self.post('user/notify-prefs', prefs) + + def tutorial_done(self): + return self.post('user/tutorial-done') + + def memory(self, path='', shard=DEFAULT_SHARD): ret = self.get('user/memory', path=path, shard=shard) if 'data' in ret: try: @@ -87,10 +147,10 @@ def memory(self, path='', shard=self.DEFAULT_SHARD): ret['data'] = json.loads(gzip_string) return ret - def set_memory(self, path, value, shard=self.DEFAULT_SHARD): + def set_memory(self, path, value, shard=DEFAULT_SHARD): return self.post('user/memory', path=path, value=value, shard=shard) - def get_segment(self, segment, shard=self.DEFAULT_SHARD): + def get_segment(self, segment, shard=DEFAULT_SHARD): ret = self.get('user/memory-segment', segment=segment, shard=shard) if 'data' in ret and ret['data'][:3] == 'gz:': try: @@ -101,46 +161,67 @@ def get_segment(self, segment, shard=self.DEFAULT_SHARD): ret['data'] = json.loads(gzip_string) return ret - - def set_segment(self, segment, data, shard=self.DEFAULT_SHARD): + def set_segment(self, segment, data, shard=DEFAULT_SHARD): return self.post('user/memory-segment', segment=segment, data=data, shard=shard) - - def console(self, cmd, shard=self.DEFAULT_SHARD): + def console(self, cmd, shard=DEFAULT_SHARD): return self.post('user/console', expression=cmd, shard=shard) + def get_code(self, branch): + return self.get('user/code', branch=branch) + + def set_code(self, branch, modules, _hash=None): + if _hash is None: + _hash = int(time.time()) + return self.post('user/code', branch=branch, modules=modules, _hash=_hash) + + def branches(self): + return self.get('user/branches') + + def set_active_branch(self, branch, activeName): + return self.post('user/set-active-branch', branch=branch, activeName=activeName) + + def clone_branch(self, branch, newName, defaultModules): + return self.post('user/clone-branch', branch=branch, newName=newName, defaultModules=defaultModules) + + def delete_branch(self, branch): + return self.post('user/delete-branch', branch=branch) + #### room info methods - def room_overview(self, room, interval=8, shard=self.DEFAULT_SHARD): + def room_overview(self, room, interval=8, shard=DEFAULT_SHARD): return self.get('game/room-overview', interval=interval, room=room, shard=shard) - def room_objects(self, room, shard=self.DEFAULT_SHARD): + def room_objects(self, room, shard=DEFAULT_SHARD): return self.get('game/room-objects', room=room, shard=shard) - def room_terrain(self, room, encoded=False, shard=self.DEFAULT_SHARD): + def room_decorations(self, room, shard=DEFAULT_SHARD): + return self.get('game/room-decorations', room=room, shard=shard) + + def room_terrain(self, room, encoded=False, shard=DEFAULT_SHARD): if encoded: return self.get('game/room-terrain', room=room, shard=shard, encoded=('1' if encoded else None)) else: return self.get('game/room-terrain', room=room, shard=shard) - def room_status(self, room, shard=self.DEFAULT_SHARD): + def room_status(self, room, shard=DEFAULT_SHARD): return self.get('game/room-status', room=room, shard=shard) #### market info methods - def orders_index(self, shard=self.DEFAULT_SHARD): + def orders_index(self, shard=DEFAULT_SHARD): return self.get('game/market/orders-index', shard=shard) - def my_orders(self, shard=self.DEFAULT_SHARD): + def my_orders(self, shard=DEFAULT_SHARD): return self.get('game/market/my-orders', shard=shard) - def market_order_by_type(self, resourceType, shard=self.DEFAULT_SHARD): + def market_order_by_type(self, resourceType, shard=DEFAULT_SHARD): return self.get('game/market/orders', resourceType=resourceType, shard=shard) - def market_history(self, page=None, shard=self.DEFAULT_SHARD): - return self.get('user/money-history', page=page, shard=shard) + def market_history(self, page=0): + return self.get('user/money-history', page=page) #### leaderboard methods @@ -165,74 +246,115 @@ 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 check_unique_name(self, type, name=None, shard=DEFAULT_SHARD) + return self.post('game/check-unique-object-name', type=type, name=name, shard=shard) - def flag_create(self, room, x, y, name=None, color='white', secondaryColor=None, shard='shard0'): + def gen_unique_flag(self, shard=DEFAULT_SHARD): + return self.post('game/gen-unique-flag-name', shard=shard) + + def check_unique_flag(self, name=None, shard=DEFAULT_SHARD) + return self.post('game/check-unique-flag-name', name=name, shard=shard) + + def flag_create(self, room, x, y, name=None, color='white', secondaryColor=None, shard=DEFAULT_SHARD): 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, shard='shard0') + return self.post('game/create-flag', room=room, x=x, y=y, name=name, color=color, secondaryColor=secondaryColor, shard=DEFAULT_SHARD) 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, shard='shard0'): + def flag_remove(self, name, room, shard=DEFAULT_SHARD): + return self.post('game/remove-flag', name=name, room=room, shard=shard) + + def flag_change_color(self, _id, color, secondaryColor=None, shard=DEFAULT_SHARD): if secondaryColor is None: secondaryColor = color - return self.post('game/change-flag-color', _id=_id, color=color, secondaryColor=secondaryColor, shard='shard0') + return self.post('game/change-flag-color', _id=_id, color=color, secondaryColor=secondaryColor, shard=DEFAULT_SHARD) - def create_site(self, type, room, x, y, shard='shard0'): + def create_site(self, type, room, x, y, shard=DEFAULT_SHARD): return self.post('game/create-construction', structureType=type, room=room, x=x, y=y, shard=shard) - def place_spawn(self, room, name, x, y, shard='shard0'): + def place_spawn(self, room, name, x, y, shard=DEFAULT_SHARD): return self.post('game/place-spawn', room=room, name=name, x=x, y=y, shard=shard) + def badge(self, badge): + return self.post('user/badge', badge=badge) + def respawn(self): return self.post('user/respawn') - def respawn_prohibited_rooms(self, shard='shard0'): + def respawn_prohibited_rooms(self, shard=DEFAULT_SHARD): return self.get('user/respawn-prohibited-rooms', shard=shard) + def add_object_intent(self, room, name, intent, shard=DEFAULT_SHARD): + return self.post('game/add-object-intent', room=room, name=name, intent=intent, shard=shard) + + def set_notify_attacked(self, _id, enabled=True, shard=DEFAULT_SHARD): + return self.post('game/set-notify-when-attacked', _id=_id, enabled=enabled, shard=shard) + + def create_invader(self, x, y, size, type, boosted=False, shard=DEFAULT_SHARD): + return self.post('game/create-invader', x=x, y=y, size=size, type=type, boosted=boosted, shard=shard) + + def remove_invader(self, x, y, size, type, boosted=False, shard=DEFAULT_SHARD): + return self.post('game/remove-invader', x=x, y=y, size=size, type=type, boosted=boosted, shard=shard) + + #### battle info methods - def battles(self, interval=None, start=None): - if start is not None: - return self.get('experimental/pvp', start=start) - if interval is not None: - return self.get('experimental/pvp', interval=interval) - return False + def battles(self, interval=100): + return self.get('experimental/pvp', interval=interval) def nukes(self): return self.get('experimental/nukes') + def warpath(self, interval=100): + return self.get('warpath/battles', interval=interval) + + def scoreboard(self, limit=20, offset=0): + return self.get('scoreboard/list', limit=limit, offset=offset) + + + #### decoration methods + + def inventory(self): + return self.get('decorations/inventory') + + def themes(self): + return self.get('decorations/themes') + + ## decorations is a string array of ids + def convert(self, decorations): + return self.post('decorations/convert', decorations=decorations) + + def pixelize(self, count, theme=''): + return self.post('decorations/pixelize', count=count, theme=theme) + + def activate(self, _id, active): + return self.post('decorations/activate', _id=_id, active=active) + + ## decorations is a string array of ids + def deactivate(self, decorations): + return self.post('decorations/deactivate', decorations=decorations) + #### other methods - def time(self, shard='shard0'): + def time(self, shard=DEFAULT_SHARD): return self.get('game/time', shard=shard)['time'] - def map_stats(self, rooms, statName, shard='shard0'): + def map_stats(self, rooms, statName, shard=DEFAULT_SHARD): return self.post('game/map-stats', rooms=rooms, statName=statName, shard=shard) - def worldsize(self, shard='shard0'): + def worldsize(self, shard=DEFAULT_SHARD): return self.get('game/world-size', shard=shard) def world_status(self): @@ -241,8 +363,8 @@ def world_status(self): def world_start_room(self, shard=None): return self.get('user/world-start-room', shard=shard) - def history(self, room, tick): - return self.get('../room-history/%s/%s.json' % (room, tick - (tick % 20))) + def history(self, room, tick, shard=DEFAULT_SHARD): + return self.get('../room-history/%s/%s/%s.json' % (shard, room, tick - (tick % 20))) def get_shards(self): try: @@ -264,7 +386,7 @@ def activate_ptr(self): class Socket(object): - def __init__(self, user=None, password=None, logging=False, host=None, prefix=None, secure=None, token=None): + def __init__(self, user=None, password=None, logging=False, host=None, prefix=None, secure=True, token=None): self.settings = {} self.user = user self.password = password @@ -303,13 +425,13 @@ def subscribe(self, watchpoint): def set_subscriptions(self): pass - def process_log(self, ws, message, shard='shard0'): + def process_log(self, ws, message, shard=DEFAULT_SHARD): pass - def process_results(self, ws, message, shard='shard0'): + def process_results(self, ws, message, shard=DEFAULT_SHARD): pass - def process_error(self, ws, message, shard='shard0'): + def process_error(self, ws, message, shard=DEFAULT_SHARD): pass def process_cpu(self, ws, data): @@ -344,7 +466,7 @@ def on_message(self, ws, message): if 'shard' in data[1]: shard = data[1]['shard'] else: - shard = 'shard0' + shard = DEFAULT_SHARD if 'messages' in data[1]: stream = [] @@ -390,11 +512,13 @@ def connect(self): url += self.prefix if self.prefix url += '/socket/websocket' - self.ws = websocket.WebSocketApp(url=url, - on_message=lambda ws, message: self.on_message(ws,message), - on_error=lambda ws, error: self.on_error(ws,error), - on_close=lambda ws: self.on_close(ws), - on_open=lambda ws: self.on_open(ws)) + self.ws = websocket.WebSocketApp( + url=url, + on_message=lambda ws, message: self.on_message(ws,message), + on_error=lambda ws, error: self.on_error(ws,error), + on_close=lambda ws: self.on_close(ws), + on_open=lambda ws: self.on_open(ws) + ) ssl_defaults = ssl.get_default_verify_paths() sslopt_ca_certs = {'ca_certs': ssl_defaults.cafile} @@ -404,12 +528,10 @@ def connect(self): else: self.ws.run_forever(ping_interval=1, sslopt=sslopt_ca_certs) - def disconnect(self): if self.ws: self.ws.close() self.ws = False - def start(self): self.connect() From 37b3912f0534ecbb09f4a4d8354f184877189af6 Mon Sep 17 00:00:00 2001 From: admon84 Date: Tue, 6 Apr 2021 17:14:29 -0600 Subject: [PATCH 4/9] Minor fixes while testing new API endpoints --- screepsapi/screepsapi.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/screepsapi/screepsapi.py b/screepsapi/screepsapi.py index 851cea8..91a8b0d 100644 --- a/screepsapi/screepsapi.py +++ b/screepsapi/screepsapi.py @@ -22,6 +22,9 @@ DEFAULT_SHARD = 'shard0' class API(object): + """ + Screeps API client + """ def req(self, func, path, **args): r = func(self.url + path, headers={'X-Token': self.token, 'X-Username': self.token}, **args) @@ -31,7 +34,7 @@ def req(self, func, path, **args): try: return json.loads(r.text, object_pairs_hook=OrderedDict) except ValueError: - print ('JSON failure:', r.text) + print('JSON failure:', r.text) return None def get(self, _path, **args): return self.req(requests.get, _path, params=args) @@ -44,7 +47,7 @@ def __init__(self, u=None, p=None, token=None, host=None, prefix=None, secure=Tr self.url = 'https://' if secure else 'http://' self.url += host if host else 'screeps.com' - self.url += prefix if prefix + self.url += prefix if prefix else '' self.url += '/api/' self.token = None @@ -84,7 +87,7 @@ def check_username(self, username): def set_username(self, username): return self.post('register/set-username', username=username) - def register(self, username, email, password, modules) + def register(self, username, email, password, modules): return self.post('register/submit', username=username, email=email, password=password, modules=modules) @@ -251,13 +254,13 @@ def board_seasons(self): def gen_unique_name(self, type): return self.post('game/gen-unique-object-name', type=type) - def check_unique_name(self, type, name=None, shard=DEFAULT_SHARD) + def check_unique_name(self, type, name=None, shard=DEFAULT_SHARD): return self.post('game/check-unique-object-name', type=type, name=name, shard=shard) def gen_unique_flag(self, shard=DEFAULT_SHARD): return self.post('game/gen-unique-flag-name', shard=shard) - def check_unique_flag(self, name=None, shard=DEFAULT_SHARD) + def check_unique_flag(self, name=None, shard=DEFAULT_SHARD): return self.post('game/check-unique-flag-name', name=name, shard=shard) def flag_create(self, room, x, y, name=None, color='white', secondaryColor=None, shard=DEFAULT_SHARD): @@ -385,7 +388,9 @@ def activate_ptr(self): class Socket(object): - + """ + Screeps WebSocket comunitcator + """ def __init__(self, user=None, password=None, logging=False, host=None, prefix=None, secure=True, token=None): self.settings = {} self.user = user @@ -399,7 +404,7 @@ def __init__(self, user=None, password=None, logging=False, host=None, prefix=No self.atoken = token def on_error(self, ws, error): - print (error) + print(error) def on_close(self, ws): self.disconnect() @@ -440,7 +445,6 @@ def process_cpu(self, ws, data): def process_rawdata(self, ws, data): pass - def on_message(self, ws, message): if (message.startswith('auth ok')): self.set_subscriptions() @@ -509,7 +513,7 @@ def connect(self): url = 'wss://' if self.secure else 'ws://' url += self.host if self.host else 'screeps.com' - url += self.prefix if self.prefix + url += self.prefix if self.prefix else '' url += '/socket/websocket' self.ws = websocket.WebSocketApp( From 86cfffbe56d7fb1fafe926e6df61b3174ca98f2e Mon Sep 17 00:00:00 2001 From: admon84 Date: Tue, 6 Apr 2021 19:15:14 -0600 Subject: [PATCH 5/9] Updating setup.py to v0.5.2 --- setup.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index db08e57..9438135 100644 --- a/setup.py +++ b/setup.py @@ -8,20 +8,20 @@ long_description = open('README.md').read() -version = '0.5.1' +version = '0.5.2' setup( name = 'screepsapi', version = version, packages=find_packages(), - description = 'Screeps Unofficial Client for the Unofficial API', + description = 'Unofficial client for the Screeps Unofficial API', long_description=long_description, python_requires='>=2', author = 'Robert Hafner, dzhu', author_email = 'tedivm@tedivm.com', - url = 'https://github.com/screepers/screeps-api', - download_url = "https://github.com/screepers/screeps-api/archive/v%s.tar.gz" % (version), + url = 'https://github.com/screepers/python-screeps', + download_url = "https://github.com/screepers/python-screeps/archive/v%s.tar.gz" % (version), keywords = 'screeps api', classifiers = [ From 3defcbcfe9b9a6573193682f47d83cf78ee6c158 Mon Sep 17 00:00:00 2001 From: admon84 Date: Tue, 6 Apr 2021 19:26:01 -0600 Subject: [PATCH 6/9] Reverting some unnecessary refactoring --- screepsapi/screepsapi.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/screepsapi/screepsapi.py b/screepsapi/screepsapi.py index 91a8b0d..b09245d 100644 --- a/screepsapi/screepsapi.py +++ b/screepsapi/screepsapi.py @@ -1,6 +1,15 @@ # Copyright @dzhu, @tedivm # https://github.com/screepers/python-screeps +from base64 import b64decode +from collections import OrderedDict +try: + from cStringIO import StringIO +except ImportError: + from io import StringIO + +from io import BytesIO +from gzip import GzipFile import json import logging import requests @@ -8,24 +17,11 @@ import sys import websocket import zlib -import time - -from base64 import b64decode -from collections import OrderedDict -from io import BytesIO -from gzip import GzipFile -try: - from cStringIO import StringIO -except ImportError: - from io import StringIO DEFAULT_SHARD = 'shard0' class API(object): - """ - Screeps API client - """ - + def req(self, func, path, **args): r = func(self.url + path, headers={'X-Token': self.token, 'X-Username': self.token}, **args) r.raise_for_status() @@ -388,9 +384,7 @@ def activate_ptr(self): class Socket(object): - """ - Screeps WebSocket comunitcator - """ + def __init__(self, user=None, password=None, logging=False, host=None, prefix=None, secure=True, token=None): self.settings = {} self.user = user From ce960be726d2ef58488d472e798dfdc7ba122ff1 Mon Sep 17 00:00:00 2001 From: admon84 Date: Tue, 4 May 2021 18:22:39 -0600 Subject: [PATCH 7/9] Removing two API methods that don't exist on MMO server --- screepsapi/screepsapi.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/screepsapi/screepsapi.py b/screepsapi/screepsapi.py index b09245d..7f12b63 100644 --- a/screepsapi/screepsapi.py +++ b/screepsapi/screepsapi.py @@ -315,12 +315,6 @@ def battles(self, interval=100): def nukes(self): return self.get('experimental/nukes') - def warpath(self, interval=100): - return self.get('warpath/battles', interval=interval) - - def scoreboard(self, limit=20, offset=0): - return self.get('scoreboard/list', limit=limit, offset=offset) - #### decoration methods From 84174536a2715bf2c3a7099236d6f931106adae7 Mon Sep 17 00:00:00 2001 From: admon84 Date: Wed, 5 May 2021 22:49:25 -0600 Subject: [PATCH 8/9] Additional fixes from testing and documentation updates --- README.md | 39 +++---- docs/Endpoints.md | 224 +++++++++++++++++++++++++++++---------- screepsapi/screepsapi.py | 91 ++++++++++------ 3 files changed, 248 insertions(+), 106 deletions(-) diff --git a/README.md b/README.md index 5901d71..cea849d 100644 --- a/README.md +++ b/README.md @@ -4,25 +4,23 @@ This is an unofficial client for the [Screeps](https://screeps.com/) Unoffical A Since the API is unofficial it could in theory change at any time. In practice though breaking changes are rare. +## Setup -## Setup: - -Simply install the library using `pip`. - +Simply install the Python `screepsapi` library using `pip`. ## Usage ### Authentication -To authenticate to the primary servers just supply a username and password. +The recommended way to authenticate to the official servers is providing your [Authorization Token](https://docs.screeps.com/auth-tokens.html) into the `token` parameter. ```python import screepsapi -USER = "MyUsername" -PASSWORD = "TimeTravelingSecretAgentForHire" -api = screepsapi.API(USER, PASSWORD) +TOKEN = "3bdd1da7-3002-4aaa-be91-330562f54093" +api = screepsapi.API(token=TOKEN) ``` +An optional `prefix` parameter can be included with values such as `"/ptr"` for the Public Test Realm or `"/season"` for Seasonal Server. It is also possible to access private servers with the `host` and `secure` parameters. @@ -30,38 +28,37 @@ It is also possible to access private servers with the `host` and `secure` param import screepsapi USER = "MyUsername" PASSWORD = "TimeTravelingSecretAgentForHire" -api = screepsapi.API(USER, PASSWORD, host="server1.screepspl.us:443", secure=True) +HOST = "server1.screepspl.us:443" +api = screepsapi.API(USER, PASSWORD, host=HOST, secure=True) ``` Note that by default private servers do not use SSL and all traffic is unencrypted. ### API -The API itself is a simple REST-based API. Each method in the api library corresponds to a different endpoint for the API. +The API class is a simple REST-based API. Each method corresponds to a different Screeps API endpoint. -The best way to discover functionality is to read through the library itself. +The best way to discover functionality is to read through the [screepsapi library](screepsapi/screepsapi.py) or [Endpoints.md](docs/Endpoints.md) #### Console Example ```python import screepsapi -USER = "MyUsername" -PASSWORD = "TimeTravelingSecretAgentForHire" -api = screepsapi.API(USER, PASSWORD, host="server1.screepspl.us:443", secure=True) +TOKEN = "3bdd1da7-3002-4aaa-be91-330562f54093" +api = screepsapi.API(token=TOKEN) # Run "Game.time" on shard1 via the console -api.console('Game.time', shard='shard1') +api.console("Game.time", shard="shard1") ``` #### User Information Example ```python import screepsapi -USER = "MyUsername" -PASSWORD = "TimeTravelingSecretAgentForHire" -api = screepsapi.API(USER, PASSWORD, host="server1.screepspl.us:443", secure=True) +TOKEN = "3bdd1da7-3002-4aaa-be91-330562f54093" +api = screepsapi.API(token=TOKEN) -# Find the GCL for `tedivm` +# Find the GCL for "tedivm" user = api.user_find("tedivm") print user["user"]["gcl"] ``` @@ -71,3 +68,7 @@ print user["user"]["gcl"] Screeps provides a sizable amount of data over a websocket. This includes console data and room details. The best way to utilize the socket is to extend `screepsapi.Socket` and override the various abstract functions. + +## Credentials + +App developers are encouraged to align with [SS3: Unified Credentials File v1.0](https://github.com/screepers/screepers-standards/blob/master/SS3-Unified_Credentials_File.md) to standardize Screeps credentials storage with other third party tools. diff --git a/docs/Endpoints.md b/docs/Endpoints.md index 6bf3b2e..05d5ebd 100644 --- a/docs/Endpoints.md +++ b/docs/Endpoints.md @@ -17,64 +17,103 @@ Memory and console endpoints are from 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 +- interval: 8, 180, 1440 (8 for 1h, 180 for 24h and 1440 for 7 days) - statName: creepsLost, creepsProduced, energyConstruction, energyControl, energyCreeps, energyHarvested # Authentication + - [POST] `https://screeps.com/api/auth/signin` - post data: `{ email, password }` - response: `{ ok, token }` +- [POST] `https://screeps.com/api/auth/steam-ticket` + - post data: `{ ticket, useNativeAuth }` + +- [POST] `https://screeps.com/api/auth/query-token` + - post data: `{ token }` + +# Registration + +- [GET] `https://screeps.com/api/register/check-email?email=user@example.com` + +- [GET] `https://screeps.com/api/register/check-username?username=danny` + +- [POST] `https://screeps.com/api/register/set-username` + - post data: `{ username }` + +- [POST] `https://screeps.com/api/register/submit` + - post data: `{ username, email, password, modules }` + +# Messaging + +- [GET] `https://screeps.com/api/user/messages/index` + - `{ ok, messages: [ { _id, message: { _id, user, respondent, date, type, text, unread } } ], users: { : { _id, username, badge: { type, color1, color2, color3, param, flip } } } }` + +- [GET] `https://screeps.com/api/user/messages/list?respondent={userId}` + - `{ ok, messages: [ { _id, date, type, text, unread } ] }` + +- [GET] `https://screeps.com/api/user/messages/unread-count` + - `{ ok, count }` + +- [POST] `https://screeps.com/api/user/messages/send` + - post data: `{ respondent, text }` + - response: `{ ok }` + +- [POST] `https://screeps.com/api/user/messages/mark-read` + - post data: `{ id }` + # 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-objects?room=E1N8` - - `{ ok, objects: [ { _id, room, type, x, y } ], users: { : { _id, username, badge: { type, color1, color2, color3, param, flip } } } }` - - `objects` contains a list of all game objects in the room (creeps, structures, sources, mineral, etc) +- [GET] `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` +- [GET] `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` +- [GET] `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` +- [GET] `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) -- `https://screeps.com/api/experimental/pvp?interval=50` +- [GET] `https://screeps.com/api/game/room-objects?room=E1N8` + - `{ ok, objects: [ { _id, room, type, x, y } ], users: { : { _id, username, badge: { type, color1, color2, color3, param, flip } } } }` + - `objects` contains a list of all game objects in the room (creeps, structures, sources, mineral, etc) + +- [GET] `https://screeps.com/api/experimental/pvp?interval=100` - `{ ok, time, rooms: [ { _id, lastPvpTime } ] }` - `time` is the current server tick - `_id` contains the room name for each room, and `lastPvpTime` contains the last tick pvp occurred - if neither a valid `interval` nor a valid `start` argument is provided, the result of the call is still `ok`, but with an empty rooms array. -- `https://screeps.com/api/experimental/pvp?start=14787157` - - `{ ok, time, rooms: [ { _id, lastPvpTime } ] }` +- [GET] `https://screeps.com/api/experimental/nukes` + - `{ ok, nukes, : [ { _id, type, room, x, y, landTime, launchRoomName } ] }` # Market information -- `https://screeps.com/api/game/market/orders-index` - - `{"ok":1,"list":[{"_id":"XUHO2","count":2}]}` - - `_id` is the resource type, and there will only be one of each type. - - `count` is the number of orders. +- [GET] `https://screeps.com/api/game/market/orders-index` + - `{"ok":1,"list":[{"_id":"XUHO2","count":2}]}` + - `_id` is the resource type, and there will only be one of each type. + - `count` is the number of orders. - - `https://screeps.com/api/game/market/my-orders` +- [GET] `https://screeps.com/api/game/market/my-orders` - `{ ok, list: [ { _id, created, user, active, type, amount, remainingAmount, resourceType, price, totalAmount, roomName } ] }` - - `https://screeps.com/api/game/market/orders?resourceType=Z` +- [GET] `https://screeps.com/api/game/market/orders?resourceType=Z` - `{ ok, list: [ { _id, created, user, active, type, amount, remainingAmount, resourceType, price, totalAmount, roomName } ] }` - `resourceType` is one of the RESOURCE_* constants. - - `https://screeps.com/api/user/money-history` +- [GET] `https://screeps.com/api/user/money-history` - `{"ok":1,"page":0,"list":[ { _id, date, tick, user, type, balance, change, market: {} } ] }` - `page` used for pagination. - `hasMore` is true if there are more pages to view. @@ -85,29 +124,29 @@ are the ones listed below. - Price Change - `{ changeOrderPrice: { orderId, oldPrice, newPrice } }` # Leaderboard -- `https://screeps.com/api/leaderboard/seasons` + +- [GET] `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` +- [GET] `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` +- [GET] `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` +- [GET] `https://screeps.com/api/leaderboard/list?limit=10&mode=world&offset=10&season=2015-09` - `{ ok, list: [ { _id, season, user, score, rank } ], count, users: { : { _id, username, badge: { type, color1, color2, color3, param, flip }, gcl } } }` - - mode can be "world" or "power" - - setting limit to too high a value will give an "invalid parameters" response # Messages -- `https://screeps.com/api/user/messages/index` + +- [GET] `https://screeps.com/api/user/messages/index` - `{ ok, messages: [ { _id, message: { _id, user, respondent, date, type, text, unread } } ], users: { : { _id, username, badge: { type, color1, color2, color3, param, flip } } } }` -- `https://screeps.com/api/user/messages/list?respondent=55605a6654db1fa952de8d90` +- [GET] `https://screeps.com/api/user/messages/list?respondent=55605a6654db1fa952de8d90` - `{ ok, messages: [ { _id, date, type, text, unread } ] }` - [POST] `https://screeps.com/api/user/messages/send` @@ -115,42 +154,48 @@ are the ones listed below. - `respondent` is the long _id of the user, not the username - response: `{ ok }` -- `https://screeps.com/api/user/messages/unread-count` +- [GET] `https://screeps.com/api/user/messages/unread-count` - `{ ok, count }` -# User information -- `https://screeps.com/api/auth/me` +# User information and settings + +- [GET] `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` +- [GET] `https://screeps.com/api/user/name` + +- [GET] `https://screeps.com/api/user/stats?interval=8` + +- [GET] `https://screeps.com/api/user/rooms?interval=8` + +- [GET] `https://screeps.com/api/user/find?id={userId}` - `{ ok, user: { _id, username, badge: { type, color1, color2, color3, param, flip }, gcl } }` -- `https://screeps.com/api/user/find?id=55c91dc66e36d62a6167e1b5` +- [GET] `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` +- [GET] `https://screeps.com/api/user/overview?interval=1440&statName=energyControl` - `{ ok, rooms: [ ], stats: { : [ { value, endTime } ] }, statsMax }` -- `https://screeps.com/api/user/respawn-prohibited-rooms` +- [GET] `https://screeps.com/api/user/respawn-prohibited-rooms` - `{ ok, rooms: [ ] }` -- `https://screeps.com/api/user/world-status` +- [GET] `https://screeps.com/api/user/world-size` + - `{ ok, width, height }` + +- [GET] `https://screeps.com/api/user/world-status` - `{ ok, status }` - - `status` is "normal" is the player is spawned, "empty" otherwise + - `status` can be `"lost"`, `"empty"` or `"normal"`, lost is when you loose all your spawns, empty is when you have respawned and not placed your spawn yet. -- `https://screeps.com/api/user/world-start-room` +- [GET] `https://screeps.com/api/user/world-start-room` - `{ ok, room: [ ] }` - `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: [ ] }` -- `https://screeps.com/api/user/memory?path=flags.Flag1` +- [GET] `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 @@ -159,20 +204,50 @@ are the ones listed below. - post data: `{ path, value }` - response: `{ ok, result: { ok, n }, ops: [ { user, expression, hidden } ], data, insertedCount, insertedIds }` -- `https://screeps.com/api/user/memory-segment?segment=[0-99]` - - `{ okay, data }` - - response: `{ ok, data: '' }` +- [GET] `https://screeps.com/api/user/memory-segment?segment=[0-99]` + - `{ ok, data }` -- [POST ]`https://screeps.com/api/user/memory-segment` +- [POST] `https://screeps.com/api/user/memory-segment` - post data: `{ segment, data }` +- [GET/POST] `https://screeps.com/api/user/code` + - for pushing or pulling code, as documented at http://support.screeps.com/hc/en-us/articles/203022612 + +- [GET] `https://screeps.com/api/user/branches` + - `{ ok, list: [ { _id, branch, activeWorld, activeSim } ] }` + +- [POST] `https://screeps.com/api/user/set-active-branch` + - post data: `{ branch, activeName }` + +- [POST] `https://screeps.com/api/user/clone-branch` + - post data: `{ branch, newName, defaultModules }` + +- [POST] `https://screeps.com/api/user/delete-branch` + - post data: `{ branch }` + +- [POST] `https://screeps.com/api/user/notify-prefs` + - post data: `{ prefs }` + - `prefs` can be any of: `disabled`, `disabledOnMessages`, `sendOnline`, `interval`, `errorsInterval` + +- [POST] `https://screeps.com/api/user/tutorial-done` + +- [POST] `https://screeps.com/api/user/badge?badge={Badge}` + - `badge` object format: `{ type, color1, color2, color3, param, flip }` + # 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/check-unique-object-name` + - post data: `{ type, name, shard }` + +- [POST] `https://screeps.com/api/game/gen-unique-flag-name` + - post data: `{ shard }` + - [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 } }` @@ -188,6 +263,9 @@ are the ones listed below. - post data: `{ _id, color, secondaryColor }` - response: `{ ok, result: { nModified, ok, n }, connection: { host, id, port } }` +- [POST] `https://screeps.com/api/game/remove-flag` + - post data: `{ room, name, shard }` + - [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 } }` @@ -211,12 +289,48 @@ are the ones listed below. - `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 }` +- [POST] `https://screeps.com/api/game/place-spawn` + - post data: `{ room, name, x, y, shard }` + +- [POST] `https://screeps.com/api/game/create-invader` + - post data: `{ x, y, size, type, boosted, shard }` + +- [POST] `https://screeps.com/api/game/remove-invader` + - post data: `{ _id, shard }` + +# Decorations + +- [GET] `https://screeps.com/api/decorations/inventory` + +- [GET] `https://screeps.com/api/decorations/themes` + +- [POST] `https://screeps.com/api/decorations/convert` + - post data: `{ decorations }` + - `decorations` is a string array of ids + +- [POST] `https://screeps.com/api/decorations/pixelize` + - post data: `{ count, theme }` + +- [POST] `https://screeps.com/api/decorations/activate` + - post data: `{ _id, active}` + +- [POST] `https://screeps.com/api/decorations/deactivate` + - post data: `{ decorations }` + - `decorations` is a string array of ids + # Other -- `https://screeps.com/api/game/time` - - `{ ok, time }` -- [GET/POST] `https://screeps.com/api/user/code` - - for pushing or pulling code, as documented at http://support.screeps.com/hc/en-us/articles/203022612 +- [POST] `https://screeps.com/api/servers/list` + - `{ ok, servers }` + +- [GET] `https://screeps.com/api/game/shards/info` + - `{ ok, shards: [ { name, lastTicks, cpuLimit, rooms, users, tick } ] }` + +- [GET] `https://screeps.com/api/version` + - `{ ok, package, protocol, serverData, users }` + +- [GET] `https://screeps.com/api/game/time` + - `{ ok, time }` - [POST] `https://screeps.com/api/game/map-stats` - post data: `{ rooms: [ ], statName: "..."}` @@ -226,7 +340,7 @@ are the ones listed below. - `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` +- [GET] `https://screeps.com/room-history/shard0/E1N8/12340.json` - `{ timestamp, room, base, ticks: {