diff options
Diffstat (limited to 'src/server.py')
| -rw-r--r-- | src/server.py | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/src/server.py b/src/server.py new file mode 100644 index 0000000..0ab0e89 --- /dev/null +++ b/src/server.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python + +import argparse +import asyncio +import json +import sys + +from datetime import datetime, timezone +from pathlib import Path +from websockets.asyncio.server import serve +from websockets.exceptions import ConnectionClosedOK + +class Context(): + def __init__(self, output): + self.output = output + output.mkdir(parents = True, exist_ok = True) + + def next(self): + ts = datetime.now(timezone.utc).timestamp() + return self.output / f"session-{ts}.json" + + +def quote(instr): + return json.dumps(instr) + +def parse_header(message): + parts = message.split('|') + if len(parts) < 3: + return None + return { + 'width': float(parts[0]), + 'height': float(parts[1]), + 'brands': '|'.join(parts[2:]), + } + +def parse_event(message): + parts = message.split('|') + if parts[0] == 'u' and len(parts) > 1: + return { + 'type': 'url', + 'url': '|'.join(parts[1:]), + } + if len(parts) < 3: + return None + return { + 'type': 'click', + 'x': float(parts[0]), + 'y': float(parts[1]), + 'tag': parts[2], + } + +async def handler(context, websocket): + filename = context.next() + with open(filename, "w", encoding="utf-8") as session_file: + try: + message = await websocket.recv() + except ConnectionClosedOK: + filename.unlink() + return + + header = parse_header(message) + if not header: + print(f"Bad header: {message}", file=sys.stderr) + filename.unlink() + return + + print(f'''{{ + "format":"clicks", + "version":"1.0", + "width": {header['width']}, + "height": {header['height']}, + "brands": {quote(header['brands'])}, + "events": [''', + file=session_file) + + comma = '' + + while True: + try: + message = await websocket.recv() + event = parse_event(message) + if event: + if event['type'] == 'click': + print(f'''{comma}{{ +"x": {event['x']}, "y": {event['y']}, "tag": {quote(event['tag'])} +}}''', + file=session_file) + else: + print(f'''{comma}{{"url": {quote(event['url'])}}}''', + file=session_file) + + if not comma: + comma = ',' + except ConnectionClosedOK: + break + + print(']}', file=session_file) + + +async def server(output): + context = Context(output) + async with serve(lambda x: handler(context, x), "", 8001): + await asyncio.get_running_loop().create_future() + +def main(): + parser = argparse.ArgumentParser( + description='Example server') + + parser.add_argument('output', type=Path, help='output directory') + + args = parser.parse_args() + + asyncio.run(server(args.output)) + +if __name__ == "__main__": + main() + |
