-
Notifications
You must be signed in to change notification settings - Fork 0
/
dstRest.py
293 lines (252 loc) · 9.68 KB
/
dstRest.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# A quick app for command queueing. Mostly RESTful.
# StyleGuide loosely observed: https://google.github.io/styleguide/pyguide.html
# HTTP Status Codes: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
import sys
from datetime import datetime
from bottle import run, route, get, post, request, put, response, static_file
print("-------------")
print("API Reloaded!")
print("-------------")
################ Configurable App Defaults ##############
# Change any options here to set as your app defaults:
default_server = 'MyDediServer'
api_host = "0.0.0.0"
api_port = "10777"
api_reloader = False
######
################ Initialisation ##############
servers = []
truthy = ['true', 't', 'yes', 'y', 'on', '1']
falsy = ['false', 'f', 'no', 'n', 'off', '0']
# Checking for input parameters which override the above defaults
arg_index = 1
arg_count = len(sys.argv) # includes arg0 which is the name of the script!
while ( arg_index < arg_count ):
my_arg = sys.argv[arg_index]
next_arg = sys.argv[arg_index+1] if arg_index+1 < arg_count else None
print ("Parameter %i: %s" % (arg_index, my_arg))
if my_arg.lower().startswith('--server=')==True:
default_server = my_arg[9:]
elif my_arg.lower()=='--server':
if next_arg is not None: default_server = next_arg
if my_arg.lower().startswith('--host=')==True:
api_host = my_arg[7:]
elif my_arg.lower()=='--host':
if next_arg is not None: api_host = next_arg
if my_arg.lower().startswith('--port=')==True:
api_port = my_arg[7:]
elif my_arg.lower()=='--port':
if next_arg is not None: api_port = next_arg
if my_arg.lower().startswith('--reload=')==True:
api_reloader = my_arg[9:].lower() not in falsy
elif my_arg.lower() == '--reload':
if next_arg is not None:
api_reloader = next_arg.lower() not in falsy
elif my_arg.lower() == '-r':
api_reloader = True
arg_index = arg_index+1
print('Default server selected: '+ default_server)
print('Api Listener host interface: '+ api_host)
print('API Port: '+ api_port)
print('Auto-Reloader : '+ str(api_reloader))
################
################ Helper Functions ##############
def get_server(server_name):
my_server = next((server for server in servers if server['name'] == server_name), None)
return my_server
def get_shard(server_name, shard_id):
my_server = get_server(server_name)
if my_server is None:
return None
else:
my_shard = next((shard for shard in my_server['shards'] if shard['id'] == shard_id), None)
return my_shard
# TODO: Python ppl say "choose returning a list or just 1 item, not both"
def get_command(server_name, shard_id, command_id, command_status):
my_shard = get_shard(server_name, shard_id)
if my_shard is None:
my_command = None
else:
if command_id is None: # then get a list
my_commands = []
if command_status is None:
my_commands = my_shard['commands']
else:
my_commands = [command for command in my_shard['commands'] if command['status'].lower() == command_status.lower()]
return my_commands
else: # then return the single specified command
my_command = next((command for command in my_shard['commands'] if command['id'] == command_id), None)
return my_command
def new_server(server_name):
my_server = {'name': server_name, 'shards': []}
servers.append(my_server)
return my_server
def new_shard(server_name, shard_id):
my_shard = get_shard(server_name, shard_id)
if my_shard is None:
my_server = get_server(server_name)
if my_server is None: my_server = new_server(server_name)
my_commands = [{'id': 0, 'command': 'start', 'status': "New"}]
my_shard = {'id': shard_id, 'commands': my_commands}
my_server['shards'].append(my_shard)
return my_shard
def new_command(server_name=None, shard_id=None, command="test"):
my_status = 418
def add_command(shard, rpc):
my_command = {
'id': len(shard['commands']),
'command': rpc,
'status': "New"}
shard['commands'].append(my_command)
return 201
if server_name is None:
# Send command to all servers.
for my_server in servers:
if my_status == 418: my_status = 503
for my_shard in my_server['shards']:
my_status = add_command(my_shard, command)
elif shard_id is None:
# Send command to all shards within server.
my_server = get_server(server_name)
if my_server is not None:
if my_status == 418: my_status = 503
for my_shard in my_server['shards']:
my_status = add_command(my_shard, command)
else:
# Send command to specific shard.
my_shard = get_shard(server_name, shard_id)
if my_shard is not None:
my_status = 503
my_status = add_command(my_shard, command)
return my_status
################
################ Quick Commands ##############
# Some extra (non-RESTful) functions for quick-commands in lieu of a web frontend.
def quick_revive(player_name=None):
my_status = 418
if player_name:
my_rpc = ('UserToPlayer("%s"):'
'PushEvent("respawnfromghost")') %(player_name)
else:
my_rpc = ('for k,v in pairs(AllPlayers) do '
'v:PushEvent("respawnfromghost") end')
my_status = new_command(None, None, my_rpc)
return my_status
def quick_give(player_name=None, item="perogies", item_count=10):
if item_count is None: item_count = 10
my_status = 418
if player_name:
my_rpc = ('c_select(UserToPlayer("%s")) '
'c_give("%s",%i)') %(player_name, item, int(item_count))
else:
my_rpc = ('for k,v in pairs(AllPlayers) do '
'c_select(v) c_give("%s",%i}) end') %(item, int(item_count))
my_status = new_command(None, None, my_rpc)
return my_status
################
################ GET/POST WWW ###############
# Quickly "Post" a new command by opening a web-page (not RESTful)
@get('/quick/<my_cmd>')
@get('/quick/<my_cmd>/<my_var>')
@get('/quick/<my_cmd>/<my_var>/<my_option>')
@get('/quick/<my_cmd>/<my_var>/<my_option>/<my_modifier>')
def post_quick_command(my_cmd,my_var=None,my_option=None, my_modifier=None):
response.status = 501
my_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
if my_cmd.lower() == "revive":
response.status = quick_revive(my_var)
elif my_cmd.lower() == "give":
response.status = quick_give(my_var, my_option, my_modifier)
my_response = "<h1>{}</h1><br>{}".format(response.status, my_time)
return my_response
################
################ GET ##############
# Shaddup Favicon alert!
@route('/favicon.ico', method='GET')
def get_favicon():
return static_file('favicon.ico', root='./static/')
#response.status = 204
#return
# List Servers
@get('/servers')
def get_servers_list():
return {'servers': servers}
# Detail Server
@get('/servers/<server_name>')
def get_server_details(server_name):
my_server = get_server(server_name)
if my_server is None:
response.status = 404
return {}
else:
return {'server': my_server}
# List Commands (can be filtered for unresolved). TODO: Handle more filtering?
@get('/servers/<server_name>/<shard_id>/commands')
def get_pending_commands(server_name,shard_id):
status_filter = request.params.get('status')
my_commands = get_command(server_name, shard_id, None, status_filter)
if my_commands:
for cmd in my_commands: cmd['status'] = 'Sent'
return {'commands': my_commands}
################
################ POST ###############
# Post a new server listing by Name
@post('/servers')
def post_server():
my_name = request.json.get('name')
new_server(my_name)
response.status = 201
return response
# Post new commands.
@post('/servers/<server_name>/commands')
@post('/servers/<server_name>/<shard_id>/commands')
def post_command(server_name, shard_id=None):
my_command = request.json.get('command')
if my_command is None:
return 400
else:
if server_name == 'default': server_name = default_server
response.status = new_command(server_name, shard_id, my_command)
return response
################
############### PATCH ################
# Update pending commands.
@post('/servers/<server_name>/<shard_id>/commands/<cmd_id:int>')
def patch_command(server_name, shard_id, cmd_id):
data = request.json #.get('data')
if data is None:
response.status = 400
return
else:
my_command = get_command(server_name, shard_id, cmd_id, None)
if my_command is None:
response.status = 404
return
else:
my_command['status'] = data['status']
response.status = 204
return
# PUT server status. Return [201] if new server created.
@post('/servers/<server_name>/<shard_id>')
def put_server(server_name,shard_id):
data = request.json #.get('data')
if data is None:
response.status = 400
return
my_shard = get_shard(server_name, shard_id)
if my_shard is None:
my_shard = new_shard(server_name, shard_id)
response.status = 201
else:
response.status = 204
# TODO: put a k,v iteration here instead!
my_shard['settings'] = data['settings']
my_shard['mods'] = data['mods']
my_shard['world'] = data['world']
my_shard['statevars'] = data['statevars']
my_shard['players'] = data['players']
# return {'server' : myServer}
return "OK"
################
if __name__ == "__main__":
run(reloader=api_reloader, host=api_host, port=api_port)