Skip to content

Commit 9291e5a

Browse files
authored
Merge pull request #136 from evonzee/develop
Docker config, discord error recovery
2 parents b5072a4 + 48738f6 commit 9291e5a

File tree

9 files changed

+120
-9
lines changed

9 files changed

+120
-9
lines changed

.dockerignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.dockerignore
2+
Dockerfile

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,15 @@ nosetests.xml
3434
.mr.developer.cfg
3535
.project
3636
.pydevproject
37+
.vscode
3738

3839
# Project specific
3940
/config/player.dat
4041
/config/player.bak
4142
/config/player.dir
43+
/config/player
4244
/config/config.json
45+
/config/permissions.json
4346
*config.json
4447
*.db
4548
*.db-journal

Dockerfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FROM python:3.5-stretch
2+
3+
RUN pip install discord irc3
4+
5+
RUN mkdir /app /app/defaults
6+
COPY . /app/
7+
COPY config/*.default /app/defaults/
8+
WORKDIR /app
9+
COPY config/permissions.json.default config/permissions.json
10+
11+
VOLUME /app/config
12+
13+
CMD [ "./docker-start.sh" ]

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,50 @@ once you have finised editing `config/config.json` and `config/permissions
160160
.json`. To terminate the proxy, either press `^C` in an interactive
161161
terminal session, or send it an `INT` signal.
162162

163+
### Running under Docker
164+
StarryPy now includes a basic Docker configuration. To run this image,
165+
all you need to do is run:
166+
167+
```bash
168+
docker run -p 21025:21025 starrypy/starrypy:1.0.0
169+
```
170+
171+
StarryPy exports a volume at /app/config which stores your configuration file and
172+
the various databases. You can create your own data container for this volume to persist
173+
your configuration and data even if you rebuild or upgrade StarryPy, or use volume
174+
mount points to share a directory from your host server into the container.
175+
176+
To use a storage volume, create a volume with:
177+
178+
```bash
179+
docker volume create --name sb-data
180+
```
181+
182+
Then provide it as part of your startup command:
183+
184+
```bash
185+
docker run -d --name starry -p 20125:21025 -v sb-data:/app/config starrypy/starrypy:1.0.0
186+
```
187+
188+
You can edit the config by mounting the volume into another container with your favorite text editor:
189+
```bash
190+
docker run --rm -v sb-data:/app/config -ti thinca/vim /app/config/config.json
191+
```
192+
193+
If you'd rather map a directory on your host, just provide that as an argument to -v instead:
194+
195+
```bash
196+
docker run -d --name starry -p 20125:21025 -v /opt/wherever/you/want/it:/app/config starrypy/starrypy:1.0.0
197+
```
198+
199+
You can also run as a low-privileges user, with starry only having access to write to its config volume:
200+
```bash
201+
docker run -d --name starry -p 20125:21025 -v /opt/wherever/you/want/it:/app/config --user 1002 starrypy/starrypy:1.0.0
202+
```
203+
204+
205+
Please note that this is a Linux-based docker container, so it won't work properly on Docker
206+
for Windows in Windows Container mode.
163207

164208
## Contributing
165209
Contributions are highly encouraged and always welcome. Please feel free to

config/config.json.default

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"command_prefix": "/"
2828
},
2929
"discord_bot": {
30+
"enabled": false,
3031
"channel": "-- channel id --",
3132
"staff_channel": "-- channel id --",
3233
"client_id": "-- client_id --",
@@ -43,6 +44,7 @@
4344
},
4445
"help_plugin": {},
4546
"irc_bot": {
47+
"enabled": false,
4648
"announce_join_leave": true,
4749
"channel": "#YourChannel",
4850
"command_prefix": "!",

docker-start.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/bash
2+
3+
# If we run in docker, and the user chose a file share volume, these files won't exist. Starry will complain.
4+
# Rather than change the way starry does its default files, let's just ensure they are present with this shell script.
5+
if [ ! -f config/config.json.default ]; then
6+
cp defaults/config.json.default config
7+
fi
8+
9+
if [ ! -f config/permissions.json.default ]; then
10+
cp defaults/permissions.json.default config
11+
fi
12+
13+
exec python3 ./server.py

plugins/discord_bot.py

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ class DiscordPlugin(BasePlugin, discord.Client):
7272
name = "discord_bot"
7373
depends = ['command_dispatcher']
7474
default_config = {
75+
"enabled": True,
7576
"token": "-- token --",
7677
"client_id": "-- client_id --",
7778
"channel": "-- channel id --",
@@ -87,8 +88,11 @@ class DiscordPlugin(BasePlugin, discord.Client):
8788
def __init__(self):
8889
BasePlugin.__init__(self)
8990
discord.Client.__init__(self)
91+
self.enabled = True
9092
self.token = None
93+
self.channel_id = None
9194
self.channel = None
95+
self.staff_channel_id = None
9296
self.staff_channel = None
9397
self.token = None
9498
self.client_id = None
@@ -111,6 +115,9 @@ def __init__(self):
111115
'shutdown', 'save')
112116

113117
def activate(self):
118+
self.enabled = self.config.get_plugin_config(self.name)["enabled"]
119+
if not self.enabled:
120+
return;
114121
BasePlugin.activate(self)
115122
self.dispatcher = self.plugins.command_dispatcher
116123
self.irc_bot_exists = link_plugin_if_available(self, 'irc_bot')
@@ -122,11 +129,11 @@ def activate(self):
122129
"command_prefix"]
123130
self.token = self.config.get_plugin_config(self.name)["token"]
124131
self.client_id = self.config.get_plugin_config(self.name)["client_id"]
125-
self.channel = self.config.get_plugin_config(self.name)["channel"]
126-
self.staff_channel = self.config.get_plugin_config(self.name)[
132+
self.channel_id = self.config.get_plugin_config(self.name)["channel"]
133+
self.staff_channel_id = self.config.get_plugin_config(self.name)[
127134
"staff_channel"]
128135
self.sc = self.config.get_plugin_config(self.name)["strip_colors"]
129-
asyncio.ensure_future(self.start_bot())
136+
asyncio.ensure_future(self.start_bot()).add_done_callback(self.error_handler)
130137
self.update_id(self.client_id)
131138
self.mock_connection = MockConnection(self)
132139
self.rank_roles = self.config.get_plugin_config(self.name)[
@@ -151,7 +158,9 @@ def on_connect_success(self, data, connection):
151158
:param connection:
152159
:return: Boolean: True. Must be true, so packet moves on.
153160
"""
154-
asyncio.ensure_future(self.make_announce(connection, "joined"))
161+
if not self.enabled:
162+
return True;
163+
asyncio.ensure_future(self.make_announce(connection, "joined")).add_done_callback(self.error_handler)
155164
return True
156165

157166
def on_client_disconnect_request(self, data, connection):
@@ -162,7 +171,9 @@ def on_client_disconnect_request(self, data, connection):
162171
:param connection:
163172
:return: Boolean: True. Must be true, so packet moves on.
164173
"""
165-
asyncio.ensure_future(self.make_announce(connection, "left"))
174+
if not self.enabled:
175+
return True;
176+
asyncio.ensure_future(self.make_announce(connection, "left")).add_done_callback(self.error_handler)
166177
return True
167178

168179
def on_chat_sent(self, data, connection):
@@ -177,6 +188,8 @@ def on_chat_sent(self, data, connection):
177188
:param connection:
178189
:return: Boolean: True. Must be true, so packet moves on.
179190
"""
191+
if not self.enabled:
192+
return True;
180193
if not data["parsed"]["message"].startswith(self.prefix):
181194
msg = data["parsed"]["message"]
182195
if self.sc:
@@ -207,13 +220,14 @@ def start_bot(self):
207220
except Exception as e:
208221
self.logger.exception(e)
209222

223+
210224
def update_id(self, client_id):
211225
self.client_id = client_id
212226

213227
@asyncio.coroutine
214228
def on_ready(self):
215-
self.channel = self.get_channel(self.channel)
216-
self.staff_channel = self.get_channel(self.staff_channel)
229+
self.channel = self.get_channel(self.channel_id)
230+
self.staff_channel = self.get_channel(self.staff_channel_id)
217231
if not self.channel:
218232
self.logger.error("Couldn't get channel! Messages can't be "
219233
"sent! Ensure the channel ID is correct.")
@@ -305,4 +319,12 @@ def bot_write(self, msg, target=None):
305319
target = self.channel
306320
if target is None:
307321
return
308-
asyncio.ensure_future(self.send_message(target, msg))
322+
asyncio.ensure_future(self.send_message(target, msg)).add_done_callback(self.error_handler)
323+
324+
def error_handler(self, future):
325+
try:
326+
future.result()
327+
except Exception as e:
328+
self.logger.error("Caught an unhandled exception in Discord bot. Will restart.")
329+
self.logger.exception(e)
330+
asyncio.ensure_future(self.start_bot())

plugins/irc_bot.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ class IRCPlugin(BasePlugin):
109109
name = "irc_bot"
110110
depends = ['command_dispatcher']
111111
default_config = {
112+
"enabled": True,
112113
"server": "irc.freenode.net",
113114
"channel": "#starrypy",
114115
"username": "starrypy3k_bot",
@@ -122,6 +123,7 @@ class IRCPlugin(BasePlugin):
122123

123124
def __init__(self):
124125
super().__init__()
126+
self.enabled = True
125127
self.server = None
126128
self.channel = None
127129
self.username = None
@@ -142,6 +144,9 @@ def __init__(self):
142144
'user', 'del_player', 'maintenance_mode',
143145
'shutdown', 'save')
144146
def activate(self):
147+
self.enabled = self.config.get_plugin_config(self.name)["enabled"]
148+
if not self.enabled:
149+
return
145150
super().activate()
146151
self.dispatcher = self.plugins.command_dispatcher
147152
self.prefix = self.config.get_plugin_config("command_dispatcher")[
@@ -193,6 +198,8 @@ def on_connect_success(self, data, connection):
193198
:param connection:
194199
:return: Boolean: True. Must be true, so packet moves on.
195200
"""
201+
if not self.enabled:
202+
return True
196203
asyncio.ensure_future(self.announce_join(connection))
197204
return True
198205

@@ -204,6 +211,8 @@ def on_client_disconnect_request(self, data, connection):
204211
:param connection:
205212
:return: Boolean: True. Must be true, so packet moves on.
206213
"""
214+
if not self.enabled:
215+
return True
207216
asyncio.ensure_future(self.announce_leave(connection.player))
208217
return True
209218

@@ -219,6 +228,8 @@ def on_chat_sent(self, data, connection):
219228
:param connection:
220229
:return: Boolean: True. Must be true, so packet moves on.
221230
"""
231+
if not self.enabled:
232+
return True
222233
if not data["parsed"]["message"].startswith(self.prefix):
223234
msg = data["parsed"]["message"]
224235
if self.sc:

server.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ def start_server():
301301
logger = logging.getLogger('starrypy')
302302
logger.setLevel(loglevel)
303303
if DEBUG:
304-
fh_d = logging.FileHandler("debug.log")
304+
fh_d = logging.FileHandler("config/debug.log")
305305
fh_d.setLevel(loglevel)
306306
fh_d.setFormatter(formatter)
307307
aiologger.addHandler(fh_d)
@@ -313,6 +313,7 @@ def start_server():
313313
logger.addHandler(ch)
314314

315315
signal.signal(signal.SIGINT, signal.default_int_handler)
316+
signal.signal(signal.SIGTERM, signal.default_int_handler)
316317

317318
loop = asyncio.get_event_loop()
318319
loop.set_debug(False) # Removed in commit to avoid errors.

0 commit comments

Comments
 (0)