-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.py
More file actions
executable file
·132 lines (118 loc) · 3.44 KB
/
server.py
File metadata and controls
executable file
·132 lines (118 loc) · 3.44 KB
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
from enum import Enum
from flask import Flask, request, abort
from threading import Lock
from copy import deepcopy
from datetime import datetime, timedelta
from multiprocessing import Process, Event
from os import system
import hashlib
import json
api = Flask(__name__)
started_timers = {}
started_timers_lock = Lock()
KEY_LEN = 5 #prob of collision is 1/KEY_LEN due to hash uniformity
config = None #gets set by load_config
def get_timers(request):
"""
Cleans up the list and encodes it into json
"""
time_now = datetime.now().strftime("%m/%d/%Y%H:%M:%S")
started_timers_lock.acquire()
to_remove = [k for (k, ts) in started_timers.items() if not ts.process.is_alive()]
for tr in to_remove:
del started_timers[tr]
to_send = {
i : {
"id" : v.get_id(),
"start_time" : v.start_time,
"time_now" : time_now,
"end_time" : v.end_time,
"message" : v.message
} for (i, v) in started_timers.items()
}
started_timers_lock.release()
return json.dumps(to_send)
def post_timers(request):
"""
Tries to create a timer with the given spec starting now
"""
err = False
to_ins = request.json
ts = TimerSpec(to_ins["seconds"], to_ins["message"], config.command)
ts_id = ts.get_id()
started_timers_lock.acquire()
if ts_id not in started_timers:
started_timers[ts_id] = ts
else:
err = True
started_timers_lock.release()
ts.start_spec()
if err:
abort(404)
return "200"
def del_timers(request):
err = False
key = request.json["id"]
started_timers_lock.acquire()
try:
started_timers[key].process.kill()
del started_timers[key]
except KeyError:
err = True
started_timers_lock.release()
if err:
abort(404)
return "200"
@api.route('/timers', methods=['GET', 'POST', 'DELETE'])
def timers():
if request.method == 'GET':
return get_timers(request)
elif request.method == 'POST':
return post_timers(request)
elif request.method == 'DELETE':
return del_timers(request)
class TimerSpec:
def __init__(self, seconds, msg, cmd):
self.message = msg
self.cmd = cmd
now = datetime.now()
self.start_time = now.strftime("%m/%d/%Y%H:%M:%S")
self.end_time = (now + timedelta(seconds=float(seconds))).strftime(
"%m/%d/%Y%H:%M:%S"
)
self.time_span = seconds
def get_id(self):
m = hashlib.sha256()
m.update("{}{}{}".format(self.message, self.start_time, self.time_span).encode())
return m.hexdigest()[:KEY_LEN]
def _runner(self, t, msg, cmd):
from shlex import quote
e = Event()
e.wait(t)
system(cmd.format(quote(msg)))
def start_spec(self):
p = Process(
target = self._runner, args=(float(self.time_span), self.message, self.cmd)
)
p.start()
self.process = p
self.pid = p.pid
def load_config():
global config
import argparse
parser = argparse.ArgumentParser(description="Queries the remote timer server")
parser.add_argument('-ip', default="127.0.0.1", help="IP of the remote server")
parser.add_argument('-port', default=7853, help="Port of the remote server")
parser.add_argument(
'-command', default='zenity --info --title="ExpiredTimer" --text="{}"',
help="Command to run on timer expired"
)
config = parser.parse_args()
return {"port" : int(config.port), "ip" : config.ip}
def start_server(config):
api.run(host = config["ip"], port = config["port"], threaded = True)
def main():
config = load_config()
start_server(config)
if __name__ == "__main__":
main()