├── .gitignore ├── LICENSE ├── README.md ├── bin └── screepsconsole.sh ├── docs ├── ExampleLogger.js └── screenshot.png ├── makefile ├── requirements.txt ├── screeps_console ├── __init__.py ├── autocomplete.py ├── command.py ├── console.py ├── data │ └── autocomplete │ │ ├── basic.dat │ │ ├── constants.dat │ │ └── properties.dat ├── interactive.py ├── outputparser.py ├── settings.py └── themes.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | #Ipython Notebook 62 | .ipynb_checkpoints 63 | 64 | 65 | .settings.yaml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Robert Hafner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # screeps_console 2 | 3 | This project streams the Screeps console output to the terminal. It strips out 4 | any html tags and adds colors. 5 | 6 | ![Screeps Interactive Console](docs/screenshot.png?raw=true "Screeps Interactive Console") 7 | 8 | 9 | ## Requirements 10 | 11 | * python 12 | * ncurses (typically already on most \*nix systems) 13 | * pip 14 | * cygwin (if on Windows) 15 | 16 | 17 | ## Installation 18 | 19 | Note: This application runs on both Python 2 and Python 3. 20 | 21 | 1. Make sure you have `pip` installed. On Mac OS X do `sudo easy_install pip` 22 | 1. Make sure `virtualenv` is installed- `pip install virtualenv` 23 | 1. Clone this GitHub repo and go into the cloned directory 24 | 1. Determine where your Python is installed with `which python` - assume it is `/path/to/python` and replace that below 25 | 1. Set up the `virtualenv` with the command `virtualenv -p /path/to/python env` 26 | 1. Use that new `virtualenv` with the command `source env/bin/activate` 27 | 1. Run `make` 28 | 1. Run `make install` 29 | 1. Run `screepsconsole` from inside the terminal 30 | 31 | 32 | ## Settings 33 | 34 | The settings file is created automatically and placed in `~.screepsconsole.yaml`. 35 | 36 | Typically there is little reason to edit it manually. When you attempt to connect 37 | to a server for the first time it will ask for your credentials (and if it's a 38 | private server for the host), which will then be saved for future use. 39 | 40 | When using the "screeps.com" server the console will automatically create a 41 | token and use that instead of storing your credentials. 42 | 43 | ## Launching 44 | 45 | The interactive shell can be used for both reading console output and sending 46 | console commands to the server. 47 | 48 | ```bash 49 | $ screepsconsole 50 | ``` 51 | 52 | By default it connects to the main server, but it can also connect to PTR. 53 | 54 | ```bash 55 | $ screepsconsole ptr 56 | ``` 57 | 58 | The system also works with private servers. Any label can be used, and unlike 59 | the main server the system will ask for a host (include the port) and whether 60 | the shard uses https. 61 | 62 | ```bash 63 | $ screepsconsole screepsplus 64 | ``` 65 | 66 | It is possible to clear a saved connection. 67 | 68 | ```bash 69 | $ screepsconsole clear connectionname 70 | ``` 71 | 72 | You can also call the python script directly. 73 | 74 | ```bash 75 | $ ./screeps_console/interactive.py 76 | ``` 77 | 78 | 79 | ## Streaming Console 80 | 81 | To stream the console output directly to your terminal's stdout without the 82 | ability to send command use the `console.py` application. 83 | 84 | ```bash 85 | $ ./screeps_console/console.py 86 | ``` 87 | 88 | Like the Interactive version different servers can be specified. 89 | 90 | ```bash 91 | $ ./screeps_console/console.py ptr 92 | ``` 93 | 94 | The output can also be sent in JSON. 95 | 96 | ```bash 97 | $ ./screeps_console/console.py main json 98 | ``` 99 | 100 | 101 | ## Interactivity 102 | 103 | The interactive console allows you to directly interact with your screeps 104 | program through the in game console. Most of the time what you put in will be 105 | sent directly to the screeps server to be processed. There are a few built in 106 | commands however. 107 | 108 | * `about` - displays information about the program. 109 | * `clear` - resets the console display. 110 | * `console` - allows users to suppress normal console output and only sure user interactions. 111 | * `disconnect` - disconnects the console from the Screeps server. 112 | * `exit` - exits the shell. 113 | * `filter` - applies filters to the console output. 114 | * `list` - lists these and other built in commands and aliases. 115 | * `pause` - disables autoscrolling (hit enter on an empty terminal to reenable). 116 | * `reconnect` - reconnects the console to the Screeps server. 117 | * `shard` - controls the active shards. 118 | * `themes` - lists available themes when called without an argument, or switches. 119 | Otherwise switches themes when called with the theme name as the first 120 | argument. 121 | * `time` - displays the time (in ticks) on the Screeps server. 122 | 123 | 124 | ## Scrolling 125 | 126 | Scrolling can be done one line at a time using `alt up` and `alt down`. Using 127 | `PageUp` and `PageDown` (FN+up and FN+down on Apple) can be used to scroll 128 | through the console buffer a bit quicker. 129 | 130 | ## Shards 131 | 132 | By default all output from all shards is displayed and console commands go to 133 | shard0. This can be changed with the `shard` commands. 134 | 135 | * `shard` - by itself it outputs the shard that console input will go to. 136 | * `shard SHARDNAME` - changes the shard that console input goes to. 137 | * `shard focus SHARDNAME` - changes the console output and only displays 138 | messages from this shard. 139 | * `shard clear` - display all output from all shards, but leave the console 140 | input pointed at the same shard. 141 | 142 | 143 | ## Filters 144 | 145 | Console output can be filtered using regular expressions and the `filter` 146 | command. Only commands that match at least one filter will be displayed. 147 | 148 | * `filter list` - this lists each current regex filter and its index. 149 | * `filter add REGEX` - add a regular expression to the filter list. 150 | * `filter clear` - remove all filters. 151 | * `filter contains STRING` - add a filter that looks for log lines that contain 152 | the supplied string. 153 | * `filter remove INDEX` - remove a regular expression using it's index. 154 | 155 | 156 | ## Colors and Severity 157 | 158 | Console output can have colors, in both the website version and the shell. To 159 | get the best of both worlds use font tags that also have a severity attribute. 160 | 161 | ``` 162 | Message goes here! 163 | ``` 164 | 165 | The severity can be anywhere from 0 to 5, with five being the most extreme. In 166 | addition you can highlight a log line by giving it the 'type' of 'highlight'. 167 | 168 | ``` 169 | This message will stand out! 170 | ``` 171 | 172 | If you do not care about coloring the web console output you can use a simpler 173 | format. 174 | 175 | ``` 176 | Message goes here 177 | This message will stand out! 178 | ``` 179 | 180 | An [example logger](docs/ExampleLogger.js) is included in the docs folder to 181 | demonstrate how this works. 182 | -------------------------------------------------------------------------------- /bin/screepsconsole.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Get real directory in case of symlink 4 | if [[ -L "${BASH_SOURCE[0]}" ]] 5 | then 6 | DIR="$( cd "$( dirname $( readlink "${BASH_SOURCE[0]}" ) )" && pwd )" 7 | else 8 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 9 | fi 10 | cd $DIR 11 | 12 | ENV="$DIR/../env/bin/activate" 13 | if [ ! -f $ENV ]; then 14 | echo 'Virtual Environment Not Installed' 15 | exit -1 16 | fi 17 | 18 | SCRIPT="$DIR/../screeps_console/interactive.py" 19 | source $ENV 20 | $SCRIPT "$@" 21 | -------------------------------------------------------------------------------- /docs/ExampleLogger.js: -------------------------------------------------------------------------------- 1 | 2 | var ExampleLogger = {} 3 | 4 | ExampleLogger.colors = { 5 | '5': '#ff0066', 6 | '4': '#e65c00', 7 | '3': '#809fff', 8 | '2': '#999999', 9 | '1': '#737373', 10 | '0': '#666666', 11 | 'highlight': '#ffff00', 12 | } 13 | 14 | ExampleLogger.log = function (message, severity = 3) { 15 | if(severity > 5) { 16 | severity = 5 17 | } else if (severity < 0) { 18 | severity = 0 19 | } else if (!Number.isInteger(severity)) { 20 | severity = 3 21 | } 22 | 23 | console.log('' + message + "") 24 | } 25 | 26 | ExampleLogger.highlight = function (message) { 27 | console.log('' + message + "") 28 | } 29 | 30 | module.exports = ExampleLogger 31 | -------------------------------------------------------------------------------- /docs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/screepers/screeps_console/0960edb99dca3fda769167c36ba848792d7975c7/docs/screenshot.png -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | 2 | SHELL:=/bin/bash 3 | ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) 4 | 5 | .PHONY: all fresh dependencies install fulluninstall uninstall removedeps 6 | 7 | all: dependencies 8 | 9 | fresh: fulluninstall dependencies 10 | 11 | fulluninstall: uninstall cleancode 12 | 13 | install: 14 | # Create link in /usr/local/bin to screeps stats program. 15 | ln -s -f $(ROOT_DIR)/bin/screepsconsole.sh /usr/local/bin/screepsconsole 16 | 17 | dependencies: 18 | if [ ! -d $(ROOT_DIR)/env ]; then virtualenv $(ROOT_DIR)/env; fi 19 | source $(ROOT_DIR)/env/bin/activate; yes w | pip install -r $(ROOT_DIR)/requirements.txt 20 | 21 | uninstall: 22 | # Remove screepsstats link in /user/local/bin 23 | if [ -L /usr/local/bin/screepsconsole.sh ]; then \ 24 | rm /usr/local/bin/screepsconsole; \ 25 | fi; 26 | 27 | cleancode: 28 | # Remove existing environment 29 | if [ -d $(ROOT_DIR)/env ]; then \ 30 | rm -rf $(ROOT_DIR)/env; \ 31 | fi; 32 | # Remove compiled python files 33 | if [ -d $(ROOT_DIR)/screep_etl ]; then \ 34 | rm -f $(ROOT_DIR)/screep_etl/*.pyc; \ 35 | fi; 36 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | colorama==0.3.9 2 | nose==1.3.7 3 | PyYAML>=3.12,<4 4 | requests>=2.18.3,<3 5 | Requires==0.0.3 6 | screepsapi>=0.5.1 7 | six>=1.10.0,<2 8 | urwid==1.3.1 9 | websocket-client==0.44.0 10 | -------------------------------------------------------------------------------- /screeps_console/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/screepers/screeps_console/0960edb99dca3fda769167c36ba848792d7975c7/screeps_console/__init__.py -------------------------------------------------------------------------------- /screeps_console/autocomplete.py: -------------------------------------------------------------------------------- 1 | from bisect import bisect_left 2 | import pkg_resources 3 | import urwid 4 | 5 | 6 | class Autocomplete(object): 7 | 8 | lists = {} 9 | 10 | def __init__(self, comp): 11 | self.comp = comp 12 | 13 | basic = self.loadList('basic') 14 | constants = self.loadList('constants') 15 | combined = basic + constants 16 | self.lists['combined'] = self.sortList(combined) 17 | return 18 | 19 | 20 | def loadList(self, listname): 21 | 22 | if not listname in self.lists: 23 | autocomplete = pkg_resources.resource_string(__name__, 'data/autocomplete/' + listname + '.dat').decode("utf-8") 24 | autocomplete_list = autocomplete.splitlines() 25 | autocomplete_list_unique = self.sortList(autocomplete_list) 26 | self.lists[listname] = autocomplete_list_unique 27 | return self.lists[listname] 28 | 29 | 30 | def sortList(self, lst): 31 | autocomplete_list_filtered = (x for x in lst if not x.startswith('#') and x != '') 32 | autocomplete_list_unique = sorted(set(autocomplete_list_filtered)) 33 | return autocomplete_list_unique 34 | 35 | 36 | def complete(self): 37 | prefix = '' 38 | user_text = self.comp.edit.get_edit_text() 39 | results = self.getMatchingString(self.loadList('combined'), user_text) 40 | number_results = len(results) 41 | 42 | # Check for a '.' to see if we're looking at a property 43 | if number_results <= 0: 44 | if '.' in user_text: 45 | pos = user_text.rfind('.') 46 | prefix = user_text[:pos+1] 47 | string = user_text[pos+1:] 48 | results = self.getMatchingString(self.loadList('properties'), string) 49 | number_results = len(results) 50 | 51 | 52 | if number_results <= 0: 53 | return False 54 | elif number_results == 1: 55 | new_edit = prefix + results[0] + ' ' 56 | self.comp.edit.set_edit_text(new_edit) 57 | self.comp.edit.set_edit_pos(len(new_edit)) 58 | elif number_results > 1: 59 | bestMatch = self.getStringOverlapFromList(results) 60 | new_edit = prefix + bestMatch 61 | self.comp.edit.set_edit_text(new_edit) 62 | self.comp.edit.set_edit_pos(len(new_edit)) 63 | self.comp.listwalker.append(urwid.Text(('logged_response', ' * '.join(results)))) 64 | self.comp.listbox.autoscroll() 65 | return False 66 | 67 | 68 | 69 | def getMatchingString(self, wordlist, word_fragment): 70 | if len(word_fragment) <= 0: 71 | return [] 72 | return wordlist[bisect_left(wordlist, word_fragment): 73 | bisect_left(wordlist, word_fragment[:-1] + chr(ord(word_fragment[-1])+1))] 74 | 75 | 76 | def getStringOverlapFromList(self, wordlist): 77 | for index,item in enumerate(wordlist): 78 | if index == 0: 79 | returnString = item 80 | else: 81 | returnString = self.getStringOverlap(returnString, item) 82 | return returnString 83 | 84 | 85 | def getStringOverlap(self, a, b): 86 | matchString = '' 87 | for index, char in enumerate(a): 88 | if char == b[index]: 89 | matchString = matchString + char 90 | else: 91 | return matchString 92 | 93 | return matchString 94 | -------------------------------------------------------------------------------- /screeps_console/command.py: -------------------------------------------------------------------------------- 1 | 2 | from autocomplete import Autocomplete 3 | import calendar 4 | import settings 5 | import re 6 | from themes import themes 7 | import time 8 | import urwid 9 | 10 | 11 | class Processor(object): 12 | shard = 'shard0' 13 | lastkey = False 14 | apiclient = False 15 | aliases = { 16 | 'theme': 'themes', 17 | 'time': 'Game.time', 18 | 'help': 'list' 19 | } 20 | 21 | def __init__(self, connectionname): 22 | self.connectionname = connectionname 23 | self.lastkeytime = 0 24 | self.listbox = False 25 | self.listwalker = False 26 | self.consolemonitor = False 27 | self.edit = False 28 | self.getApiClient() 29 | self.autocomplete = Autocomplete(self) 30 | 31 | 32 | def setDisplayWidgets(self, loop, frame, listbox, listwalker, edit, consolemonitor): 33 | self.listbox = listbox # console 34 | self.listwalker = listwalker 35 | self.edit = edit 36 | self.loop = loop 37 | self.consolemonitor = consolemonitor 38 | 39 | 40 | def getApiClient(self): 41 | return settings.getApiClient(self.connectionname) 42 | 43 | 44 | def onInput(self, key): 45 | 46 | lastkeytime = self.lastkeytime 47 | self.lastkeytime = calendar.timegm(time.gmtime()) 48 | 49 | if self.lastkeytime - lastkeytime > 1: 50 | self.lastkey = False 51 | 52 | lastkey = self.lastkey 53 | self.lastkey = key 54 | 55 | if not self.listbox: 56 | return 57 | 58 | if key == 'enter': 59 | self.onEnter(key) 60 | return 61 | 62 | if key == 'tab' and lastkey == 'tab': 63 | self.onTab(key) 64 | return 65 | 66 | if key == 'page up': 67 | self.onPageUp(key) 68 | return 69 | 70 | if key == 'page down': 71 | self.onPageDown(key) 72 | return 73 | 74 | if key == 'meta up': 75 | self.onMetaUp(key) 76 | return 77 | 78 | if key == 'meta down': 79 | self.onMetaDown(key) 80 | return 81 | 82 | 83 | return 84 | 85 | 86 | def onEnter(self, key): 87 | self.listbox.setAutoscroll(True) 88 | userInput = self.edit 89 | user_text = userInput.get_edit_text() 90 | 91 | # Check for builtin commands before passing to the screeps api. 92 | user_command_split = user_text.split(' ') 93 | first_command = user_command_split[0] 94 | 95 | # Check to see if command is actually an alias 96 | if first_command in self.aliases: 97 | first_command = self.aliases[first_command] 98 | user_command_split[0] = first_command 99 | user_text = ' '.join(user_command_split) 100 | 101 | # Look for built in commands before attempting API call. 102 | builtin = Builtin() 103 | if hasattr(builtin, first_command): 104 | self.listwalker.append( 105 | urwid.Text(('logged_input', '> ' + user_text))) 106 | builtin_command = getattr(builtin, first_command) 107 | builtin_command(self) 108 | self.listbox.autoscroll() 109 | userInput.set_edit_text('') 110 | return 111 | 112 | # Send command to Screeps API. Output will come from the console stream. 113 | if len(user_text) > 0: 114 | self.listwalker.append( 115 | urwid.Text(('logged_input', '> ' + user_text))) 116 | self.listbox.scrollBottom() 117 | userInput.set_edit_text('') 118 | apiclient = self.getApiClient() 119 | apiclient.console(user_text, self.shard) 120 | 121 | def onTab(self, key): 122 | self.autocomplete.complete() 123 | pass 124 | 125 | def onPageUp(self, key): 126 | info = self.loop.screen.get_cols_rows() 127 | self.listbox.scrollUp(int(info[1] / 3)) 128 | 129 | def onPageDown(self, key): 130 | info = self.loop.screen.get_cols_rows() 131 | self.listbox.scrollDown(int(info[1] / 3)) 132 | 133 | def onMetaUp(self, key): 134 | self.listbox.scrollUp(1) 135 | 136 | def onMetaDown(self, key): 137 | self.listbox.scrollDown(1) 138 | 139 | 140 | 141 | class Builtin(object): 142 | 143 | def about(self, comp): 144 | about = 'Screeps Interactive Console by Robert Hafner ' # noqa 145 | comp.listwalker.append(urwid.Text(('logged_response', about))) 146 | about = 'https://github.com/screepers/screeps_console' 147 | comp.listwalker.append(urwid.Text(('logged_response', about))) 148 | return 149 | 150 | def buffer(self, comp): 151 | comp.listwalker.append(urwid.Text(('logged_response', str(len(comp.listwalker))))) 152 | return 153 | 154 | def clear(self, comp): 155 | comp.listbox.set_focus_pending = 0 156 | comp.listwalker[:] = [urwid.Text('')] 157 | return 158 | 159 | def console(self, comp): 160 | helpmessage = 'Control console output. `console quiet` to suppress console output and `console reset` to turn it back on.' 161 | userInput = comp.edit 162 | user_text = userInput.get_edit_text() 163 | user_command_split = user_text.split(' ') 164 | 165 | if len(user_command_split) < 2: 166 | comp.listwalker.append(urwid.Text(('logged_response', helpmessage))) 167 | return False 168 | 169 | command = user_command_split[1] 170 | 171 | if command == 'quiet': 172 | comp.consolemonitor.quiet = True 173 | comp.listwalker.append(urwid.Text(('logged_response', 'Limiting display to user interactions only.'))) 174 | elif command == 'reset' or command == 'verbose': 175 | comp.consolemonitor.quiet = False 176 | comp.listwalker.append(urwid.Text(('logged_response', 'Displaying all console output.'))) 177 | else: 178 | comp.listwalker.append(urwid.Text(('logged_response', helpmessage))) 179 | 180 | def disconnect(self, comp): 181 | comp.consolemonitor.disconnect() 182 | comp.listwalker.append(urwid.Text(('logged_response', 'disconnecting'))) 183 | comp.listbox.autoscroll() 184 | 185 | def exit(self, comp): 186 | raise urwid.ExitMainLoop() 187 | 188 | def filter(self, comp): 189 | 190 | user_text = comp.edit.get_edit_text() 191 | user_command_split = user_text.split(' ') 192 | 193 | if len(user_command_split) <= 1: 194 | subcommand = 'list' 195 | else: 196 | subcommand = user_command_split[1] 197 | 198 | if subcommand == 'list': 199 | filters = comp.consolemonitor.filters[:] 200 | 201 | if len(filters) <= 0: 202 | comp.listwalker.append(urwid.Text(('logged_response', 'No filters'))) 203 | comp.listbox.autoscroll() 204 | return 205 | else: 206 | for index, pattern in enumerate(filters): 207 | comp.listwalker.append(urwid.Text(('logged_response', str(index) + '. ' + pattern))) 208 | comp.listbox.autoscroll() 209 | return 210 | 211 | elif subcommand == 'add': 212 | regex = user_command_split[2:] 213 | comp.consolemonitor.filters.append(' '.join(regex)) 214 | 215 | elif subcommand == 'clear': 216 | comp.consolemonitor.filters = [] 217 | 218 | elif subcommand == 'contains': 219 | remaining_commands = user_command_split[2:] 220 | matchstring = ' '.join(remaining_commands) 221 | matchstring_escaped = '.*' + re.escape(matchstring) + '.*' 222 | comp.consolemonitor.filters.append(matchstring_escaped) 223 | 224 | elif subcommand == 'remove': 225 | if len(user_command_split) > 2: 226 | toRemove = int(user_command_split[2]) 227 | 228 | if len(comp.consolemonitor.filters) > toRemove: 229 | comp.consolemonitor.filters.pop(toRemove) 230 | else: 231 | comp.listwalker.append(urwid.Text(('logged_response', 'filter out of range'))) 232 | comp.listbox.autoscroll() 233 | 234 | def gcl(self, comp): 235 | control_points = int(comp.getApiClient().me()['gcl']) 236 | gcl = str(int((control_points/1000000) ** (1/2.4))+1) 237 | comp.listwalker.append(urwid.Text(('logged_response', gcl))) 238 | 239 | def list(self, comp): 240 | command_list = '' 241 | aliases = list(comp.aliases) 242 | builtin_list = [method for method in dir(self) if callable(getattr(self, method))] 243 | commands = builtin_list 244 | 245 | for alias in aliases: 246 | alias_real = comp.aliases[alias] 247 | if alias_real not in builtin_list: 248 | commands.append(alias) 249 | 250 | commands.sort() 251 | commands.reverse() 252 | for builtin_command in commands: 253 | if builtin_command != 'turtle' and not builtin_command.startswith('__'): 254 | command_list = builtin_command + ' ' + command_list 255 | 256 | comp.listwalker.append(urwid.Text(('logged_response', command_list))) 257 | return 258 | 259 | def pause(self, comp): 260 | comp.listbox.setAutoscroll(False) 261 | 262 | def reconnect(self, comp): 263 | comp.consolemonitor.reconnect() 264 | comp.listwalker.append(urwid.Text(('logged_response', 'reconnecting'))) 265 | comp.listbox.autoscroll() 266 | 267 | def shard(self, comp): 268 | userInput = comp.edit 269 | user_text = userInput.get_edit_text() 270 | user_command_split = user_text.split(' ') 271 | 272 | if len(user_command_split) < 2 or user_command_split[1] == 'current': 273 | comp.listwalker.appendText(comp.shard) 274 | return 275 | 276 | if user_command_split[1] == 'clear': 277 | comp.consolemonitor.focus = False 278 | comp.listwalker.appendText('Clearing shard settings') 279 | return 280 | 281 | if user_command_split[1] == 'focus': 282 | if len(user_command_split) < 3: 283 | comp.consolemonitor.focus = False 284 | comp.listwalker.appendText('Disabled shard focus') 285 | else: 286 | shard = user_command_split[2] 287 | comp.consolemonitor.focus = shard 288 | comp.shard = shard 289 | message = 'Enabled shard focus on %s' % (shard) 290 | comp.listwalker.appendText(message) 291 | return 292 | 293 | comp.shard = user_command_split[1] 294 | response = 'Setting active shard to %s' % (comp.shard,) 295 | comp.listwalker.appendText(response) 296 | 297 | def themes(self, comp): 298 | userInput = comp.edit 299 | user_text = userInput.get_edit_text() 300 | user_command_split = user_text.split(' ') 301 | if len(user_command_split) < 2 or user_command_split[1] == 'list': 302 | theme_names = themes.keys() 303 | theme_names.reverse() 304 | theme_list = '' 305 | for theme in theme_names: 306 | theme_list = theme + ' ' + theme_list 307 | 308 | comp.listwalker.appendText(theme_list) 309 | return 310 | 311 | if user_command_split[1] == 'test': 312 | comp.listwalker.append(urwid.Text(('logged_input', 'logged_input'))) 313 | comp.listwalker.append(urwid.Text(('logged_response', 'logged_response'))) 314 | comp.listwalker.append(urwid.Text(('error', 'error'))) 315 | comp.listwalker.append(urwid.Text(('default', 'default'))) 316 | comp.listwalker.append(urwid.Text(('severity0', 'severity0'))) 317 | comp.listwalker.append(urwid.Text(('severity1', 'severity1'))) 318 | comp.listwalker.append(urwid.Text(('severity2', 'severity2'))) 319 | comp.listwalker.append(urwid.Text(('severity3', 'severity3'))) 320 | comp.listwalker.append(urwid.Text(('severity4', 'severity4'))) 321 | comp.listwalker.append(urwid.Text(('severity5', 'severity5'))) 322 | comp.listwalker.append(urwid.Text(('highlight', 'highlight'))) 323 | comp.listbox.set_focus(len(comp.listwalker)-1) 324 | return 325 | 326 | theme = user_command_split[1] 327 | if theme in themes: 328 | comp.loop.screen.register_palette(themes[theme]) 329 | comp.loop.screen.clear() 330 | 331 | return 332 | 333 | def turtle(self, comp): 334 | turtle = ''' 335 | ________________ 336 | < How you doing? > 337 | ---------------- 338 | \ ___-------___ 339 | \ _-~~ ~~-_ 340 | \ _-~ /~-_ 341 | /^\__/^\ /~ \ / \\ 342 | /| O|| O| / \_______________/ \\ 343 | | |___||__| / / \ \\ 344 | | \ / / \ \\ 345 | | (_______) /______/ \_________ \\ 346 | | / / \ / \\ 347 | \ \^\\\ \ / \ / 348 | \ || \______________/ _-_ //\__// 349 | \ ||------_-~~-_ ------------- \ --/~ ~\ || __/ 350 | ~-----||====/~ |==================| |/~~~~~ 351 | (_(__/ ./ / \_\ \. 352 | (_(___/ \_____)_) 353 | ''' 354 | comp.listwalker.appendText(turtle) 355 | comp.listwalker.appendText('') 356 | 357 | def whoami(self, comp): 358 | me = comp.getApiClient().me()['username'] 359 | comp.listwalker.append(urwid.Text(('logged_response', me))) 360 | -------------------------------------------------------------------------------- /screeps_console/console.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from base64 import b64decode 4 | import json 5 | from outputparser import parseLine 6 | from outputparser import tagLine 7 | import screepsapi 8 | import settings 9 | from time import sleep 10 | import websocket 11 | import sys 12 | import zlib 13 | 14 | 15 | class ScreepsConsole(screepsapi.Socket): 16 | format = 'color' 17 | 18 | def set_subscriptions(self): 19 | self.subscribe_user('console') 20 | pass 21 | 22 | 23 | def on_close(self, ws): 24 | print("### closed ###") 25 | self.disconnect() 26 | 27 | 28 | def on_message(self, ws, message): 29 | if (message.startswith('auth ok')): 30 | ws.send('subscribe user:' + self.user_id + '/console') 31 | return 32 | 33 | if (message.startswith('time')): 34 | return 35 | 36 | if (message.startswith('gz')): 37 | try: 38 | decoded = b64decode(message[3:]) 39 | message = zlib.decompress(decoded, 0) 40 | except: 41 | print("Unexpected error:", sys.exc_info()) 42 | return 43 | 44 | data = json.loads(message) 45 | 46 | if 'shard' in data[1]: 47 | shard = data[1]['shard'] 48 | else: 49 | shard = 'shard0' 50 | 51 | 52 | if 'messages' in data[1]: 53 | stream = [] 54 | 55 | messages = data[1]["messages"] 56 | if 'log' in messages: 57 | stream.extend(messages['log']) 58 | 59 | if 'results' in messages: 60 | results = messages['results'] 61 | results = map(lambda x:''+x+'',results) 62 | stream.extend(results) 63 | 64 | message_count = len(stream) 65 | 66 | if message_count > 0: 67 | config = settings.getSettings() 68 | # Make sure the delay doesn't cause an overlap into other ticks 69 | if 'smooth_scroll' in config and config['smooth_scroll'] is True: 70 | message_delay = 0.2 / message_count 71 | if message_delay > 0.07: 72 | message_delay = 0.07 73 | else: 74 | message_delay = 0.00001 75 | 76 | for line in stream: 77 | if self.format == 'color': 78 | line_parsed = '%s: %s' % (shard, parseLine(line)) 79 | elif self.format == 'json': 80 | line_parsed = json.dumps({'line':line,'shard':shard}) 81 | else: 82 | line_parsed = '%s: %s' % (shard, tagLine(line)) 83 | 84 | print(line_parsed) 85 | sys.stdout.flush() 86 | sleep(message_delay) # sleep for smoother scrolling 87 | return 88 | else: 89 | if 'error' in data[1]: 90 | #line = '<'+data[1]['error'] 91 | line = "" + data[1]['error'] + "" 92 | if self.format == 'color': 93 | line_parsed = '%s: %s' % (shard, parseLine(line)) 94 | elif self.format == 'json': 95 | line_parsed = json.dumps({'line':line,'shard':shard}) 96 | else: 97 | line_parsed = '%s: %s' % (shard, tagLine(line)) 98 | 99 | print(line_parsed) 100 | return 101 | else: 102 | print('undiscovered protocol feature') 103 | print(json.dumps(message)) 104 | 105 | print('on_message', message) 106 | 107 | def start(self): 108 | self.connect() 109 | 110 | 111 | if __name__ == "__main__": 112 | 113 | if len(sys.argv) < 2: 114 | server = 'main' 115 | else: 116 | server = sys.argv[1] 117 | 118 | config = settings.getConnection(server) 119 | 120 | if server == 'main' and 'token' not in config: 121 | settings.addConnection('main', config['username'], config['password']) 122 | config = settings.getConnection(server) 123 | 124 | if 'token' in config: 125 | screepsconsole = ScreepsConsole(token=config['token'], secure=config['secure'], host=config['host']) 126 | else: 127 | screepsconsole = ScreepsConsole(user=config['username'], password=config['password'], secure=config['secure'], host=config['host']) 128 | 129 | if len(sys.argv) > 2: 130 | if sys.argv[2] == 'interactive': 131 | screepsconsole.format = 'tag' 132 | if sys.argv[2] == 'json': 133 | screepsconsole.format = 'json' 134 | 135 | screepsconsole.start() 136 | -------------------------------------------------------------------------------- /screeps_console/data/autocomplete/basic.dat: -------------------------------------------------------------------------------- 1 | 2 | # Basic commands - whole line replacement 3 | 4 | about 5 | buffer 6 | disconnect 7 | clear 8 | exit 9 | filter 10 | filter add 11 | filter list 12 | filter clear 13 | filter remove 14 | list 15 | pause 16 | reconnect 17 | themes list 18 | themes test 19 | time 20 | -------------------------------------------------------------------------------- /screeps_console/data/autocomplete/constants.dat: -------------------------------------------------------------------------------- 1 | 2 | 3 | Game.cpu.limit 4 | Game.cpu.tickLimit 5 | Game.cpu.bucket 6 | Game.cpu.getUsed() 7 | Game.creeps 8 | Game.flags 9 | Game.gcl 10 | Game.getObjectById( 11 | Game.map 12 | Game.market 13 | Game.notify( 14 | Game.rooms 15 | Game.spawns 16 | Game.structures 17 | Game.time 18 | 19 | Game.map.describeExits( 20 | Game.map.findExit( 21 | Game.map.findRoute( 22 | Game.map.getRoomLinearDistance( 23 | Game.map.getTerrainAt( 24 | Game.map.isRoomProtected( 25 | 26 | Game.market.incomingTransactions 27 | Game.market.outgoingTransactions 28 | Game.market.myOrders 29 | Game.market.orders 30 | Game.market.cancelOrder( 31 | Game.market.createBuyOrder( 32 | Game.market.createSellOrder( 33 | Game.market.deal( 34 | 35 | PathFinder 36 | PathFinder.search( 37 | PathFinder.user( 38 | PathFinder.CostMatrix( 39 | PathFinder.CostMatrix.deserialize( 40 | 41 | RawMemory.get() 42 | RawMemory.set( 43 | 44 | Room.serializePath( 45 | Room.deserializePath( 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | OK 56 | ERR_NOT_OWNER 57 | ERR_NO_PATH 58 | ERR_NAME_EXISTS 59 | ERR_BUSY 60 | ERR_NOT_FOUND 61 | ERR_NOT_ENOUGH_ENERGY 62 | ERR_NOT_ENOUGH_RESOURCES 63 | ERR_INVALID_TARGET 64 | ERR_FULL 65 | ERR_NOT_IN_RANGE 66 | ERR_INVALID_ARGS 67 | ERR_TIRED 68 | ERR_NO_BODYPART 69 | ERR_NOT_ENOUGH_EXTENSION 70 | ERR_RCL_NOT_ENOUGH 71 | ERR_GCL_NOT_ENOUGH 72 | 73 | FIND_EXIT_TOP 74 | FIND_EXIT_RIGHT 75 | FIND_EXIT_BOTTOM 76 | FIND_EXIT_LEFT 77 | FIND_EXIT 78 | FIND_CREEPS 79 | FIND_MY_CREEPS 80 | FIND_HOSTILE_CREEPS 81 | FIND_SOURCES_ACTIVE 82 | FIND_SOURCES 83 | FIND_DROPPED_ENERGY 84 | FIND_DROPPED_RESOURCES 85 | FIND_STRUCTURES 86 | FIND_MY_STRUCTURES: 108, 87 | FIND_HOSTILE_STRUCTURES 88 | FIND_FLAGS 89 | FIND_CONSTRUCTION_SITES 90 | FIND_MY_SPAWNS 91 | FIND_HOSTILE_SPAWNS 92 | FIND_MY_CONSTRUCTION_SITES 93 | FIND_HOSTILE_CONSTRUCTION_SITES 94 | FIND_MINERALS 95 | FIND_NUKES 96 | 97 | TOP 98 | TOP_RIGHT 99 | RIGHT 100 | BOTTOM_RIGHT 101 | BOTTOM 102 | BOTTOM_LEFT 103 | LEFT 104 | TOP_LEFT 105 | 106 | COLOR_RED 107 | COLOR_PURPLE 108 | COLOR_BLUE 109 | COLOR_CYAN 110 | COLOR_GREEN 111 | COLOR_YELLOW 112 | COLOR_ORANGE 113 | COLOR_BROWN 114 | COLOR_GREY 115 | COLOR_WHITE 116 | 117 | LOOK_CREEPS 118 | LOOK_ENERGY 119 | LOOK_RESOURCES 120 | LOOK_SOURCES 121 | LOOK_MINERALS 122 | LOOK_STRUCTURES 123 | LOOK_FLAGS 124 | LOOK_CONSTRUCTION_SITES 125 | LOOK_NUKES 126 | LOOK_TERRAIN 127 | 128 | OBSTACLE_OBJECT_TYPES 129 | 130 | MOVE 131 | WORK 132 | CARRY 133 | ATTACK 134 | RANGED_ATTACK 135 | TOUGH 136 | HEAL 137 | CLAIM 138 | 139 | BODYPART_COST 140 | 141 | CREEP_LIFE_TIME 142 | CREEP_CLAIM_LIFE_TIME 143 | CREEP_CORPSE_RATE 144 | 145 | CARRY_CAPACITY 146 | HARVEST_POWER 147 | HARVEST_MINERAL_POWER 148 | REPAIR_POWER 149 | DISMANTLE_POWER 150 | BUILD_POWER 151 | ATTACK_POWER 152 | UPGRADE_CONTROLLER_POWER 153 | RANGED_ATTACK_POWER 154 | HEAL_POWER 155 | RANGED_HEAL_POWER 156 | REPAIR_COST 157 | DISMANTLE_COST 158 | 159 | RAMPART_DECAY_AMOUNT 160 | RAMPART_DECAY_TIME 161 | RAMPART_HITS 162 | RAMPART_HITS_MAX 163 | 164 | ENERGY_REGEN_TIME 165 | ENERGY_DECAY 166 | 167 | SPAWN_HITS 168 | SPAWN_ENERGY_START 169 | SPAWN_ENERGY_CAPACITY 170 | CREEP_SPAWN_TIME 171 | 172 | SOURCE_ENERGY_CAPACITY 173 | SOURCE_ENERGY_NEUTRAL_CAPACITY 174 | SOURCE_ENERGY_KEEPER_CAPACITY 175 | 176 | WALL_HITS 177 | WALL_HITS_MAX 178 | 179 | EXTENSION_HITS 180 | EXTENSION_ENERGY_CAPACITY 181 | 182 | ROAD_HITS 183 | ROAD_WEAROUT 184 | ROAD_DECAY_AMOUNT 185 | ROAD_DECAY_TIME 186 | 187 | LINK_HITS 188 | LINK_HITS_MAX 189 | LINK_CAPACITY 190 | LINK_COOLDOWN 191 | LINK_LOSS_RATIO 192 | 193 | STORAGE_CAPACITY 194 | STORAGE_HITS 195 | 196 | STRUCTURE_SPAWN 197 | STRUCTURE_EXTENSION 198 | STRUCTURE_ROAD 199 | STRUCTURE_WALL 200 | STRUCTURE_RAMPART 201 | STRUCTURE_KEEPER_LAIR 202 | STRUCTURE_PORTAL 203 | STRUCTURE_CONTROLLER 204 | STRUCTURE_LINK 205 | STRUCTURE_STORAGE 206 | STRUCTURE_TOWER 207 | STRUCTURE_OBSERVER 208 | STRUCTURE_POWER_BANK 209 | STRUCTURE_POWER_SPAWN 210 | STRUCTURE_EXTRACTOR 211 | STRUCTURE_LAB 212 | STRUCTURE_TERMINAL 213 | STRUCTURE_CONTAINER 214 | STRUCTURE_NUKER 215 | 216 | CONSTRUCTION_COST 217 | CONSTRUCTION_COST_ROAD_SWAMP_RATIO 218 | 219 | CONTROLLER_LEVELS 220 | CONTROLLER_STRUCTURES 221 | CONTROLLER_DOWNGRADE 222 | CONTROLLER_CLAIM_DOWNGRADE 223 | CONTROLLER_RESERVE 224 | CONTROLLER_RESERVE_MAX 225 | CONTROLLER_MAX_UPGRADE_PER_TICK 226 | CONTROLLER_ATTACK_BLOCKED_UPGRADE 227 | 228 | TOWER_HITS 229 | TOWER_CAPACITY 230 | TOWER_ENERGY_COST 231 | TOWER_POWER_ATTACK 232 | TOWER_POWER_HEAL 233 | TOWER_POWER_REPAIR 234 | TOWER_OPTIMAL_RANGE 235 | TOWER_FALLOFF_RANGE 236 | TOWER_FALLOFF 237 | 238 | OBSERVER_HITS 239 | OBSERVER_RANGE 240 | 241 | POWER_BANK_HITS 242 | POWER_BANK_CAPACITY_MAX 243 | POWER_BANK_CAPACITY_MIN 244 | POWER_BANK_CAPACITY_CRIT 245 | POWER_BANK_DECAY 246 | POWER_BANK_HIT_BACK 247 | 248 | POWER_SPAWN_HITS 249 | POWER_SPAWN_ENERGY_CAPACITY 250 | POWER_SPAWN_POWER_CAPACITY 251 | POWER_SPAWN_ENERGY_RATIO 252 | 253 | EXTRACTOR_HITS 254 | 255 | LAB_HITS 256 | LAB_MINERAL_CAPACITY 257 | LAB_ENERGY_CAPACITY 258 | LAB_BOOST_ENERGY 259 | LAB_BOOST_MINERAL 260 | LAB_COOLDOWN 261 | 262 | GCL_POW 263 | GCL_MULTIPLY 264 | GCL_NOVICE 265 | 266 | MODE_SIMULATION 267 | MODE_SURVIVAL 268 | MODE_WORLD 269 | MODE_ARENA 270 | 271 | TERRAIN_MASK_WALL 272 | TERRAIN_MASK_SWAMP 273 | TERRAIN_MASK_LAVA 274 | 275 | MAX_CONSTRUCTION_SITES 276 | MAX_CREEP_SIZE 277 | 278 | MINERAL_REGEN_TIME 279 | MINERAL_MIN_AMOUNT["H"] 280 | MINERAL_MIN_AMOUNT["O"] 281 | MINERAL_MIN_AMOUNT["L"] 282 | MINERAL_MIN_AMOUNT["K"] 283 | MINERAL_MIN_AMOUNT["Z"] 284 | MINERAL_MIN_AMOUNT["U"] 285 | MINERAL_MIN_AMOUNT["X"] 286 | MINERAL_RANDOM_FACTOR 287 | 288 | TERMINAL_CAPACITY 289 | TERMINAL_HITS 290 | TERMINAL_SEND_COST 291 | TERMINAL_MIN_SEND 292 | 293 | CONTAINER_HITS 294 | CONTAINER_CAPACITY 295 | CONTAINER_DECAY 296 | CONTAINER_DECAY_TIME 297 | CONTAINER_DECAY_TIME_OWNED 298 | 299 | NUKER_HITS 300 | NUKER_COOLDOWN 301 | NUKER_ENERGY_CAPACITY 302 | NUKER_GHODIUM_CAPACITY 303 | NUKE_LAND_TIME 304 | NUKE_RANGE 305 | NUKE_DAMAGE 306 | 307 | PORTAL_DECAY 308 | 309 | RESOURCE_ENERGY 310 | RESOURCE_POWER 311 | 312 | RESOURCE_HYDROGEN 313 | RESOURCE_OXYGEN 314 | RESOURCE_UTRIUM 315 | RESOURCE_LEMERGIUM 316 | RESOURCE_KEANIUM 317 | RESOURCE_ZYNTHIUM 318 | RESOURCE_CATALYST 319 | RESOURCE_GHODIUM 320 | 321 | RESOURCE_HYDROXIDE 322 | RESOURCE_ZYNTHIUM_KEANITE 323 | RESOURCE_UTRIUM_LEMERGITE 324 | 325 | RESOURCE_UTRIUM_HYDRIDE 326 | RESOURCE_UTRIUM_OXIDE 327 | RESOURCE_KEANIUM_HYDRIDE 328 | RESOURCE_KEANIUM_OXIDE 329 | RESOURCE_LEMERGIUM_HYDRIDE 330 | RESOURCE_LEMERGIUM_OXIDE 331 | RESOURCE_ZYNTHIUM_HYDRIDE 332 | RESOURCE_ZYNTHIUM_OXIDE 333 | RESOURCE_GHODIUM_HYDRIDE 334 | RESOURCE_GHODIUM_OXIDE 335 | 336 | RESOURCE_UTRIUM_ACID 337 | RESOURCE_UTRIUM_ALKALIDE 338 | RESOURCE_KEANIUM_ACID 339 | RESOURCE_KEANIUM_ALKALIDE 340 | RESOURCE_LEMERGIUM_ACID 341 | RESOURCE_LEMERGIUM_ALKALIDE 342 | RESOURCE_ZYNTHIUM_ACID 343 | RESOURCE_ZYNTHIUM_ALKALIDE 344 | RESOURCE_GHODIUM_ACID 345 | RESOURCE_GHODIUM_ALKALIDE 346 | 347 | RESOURCE_CATALYZED_UTRIUM_ACID 348 | RESOURCE_CATALYZED_UTRIUM_ALKALIDE 349 | RESOURCE_CATALYZED_KEANIUM_ACID 350 | RESOURCE_CATALYZED_KEANIUM_ALKALIDE 351 | RESOURCE_CATALYZED_LEMERGIUM_ACID 352 | RESOURCE_CATALYZED_LEMERGIUM_ALKALIDE 353 | RESOURCE_CATALYZED_ZYNTHIUM_ACID 354 | RESOURCE_CATALYZED_ZYNTHIUM_ALKALIDE 355 | RESOURCE_CATALYZED_GHODIUM_ACID 356 | RESOURCE_CATALYZED_GHODIUM_ALKALIDE 357 | 358 | BODYPARTS_ALL 359 | RESOURCES_ALL 360 | COLORS_ALL 361 | -------------------------------------------------------------------------------- /screeps_console/data/autocomplete/properties.dat: -------------------------------------------------------------------------------- 1 | 2 | # Object properties - triggered by user input in '.' 3 | 4 | 5 | # Creep Properties - 6 | 7 | body 8 | carry 9 | carryCapacity 10 | fatigue 11 | hits 12 | hitsMax 13 | id 14 | memory 15 | my 16 | name 17 | owner 18 | spawning 19 | ticksToLive 20 | attack( 21 | attackController( 22 | build( 23 | cancelOrder( 24 | dismantle( 25 | drop( 26 | getActiveBodyparts( 27 | harvest( 28 | heal( 29 | move( 30 | move( 31 | moveByPath( 32 | moveTo( 33 | notifyWhenAttacked( 34 | pickup( 35 | rangedAttack( 36 | rangedHeal( 37 | rangedMassAttack( 38 | repair( 39 | reserveController( 40 | say( 41 | suicide( 42 | transfer( 43 | upgradeController( 44 | 45 | 46 | 47 | # Flag 48 | 49 | color 50 | memory 51 | name 52 | secondaryColor 53 | remove( 54 | setColor( 55 | setPosition( 56 | 57 | 58 | # Market 59 | 60 | incomingTransactions 61 | outgoingTransactions 62 | myOrders 63 | orders 64 | cancelOrder( 65 | createBuyOrder( 66 | createSellOrder( 67 | deal( 68 | 69 | 70 | # Mineral 71 | 72 | mineralAmount 73 | mineralType 74 | id 75 | ticksToRegeneration 76 | 77 | 78 | # Owned Structure 79 | 80 | my 81 | owner 82 | 83 | 84 | # PathFinder Costmatrix 85 | 86 | set( 87 | get( 88 | clone() 89 | serialize() 90 | 91 | 92 | # Resource 93 | 94 | amount 95 | id 96 | resourceType 97 | 98 | 99 | # Room 100 | 101 | controller 102 | energyAvailable 103 | energyCapacityAvailable 104 | memory 105 | mode 106 | name 107 | storage 108 | terminal 109 | createConstructionSite( 110 | createFlag( 111 | find( 112 | findExitTo( 113 | findPath( 114 | getPositionAt( 115 | lookAt( 116 | lookAtArea( 117 | lookForAt( 118 | lookForAtArea( 119 | 120 | 121 | # RoomObject 122 | 123 | pos 124 | room 125 | 126 | 127 | # RoomPosition 128 | 129 | roomName 130 | x 131 | y 132 | createConstructionSite( 133 | createFlag( 134 | findClosestByPath 135 | findClosestByRange 136 | findInRange 137 | findPathTo 138 | getDirectionTo 139 | getRangeTo 140 | inRangeTo 141 | isEqualTo 142 | isNearTo 143 | look 144 | lookFor 145 | 146 | 147 | # Source 148 | 149 | energy 150 | energyCapacity 151 | id 152 | ticksToRegeneration 153 | 154 | 155 | # Structure 156 | 157 | hits 158 | hitsMax 159 | id 160 | structureType 161 | destroy( 162 | isActive( 163 | notifyWhenAttacked( 164 | 165 | 166 | # StructureContainer 167 | 168 | store 169 | storeCapacity 170 | transfer( 171 | 172 | 173 | # StructureController 174 | 175 | level 176 | progress 177 | progressTotal 178 | reservation 179 | ticksToDowngrade 180 | upgradeBlocked 181 | unclaim( 182 | 183 | 184 | # StructureExtension 185 | 186 | energy 187 | energyCapacity 188 | transferEnergy( 189 | 190 | 191 | # StructureExtractor 192 | 193 | 194 | # StructureKeeperLair 195 | 196 | ticksToSpawn 197 | 198 | 199 | # StructureLab 200 | 201 | cooldown 202 | energy 203 | energyCapacity 204 | mineralAmount 205 | mineralType 206 | mineralCapacity 207 | boostCreep( 208 | runReaction( 209 | transfer( 210 | 211 | 212 | # StructureLink 213 | 214 | cooldown 215 | energy 216 | energyCapacity 217 | transferEnergy( 218 | 219 | 220 | # StructureNuker 221 | 222 | energy 223 | energyCapacity 224 | ghodium 225 | ghodiumCapacity 226 | cooldown 227 | launchNuke( 228 | 229 | 230 | # StructureObserver 231 | 232 | observeRoom( 233 | 234 | 235 | # StructurePowerBank 236 | 237 | power 238 | ticksToDecay 239 | 240 | 241 | # StructurePowerSpawn 242 | 243 | energy 244 | energyCapacity 245 | power 246 | powerCapacity 247 | 248 | createPowerCreep( 249 | processPower( 250 | transferEnergy( 251 | 252 | 253 | # StructurePortal 254 | 255 | destination 256 | ticksToDecay 257 | 258 | 259 | # StructureRampart 260 | 261 | ticksToDecay 262 | 263 | 264 | # StructureRoad 265 | 266 | ticksToDecay 267 | 268 | 269 | # StructureSpawn 270 | 271 | energy 272 | energyCapacity 273 | memory 274 | name 275 | spawning 276 | canCreateCreep( 277 | createCreep( 278 | recycleCreep( 279 | renewCreep( 280 | transferEnergy( 281 | 282 | 283 | # StructureStorage 284 | 285 | store 286 | storeCapacity 287 | transfer( 288 | 289 | 290 | # StructureTerminal 291 | store 292 | storeCapacity 293 | send( 294 | transfer( 295 | 296 | 297 | # StructureTower 298 | 299 | energy 300 | energyCapacity 301 | attack( 302 | heal( 303 | repair( 304 | transferEnergy( 305 | 306 | 307 | # StructureWall 308 | 309 | ticksToLive 310 | 311 | -------------------------------------------------------------------------------- /screeps_console/interactive.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import atexit 4 | import command 5 | import json 6 | import logging 7 | import os 8 | from os.path import expanduser 9 | import outputparser 10 | import re 11 | import settings 12 | import signal 13 | import subprocess 14 | import sys 15 | from themes import themes 16 | import urwid 17 | 18 | if hasattr(__builtins__, 'raw_input'): 19 | input = raw_input 20 | 21 | class ScreepsInteractiveConsole: 22 | 23 | consoleWidget = False 24 | listWalker = False 25 | userInput = False 26 | consoleMonitor = False 27 | 28 | def __init__(self, connection_name): 29 | try: 30 | self.connection_name = connection_name 31 | frame = self.getFrame() 32 | comp = self.getCommandProcessor() 33 | self.loop = urwid.MainLoop(urwid.AttrMap(frame, 'bg'), 34 | unhandled_input=comp.onInput, 35 | palette=themes['dark']) 36 | 37 | self.consoleMonitor = ScreepsConsoleMonitor(connection_name, 38 | self.consoleWidget, 39 | self.listWalker, 40 | self.loop) 41 | 42 | comp.setDisplayWidgets(self.loop, 43 | frame, 44 | self.getConsole(), 45 | self.getConsoleListWalker(), 46 | self.getEdit(), 47 | self.consoleMonitor) 48 | self.loop.run() 49 | except KeyboardInterrupt: 50 | exit(0) 51 | 52 | 53 | def getFrame(self): 54 | urwid.AttrMap(self.getEdit(), 'input') 55 | frame_widget = urwid.Frame( 56 | header=self.getHeader(), 57 | body=self.getConsole(), 58 | footer=urwid.AttrMap(self.getEdit(), 'input'), 59 | focus_part='footer') 60 | return frame_widget 61 | 62 | 63 | def getHeader(self): 64 | return urwid.AttrMap(urwid.Text("%s - Screeps Interactive Console" % (self.connection_name,), align='center'), 'header') 65 | 66 | def getEdit(self): 67 | if not self.userInput: 68 | self.userInput = consoleEdit("> ") 69 | return self.userInput 70 | 71 | def getConsole(self): 72 | if not self.consoleWidget: 73 | self.consoleWidget = consoleWidget(self.getConsoleListWalker()) 74 | return self.consoleWidget 75 | 76 | def getConsoleListWalker(self): 77 | if not self.listWalker: 78 | self.listWalker = consoleWalker([self.getWelcomeMessage()]) 79 | config = settings.getSettings() 80 | if 'max_buffer' in config: 81 | self.listWalker.max_buffer = config['max_buffer'] 82 | else: 83 | self.listWalker.max_buffer = 200000 84 | 85 | return self.listWalker 86 | 87 | def getCommandProcessor(self): 88 | return command.Processor(self.connection_name) 89 | 90 | def getWelcomeMessage(self): 91 | return urwid.Text(('default', 'Welcome to the Screeps Interactive Console')) 92 | 93 | 94 | class consoleWidget(urwid.ListBox): 95 | 96 | _autoscroll = True 97 | 98 | 99 | def setAutoscroll(self, option): 100 | self._autoscroll = option 101 | 102 | 103 | def autoscroll(self): 104 | if(self._autoscroll): 105 | self.scrollBottom() 106 | 107 | def scrollBottom(self): 108 | self._autoscroll = True 109 | if len(self.body) > 0: 110 | self.set_focus(len(self.body)-1) 111 | 112 | def scrollUp(self, quantity): 113 | self.setAutoscroll(False) 114 | new_pos = self.focus_position - quantity 115 | if new_pos < 0: 116 | new_pos = 0 117 | self.set_focus(new_pos) 118 | 119 | 120 | def scrollDown(self, quantity): 121 | self.setAutoscroll(False) 122 | max_pos = len(self.body)-1 123 | new_pos = self.focus_position + quantity 124 | if new_pos > max_pos: 125 | self.setAutoscroll(True) 126 | new_pos = max_pos 127 | self.set_focus(new_pos) 128 | 129 | 130 | 131 | class consoleWalker(urwid.SimpleListWalker): 132 | 133 | def appendText(self, message, format='logged_response'): 134 | self.append(urwid.Text((format, message))) 135 | 136 | def append(self, value): 137 | if(len(self) >= self.max_buffer): 138 | self.pop(0) 139 | 140 | return super(consoleWalker, self).append(value) 141 | 142 | 143 | class consoleEdit(urwid.Edit): 144 | 145 | inputBuffer = [] 146 | inputOffset = 0 147 | 148 | def __init__(self, caption=u'', edit_text=u'', multiline=False, align='left', wrap='space', allow_tab=False, edit_pos=None, layout=None, mask=None): 149 | path = expanduser('~') + '/.screeps_history' 150 | if os.path.isfile(path): 151 | with open(path, 'r') as myfile: 152 | file_contents = myfile.read() 153 | self.inputBuffer = file_contents.splitlines() 154 | self.inputBuffer.reverse() 155 | 156 | return super(consoleEdit, self).__init__(caption, edit_text, multiline, align, wrap, allow_tab, edit_pos, layout, mask) 157 | 158 | def bufferInput(self, text): 159 | if len(text) < 1: 160 | return 161 | path = expanduser('~') + '/.screeps_history' 162 | history_file = open(path, 'a') 163 | history_file.write(text + "\n") 164 | self.inputBuffer.insert(0, text) 165 | self.manageBufferHistory() 166 | 167 | def manageBufferHistory(self): 168 | path = expanduser('~') + '/.screeps_history' 169 | with open(path, 'r') as myfile: 170 | file_contents = myfile.read() 171 | file_contents_line = file_contents.splitlines() 172 | num_lines = len(file_contents_line) 173 | config = settings.getSettings() 174 | if 'max_history' in config: 175 | max_scroll = config['max_history'] 176 | else: 177 | max_scroll = 200000 178 | 179 | if num_lines > max_scroll: 180 | truncate = num_lines - max_scroll 181 | list_copy = file_contents_line[:] 182 | list_copy = [s + "\n" for s in list_copy] 183 | open(path, 'w').writelines(list_copy[truncate+1:]) 184 | 185 | def keypress(self, size, key): 186 | 187 | if key == 'enter': 188 | edit_text = self.get_edit_text() 189 | self.bufferInput(edit_text) 190 | self.inputOffset = 0 191 | return super(consoleEdit, self).keypress(size, key) 192 | 193 | if key == 'ctrl a': 194 | self.edit_pos = 0 195 | return 196 | 197 | if key == 'ctrl e': 198 | edit_text = self.get_edit_text() 199 | self.edit_pos = len(edit_text) 200 | return 201 | 202 | if key == 'ctrl u': 203 | self.set_edit_text('') 204 | self.edit_pos = 0 205 | return 206 | 207 | if key == 'up': 208 | bufferLength = len(self.inputBuffer) 209 | if bufferLength > 0: 210 | self.inputOffset += 1 211 | if self.inputOffset > bufferLength: 212 | self.inputOffset = bufferLength 213 | 214 | index = self.inputOffset-1 215 | new_text = self.inputBuffer[index] 216 | self.set_edit_text(new_text) 217 | self.edit_pos = len(new_text) 218 | return 219 | 220 | if key == 'down': 221 | bufferLength = len(self.inputBuffer) 222 | if bufferLength > 0: 223 | self.inputOffset -= 1 224 | if self.inputOffset < 0: 225 | self.inputOffset = 0 226 | 227 | if self.inputOffset == 0: 228 | new_text = '' 229 | else: 230 | index = self.inputOffset-1 231 | new_text = self.inputBuffer[index] 232 | 233 | self.set_edit_text(new_text) 234 | self.edit_pos = len(new_text) 235 | return 236 | 237 | return super(consoleEdit, self).keypress(size, key) 238 | 239 | 240 | class ScreepsConsoleMonitor: 241 | 242 | proc = False 243 | quiet = False 244 | focus = False 245 | filters = [] 246 | 247 | def __init__(self, connectionname, widget, walker, loop): 248 | self.connectionname = connectionname 249 | self.widget = widget 250 | self.walker = walker 251 | self.loop = loop 252 | self.buffer = '' 253 | self.getProcess() 254 | atexit.register(self.__del__) 255 | 256 | def getProcess(self): 257 | if self.proc: 258 | return self.proc 259 | console_path = os.path.join(os.path.dirname(sys.argv[0]), 'console.py ') 260 | write_fd = self.loop.watch_pipe(self.onUpdate) 261 | self.proc = subprocess.Popen( 262 | [sys.executable + ' ' + console_path + ' ' + self.connectionname + ' json'], 263 | stdout=write_fd, 264 | preexec_fn=os.setsid, 265 | close_fds=True, 266 | shell=True) 267 | return self.proc 268 | 269 | 270 | def reconnect(self): 271 | self.disconnect() 272 | self.getProcess() 273 | 274 | def disconnect(self): 275 | if self.proc: 276 | try: 277 | os.killpg(os.getpgid(self.proc.pid), signal.SIGTERM) 278 | except: 279 | pass 280 | self.proc = False 281 | 282 | def onUpdate(self, data): 283 | # If we lose the connection to the remote system close the console. 284 | if data.startswith(b'### closed ###'): 285 | self.proc = False 286 | self.getProcess() 287 | lostprocess_message = 'reconnecting to server . . .' 288 | self.walker.append(urwid.Text(('logged_response', lostprocess_message))) 289 | self.widget.set_focus(len(self.walker)-1) 290 | return 291 | if data[-1:] != b'\n': 292 | self.buffer += data.decode("utf-8") 293 | return 294 | if len(self.buffer) > 0: 295 | data = (self.buffer + data.decode("utf-8")).encode("utf-8") 296 | self.buffer = '' 297 | data_lines = data.decode("utf-8").rstrip().split('\n') 298 | for line_json in data_lines: 299 | try: 300 | if len(line_json) <= 0: 301 | continue 302 | try: 303 | line_data = json.loads(line_json.strip()) 304 | line = line_data['line'] 305 | shard = line_data['shard'] 306 | 307 | if self.focus and self.focus != line_data['shard']: 308 | continue 309 | 310 | 311 | except: 312 | print(line_json) 313 | logging.exception('error processing data: ' + line_json) 314 | continue 315 | 316 | log_type = outputparser.getType(line) 317 | 318 | if self.quiet and log_type != 'result': 319 | return 320 | 321 | if log_type == 'result': 322 | formatting = 'logged_response' 323 | elif log_type == 'highlight': 324 | formatting = 'highlight' 325 | elif log_type == 'error': 326 | formatting = 'error' 327 | else: 328 | severity = outputparser.getSeverity(line) 329 | if not severity or severity > 5 or severity < 0: 330 | severity = 2 331 | formatting = 'severity' + str(severity) 332 | 333 | line = line.replace(' ', " ") 334 | line = outputparser.clearTags(line) 335 | 336 | if line.startswith('ScreepStats: Processed'): 337 | return 338 | 339 | if line.startswith('STATS'): 340 | return 341 | 342 | if log_type == 'log' and len(self.filters) > 0: 343 | has_match = False 344 | 345 | for pattern in self.filters: 346 | try: 347 | match = re.search(pattern, line) 348 | except: 349 | e = sys.exc_info()[0] 350 | logging.exception('dammit') 351 | 352 | if match is not None: 353 | has_match = True 354 | break 355 | 356 | if not has_match: 357 | continue 358 | 359 | self.walker.append(urwid.Text((formatting, '%s: %s' % (shard, line)))) 360 | self.widget.autoscroll() 361 | 362 | except: 363 | logging.exception('error processing data') 364 | 365 | def __del__(self): 366 | if self.proc: 367 | try: 368 | os.killpg(os.getpgid(self.proc.pid), signal.SIGTERM) 369 | except: 370 | pass 371 | 372 | 373 | if __name__ == "__main__": 374 | 375 | if len(sys.argv) < 2: 376 | server = 'main' 377 | else: 378 | server = sys.argv[1] 379 | 380 | if server == 'clear': 381 | if len(sys.argv) < 3: 382 | server = 'main' 383 | else: 384 | server = sys.argv[2] 385 | settings.removeConnection(server) 386 | sys.exit(0) 387 | 388 | connectionSettings = settings.getConnection(server) 389 | 390 | if not connectionSettings: 391 | if server == 'main' or server == 'ptr': 392 | legacyConfig = settings.getLegacySettings() 393 | if legacyConfig: 394 | if input("Upgrade settings file to the new format? (y/n) ") == "y": 395 | settings.addConnection('main', legacyConfig['screeps_username'], legacyConfig['screeps_password']) 396 | config = settings.getSettings() 397 | config['smooth_scroll'] = legacyConfig['smooth_scroll'] 398 | config['max_scroll'] = legacyConfig['max_scroll'] 399 | config['max_history'] = legacyConfig['max_history'] 400 | settings.saveSettings(config) 401 | connectionSettings = settings.getConnection(server) 402 | 403 | if not connectionSettings: 404 | if server is 'main': 405 | host = 'screeps.com' 406 | secure = True 407 | else: 408 | host = input("Host: ") 409 | secure = input("Secure (y/n) ") == "y" 410 | username = input("Username: ") 411 | password = input("Password: ") 412 | settings.addConnection(server, username, password, host, secure) 413 | 414 | if server == 'main' and 'token' not in connectionSettings: 415 | settings.addConnection('main', connectionSettings['username'], connectionSettings['password']) 416 | connectionSettings = settings.getConnection(server) 417 | 418 | ScreepsInteractiveConsole(server) 419 | -------------------------------------------------------------------------------- /screeps_console/outputparser.py: -------------------------------------------------------------------------------- 1 | 2 | import colorama 3 | from colorama import Fore, Back, Style 4 | import re 5 | 6 | colorama.init() 7 | SEVERITY_RE = re.compile(r'<.*severity="(\d)".*>') 8 | TYPE_RE = re.compile(r'<.*type="([a-zA-Z0-9]*)".*>') 9 | TAG_RE = re.compile(r'<[^>]+>') 10 | 11 | def parseLine(line): 12 | severity = getSeverity(line) 13 | 14 | # Add color based on severity 15 | if 'severity' not in locals(): 16 | severity = 3 17 | 18 | if severity == 0: 19 | color = Style.DIM + Fore.WHITE 20 | elif severity == 1: 21 | color = Style.NORMAL + Fore.BLUE 22 | elif severity == 2: 23 | color = Style.NORMAL + Fore.CYAN 24 | elif severity == 3: 25 | color = Style.NORMAL + Fore.WHITE 26 | elif severity == 4: 27 | color = Style.NORMAL + Fore.RED 28 | elif severity == 5: 29 | color = Style.NORMAL + Fore.BLACK + Back.RED 30 | else: 31 | color = Style.NORMAL + Fore.BLACK + Back.YELLOW 32 | 33 | # Replace html tab entity with actual tabs 34 | line = clearTags(line) 35 | line = line.replace(' ', "\t") 36 | return color + line + Style.RESET_ALL 37 | 38 | 39 | def tagLine(line): 40 | severity = str(getSeverity(line)) 41 | log_type = getType(line) 42 | line = clearTags(line) 43 | return '' + line + '' 44 | 45 | 46 | def clearTags(line): 47 | try: 48 | line = TAG_RE.sub('', line) 49 | return line 50 | except: 51 | return line 52 | 53 | 54 | def getSeverity(line): 55 | if '<' in line: 56 | try: 57 | match_return = SEVERITY_RE.match(line) 58 | groups = match_return.groups() 59 | if len(groups) > 0: 60 | return int(groups[0]) 61 | else: 62 | return 3 63 | except: 64 | return 3 65 | else: 66 | return 3 67 | 68 | 69 | def getType(line): 70 | if '<' in line: 71 | try: 72 | match_return = TYPE_RE.match(line) 73 | groups = match_return.groups() 74 | if len(groups) > 0: 75 | return groups[0] 76 | else: 77 | return 'log' 78 | except: 79 | return 'log' 80 | else: 81 | return 'log' 82 | -------------------------------------------------------------------------------- /screeps_console/settings.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | import json 3 | import os 4 | from os.path import expanduser 5 | import yaml 6 | import screepsapi 7 | import requests 8 | 9 | 10 | userhome = expanduser('~') 11 | settingsfile = userhome + '/.screepsconsole.yaml' 12 | 13 | 14 | def getSettings(): 15 | if not os.path.isfile(settingsfile): 16 | settings = { 17 | 'connections': {}, 18 | 'max_history': 200000, 19 | 'max_scroll': 200000, 20 | 'smooth_scroll': True 21 | } 22 | saveSettings(settings) 23 | return settings 24 | with open(settingsfile, 'r') as f: 25 | settings = yaml.load(f) 26 | return settings 27 | 28 | 29 | def getConnection(name): 30 | settings = getSettings() 31 | 32 | if not settings: 33 | return False 34 | 35 | if 'connections' not in settings: 36 | return False 37 | 38 | if name not in settings['connections']: 39 | return False 40 | 41 | return settings['connections'][name] 42 | 43 | 44 | def addConnection(name, username, password, host=False, secure=False): 45 | if name == 'main': 46 | secure = True 47 | host = 'screeps.com' 48 | #addConnection('ptr', username, password) 49 | if name == 'ptr': 50 | secure = True 51 | host = 'screeps.com/ptr' 52 | 53 | settings = getSettings() 54 | if not settings: 55 | settings = {} 56 | 57 | if 'connections' not in settings: 58 | settings['connections'] = {} 59 | 60 | if name == 'main' or name == 'ptr': 61 | token = getToken(username, password, host, secure) 62 | settings['connections'][name] = { 63 | 'host': host, 64 | 'secure': secure, 65 | 'token': token 66 | } 67 | else: 68 | settings['connections'][name] = { 69 | 'host': host, 70 | 'secure': secure, 71 | 'username': username, 72 | 'password': password 73 | } 74 | 75 | saveSettings(settings) 76 | 77 | 78 | def getToken(username, password, host, secure): 79 | if secure: 80 | apiurl = 'https://%s/api/user/auth-token' % (host) 81 | else: 82 | apiurl = 'http://%s/api/user/auth-token' % (host) 83 | 84 | authtype = {"type": "full", 85 | "endpoints": { 86 | "GET /api/user/name": False, 87 | "GET /api/user/money-history": False, 88 | "GET /api/market/my-orders": False, 89 | "GET /api/user/memory": False, 90 | "GET /api/user/memory-segment": False, 91 | "POST /api/user/memory-segment": False}, 92 | "websockets": { 93 | "console": False, 94 | "rooms": False}, 95 | "memorySegments": "" 96 | } 97 | r = requests.post(apiurl, data=authtype, auth=(username, password)) 98 | r.raise_for_status() 99 | apiret = json.loads(r.text, object_pairs_hook=OrderedDict) 100 | return apiret['token'] 101 | 102 | 103 | def removeConnection(name): 104 | if name == 'main': 105 | removeConnection('ptr') 106 | if not getConnection(name): 107 | return False 108 | config = getSettings() 109 | del config['connections'][name] 110 | saveSettings(config) 111 | 112 | 113 | def saveSettings(settings): 114 | with open(settingsfile, 'w') as outfile: 115 | yaml.dump(settings, outfile, default_flow_style=False) 116 | 117 | 118 | def getApiClient(name): 119 | settings = getConnection(name) 120 | if 'token' in settings: 121 | return screepsapi.API( 122 | token=settings['token'], 123 | host=settings['host'], 124 | secure=settings['secure'], 125 | ) 126 | else: 127 | return screepsapi.API( 128 | u=settings['username'], 129 | p=settings['password'], 130 | host=settings['host'], 131 | secure=settings['secure'], 132 | ) 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | def getLegacySettings(): 141 | if not getLegacySettings.settings: 142 | cwd = os.getcwd() 143 | path = cwd + '/.settings.yaml' 144 | 145 | if not os.path.isfile(path): 146 | path = cwd + '/.screeps_settings.yaml' 147 | 148 | if not os.path.isfile(path): 149 | path = expanduser('~') + '/.screeps_settings.yaml' 150 | 151 | if not os.path.isfile(path): 152 | return False 153 | 154 | with open(path, 'r') as f: 155 | getLegacySettings.settings = yaml.load(f) 156 | 157 | return getLegacySettings.settings 158 | 159 | getLegacySettings.settings = False 160 | -------------------------------------------------------------------------------- /screeps_console/themes.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | # (StyleLabel, Background16, Foreground16, monochrome, Background256, Foreground256) 7 | # Monochrome can be used to define styles (bold, underline, standout) only. 8 | # 256 color is optional. 9 | # http://urwid.org/manual/displayattributes.html 10 | 11 | themes = { 12 | 'dark':[ 13 | ('bg', 'dark blue', 'black'), 14 | ('header', 'white', 'dark gray'), 15 | ('input', 'white', 'dark blue'), 16 | 17 | ('logged_input', 'dark magenta', 'black'), 18 | ('logged_response', 'light magenta', 'black'), 19 | 20 | ('error', 'yellow', 'dark red'), 21 | ('default', 'light gray', 'black'), 22 | ('severity0', 'dark blue', 'black'), 23 | ('severity1', 'dark green', 'black'), 24 | ('severity2', 'dark cyan', 'black'), 25 | ('severity3', 'light gray', 'black'), 26 | ('severity4', 'light red', 'black'), 27 | ('severity5', 'yellow', 'dark red'), 28 | ('highlight', 'black', 'yellow'), 29 | ], 30 | 31 | 'light':[ 32 | ('bg', 'dark blue', 'white'), 33 | ('header', 'black', 'light gray'), 34 | ('input', 'black', 'light gray'), 35 | 36 | ('logged_input', 'dark magenta', 'white'), 37 | ('logged_response', 'light magenta', 'white'), 38 | 39 | ('error', 'light red', 'yellow'), 40 | ('default', 'black', 'white'), 41 | ('severity0', 'light gray', 'white'), 42 | ('severity1', 'dark gray', 'white'), 43 | ('severity2', 'dark blue', 'white'), 44 | ('severity3', 'black', 'white'), 45 | ('severity4', 'dark red', 'white'), 46 | ('severity5', 'light red', 'white'), 47 | ('highlight', 'black', 'yellow'), 48 | ], 49 | 50 | } 51 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | try: 2 | from setuptools import setup 3 | except ImportError: 4 | from distutils.core import setup 5 | 6 | config = { 7 | 'description': 'Screeps Console', 8 | 'author': 'Robert Hafner', 9 | 'url': 'https://github.com/tedivm/screeps_console', 10 | 'download_url': 'https://github.com/tedivm/screeps_console/releases', 11 | 'author_email': 'tedivm@tedivm.com', 12 | 'version': '0.1', 13 | 'install_requires': ['nose'], 14 | 'packages': ['screeps_console'], 15 | 'scripts': [], 16 | 'name': 'screeps_console' 17 | } 18 | 19 | setup(**config) 20 | --------------------------------------------------------------------------------