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
|
#!/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), "127.0.0.1", 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()
|