Passed
Push — master ( e2662a...8b3758 )
by manny
01:50
created

rtgg_obs.RacetimeObs.fill_entrant_list()   A

Complexity

Conditions 3

Size

Total Lines 7
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 3
nop 2
1
import json
2
import websockets
3
from websockets.client import WebSocketClientProtocol
4
from datetime import datetime, timedelta, timezone
5
import racetime_client
6
from models.race import Race, race_from_dict
7
import asyncio
8
import websockets
9
import dateutil
10
import logging
11
from helpers.LogFormatter import LogFormatter
12
from gadgets.timer import Timer
13
from gadgets.coop import Coop
14
from gadgets.qualifier import Qualifier
15
16
def script_description():
17
    return "<p>You've loaded the incorrect script.<br><br>Please remove this file and add 'racetime_obs.py' instead</p>"
18
19
class RacetimeObs():
20
    logger = logging.Logger("racetime-obs")
21
    race: Race = None
22
    selected_race = ""
23
    check_race_updates = False
24
    race_changed = False
25
    full_name = ""
26
    category = ""
27
    timer = Timer()
28
    coop = Coop()
29
    qualifier = Qualifier()
30
31
    def __init__(self):
32
        self.timer.logger = self.coop.logger = self.qualifier.logger = self.logger
33
34
    
35
36
    def race_update_thread(self):
37
        self.logger.debug("starting race update")
38
        race_event_loop = asyncio.new_event_loop()
39
        race_event_loop.run_until_complete(self.race_updater())
40
        race_event_loop.run_forever()
41
42
43
    async def race_updater(self):
44
        headers = {
45
            'User-Agent': "oro-obs-bot_alpha"
46
        }
47
        host = "racetime.gg"
48
49
        while True:
50
            if not self.timer.enabled:
51
                await asyncio.sleep(5.0)
52
            else:
53
                if self.race is None and self.selected_race != "":
54
                    self.race = racetime_client.get_race(self.selected_race)
55
                if self.race is not None and self.race.websocket_url != "":
56
                    async with websockets.connect("wss://racetime.gg" + self.race.websocket_url, host=host, extra_headers=headers) as ws:
57
                        self.race_changed = False
58
                        self.logger.info(
59
                            f"connected to websocket: {self.race.websocket_url}")
60
                        await self.process_messages(ws)
61
            await asyncio.sleep(5.0)
62
63
64
    async def process_messages(self, ws: WebSocketClientProtocol):
65
        last_pong = datetime.now(timezone.utc)
66
        while True:
67
            try:
68
                if self.race_changed:
69
                    self.logger.info("new race selected")
70
                    self.race_changed = False
71
                    break
72
                message = await asyncio.wait_for(ws.recv(), 5.0)
73
                self.logger.info(f"received message from websocket: {message}")
74
                data = json.loads(message)
75
                last_pong = self.process_ws_message(data, last_pong)
76
            except asyncio.TimeoutError:
77
                if datetime.now(timezone.utc) - last_pong > timedelta(seconds=20):
78
                    await ws.send(json.dumps({"action": "ping"}))
79
            except websockets.ConnectionClosed:
80
                self.logger.error(f"websocket connection closed")
81
                self.race = None
82
                break
83
84
    def process_ws_message(self, data, last_pong):
85
        if data.get("type") == "race.data":
86
            r = race_from_dict(data.get("race"))
87
            self.logger.debug(f"race data parsed: {r}")
88
            self.logger.debug(f"current race is {self.race}")
89
            if r is not None and r.version > self.race.version:
90
                self.race = r
91
                self.logger.debug(f"self.race is {self.race}")
92
                self.coop.update_coop_text(self.race, self.full_name)
93
                self.qualifier.update_qualifier_text(self.race, self.full_name)
94
        elif data.get("type") == "pong":
95
            last_pong = dateutil.parser.parse(data.get("date"))
96
            pass
97
        return last_pong
98
99
100
    def update_logger(self, enabled: bool, log_to_file: bool, log_file: str, level: str):
101
        self.logger.disabled = not enabled
102
        self.logger.handlers = []
103
        handler = logging.StreamHandler()
104
        if log_to_file:
105
            try:
106
                handler = logging.FileHandler(log_file)
107
            except:
108
                self.logger.error(f"Unable to open {log_file}")
109
        elif level == "Debug":
110
            handler.setLevel(logging.DEBUG)
111
        elif level == "Info":
112
            handler.setLevel(logging.INFO)
113
        else:
114
            handler.setLevel(logging.ERROR)
115
        handler.setFormatter(LogFormatter())
116
        self.logger.addHandler(handler)
117