├── .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 | 
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 |
--------------------------------------------------------------------------------