def test_opponents_and_entrant_finished_race_ongoing(random_users): entrant = get_test_entrant(next(random_users), status_value="finished", finished_at=datetime.now(timezone.utc), finish_time=timedelta(hours=1, microseconds=1)) partner = get_test_entrant(next(random_users), status_value="in_progress") opponent1 = get_test_entrant( next(random_users), status_value="finished", finished_at=datetime.now(timezone.utc), finish_time=timedelta(hours=2, microseconds=2), ) opponent2 = get_test_entrant( next(random_users), status_value="finished", finished_at=datetime.now(timezone.utc), finish_time=timedelta(hours=2, microseconds=1), ) race = get_test_race(entrants=[entrant, partner, opponent1, opponent2], started_at=time_ago(hours=2, microseconds=3)) coop = Coop() coop.enabled = True coop.partner = partner.user.full_name coop.opponent1 = opponent1.user.full_name coop.opponent2 = opponent2.user.full_name coop.update_coop_text(race, entrant.user.full_name) assert coop.opponent_time_text == "2:00:00.0" assert coop.our_time_text == "0:29:59.9" race.started_at = time_ago(hours=2, minutes=30, microseconds=3) coop.update_coop_text(race, entrant.user.full_name) assert coop.opponent_time_text == "2:00:00.0" assert coop.our_time_text == "0:14:59.9"
def test_entrant_and_partner_and_opponent1_finished_race_ongoing(random_users): entrant = get_test_entrant(next(random_users), status_value="finished", finished_at=datetime.now(timezone.utc), finish_time=timedelta(hours=2, microseconds=1)) partner = get_test_entrant(next(random_users), status_value="finished", finished_at=datetime.now(timezone.utc), finish_time=timedelta(hours=2, microseconds=1)) opponent1 = get_test_entrant(next(random_users), status_value="finished", finished_at=datetime.now(timezone.utc), finish_time=timedelta(hours=1, microseconds=1)) opponent2 = get_test_entrant(next(random_users), status_value="in_progress") race = get_test_race(entrants=[entrant, partner, opponent1, opponent2], opened_by=next(random_users), started_at=time_ago(hours=2, minutes=12)) coop = Coop() coop.enabled = True coop.partner = partner.user.full_name coop.opponent1 = opponent1.user.full_name coop.opponent2 = opponent2.user.full_name coop.update_coop_text(race, entrant.user.full_name) assert coop.opponent_time_text == "0:23:59.9" assert coop.our_time_text == "2:00:00.0" assert coop.our_time_color == coop.still_racing_color assert coop.opponent_time_color == coop.still_racing_color race.started_at = time_ago(hours=2, minutes=52) coop.update_coop_text(race, entrant.user.full_name) assert coop.opponent_time_text == "0:03:59.9"
def test_everyone_finished_opponents_won(random_users): entrant = get_test_entrant(next(random_users), status_value="finished", finished_at=datetime.now(timezone.utc), finish_time=timedelta(hours=2, microseconds=1)) partner = get_test_entrant(next(random_users), status_value="finished", finished_at=datetime.now(timezone.utc), finish_time=timedelta(hours=2, microseconds=1)) opponent1 = get_test_entrant(next(random_users), status_value="finished", finished_at=datetime.now(timezone.utc), finish_time=timedelta(hours=1, microseconds=1)) opponent2 = get_test_entrant(next(random_users), status_value="finished", finished_at=datetime.now(timezone.utc), finish_time=timedelta(hours=2, microseconds=1)) race = get_test_race(entrants=[entrant, partner, opponent1, opponent2], opened_by=next(random_users), started_at=time_ago(hours=2, minutes=1)) coop = Coop() coop.enabled = True coop.partner = partner.user.full_name coop.opponent1 = opponent1.user.full_name coop.opponent2 = opponent2.user.full_name coop.update_coop_text(race, entrant.user.full_name) assert coop.opponent_time_text == "1:30:00.0" assert coop.opponent_time_color == coop.winner_color assert coop.our_time_text == "2:00:00.0" assert coop.our_time_color == coop.loser_color
def test_entrant_and_partner_and_opponent1_finished_race_over(random_users): entrant = get_test_entrant(next(random_users), status_value="finished", finished_at=datetime.now(timezone.utc), finish_time=timedelta(hours=1, microseconds=1)) partner = get_test_entrant( next(random_users), status_value="finished", finished_at=datetime.now(timezone.utc), finish_time=timedelta(hours=2, microseconds=1), ) opponent1 = get_test_entrant( next(random_users), status_value="finished", finished_at=datetime.now(timezone.utc), finish_time=timedelta(hours=3, microseconds=2), ) opponent2 = get_test_entrant(next(random_users), status_value="in_progress") race = get_test_race(entrants=[entrant, partner, opponent1, opponent2], opened_by=next(random_users), started_at=time_ago(hours=3, minutes=4)) coop = Coop() coop.enabled = True coop.partner = partner.user.full_name coop.opponent1 = opponent1.user.full_name coop.opponent2 = opponent2.user.full_name coop.update_coop_text(race, entrant.user.full_name) assert (coop.opponent_time_text == "3:02:00.0") assert coop.our_time_text == "1:30:00.0"
def test_coop_no_one_finished(random_users): entrant = get_test_entrant(next(random_users), status_value="in_progress") partner = get_test_entrant(next(random_users), status_value="in_progress") opponent1 = get_test_entrant(next(random_users), status_value="in_progress") opponent2 = get_test_entrant( next(random_users), status_value="in_progress", ) race = get_test_race(entrants=[entrant, partner, opponent1, opponent2], opened_by=next(random_users), started_at=time_ago(hours=1)) coop = Coop() coop.enabled = True coop.partner = partner.user.full_name coop.opponent1 = opponent1.user.full_name coop.opponent2 = opponent2.user.full_name coop.update_coop_text(race, entrant.user.full_name) assert coop.opponent_time_text == " " assert coop.our_time_text == " " assert coop.our_time_color == coop.still_racing_color assert coop.opponent_time_color == coop.still_racing_color
class RacetimeObs(): logger = logging.Logger("racetime-obs") race: Race = None selected_race = "None" check_race_updates = False race_changed = False full_name = "" category = "" timer = Timer() coop = Coop() qualifier = Qualifier() media_player: MediaPlayer = None event_loop: AbstractEventLoop = None preview_mode = False timer_decimals: bool = True def __init__(self): self.event_loop = asyncio.get_event_loop() self.timer.logger = self.logger self.coop.logger = self.logger self.qualifier.logger = self.logger self.media_player = MediaPlayer() self.media_player.logger = self.logger def race_update_thread(self): self.logger.debug("starting race update") self.event_loop.run_until_complete(self.race_updater()) self.event_loop.run_forever() async def race_updater(self): headers = { 'User-Agent': "oro-obs-bot_alpha" } host = "racetime.gg" while True: if not self.timer.is_enabled(): await asyncio.sleep(5.0) else: if self.race is None and self.selected_race != "None": self.race = ( racetime_client.get_race_by_name(self.selected_race) ) self.logger.debug("got race, trying to connect to ws") if self.race is not None and self.race.websocket_url != "": self.logger.debug("received race, trying to connect to ws") try: async with websockets.connect( "wss://racetime.gg" + self.race.websocket_url, host=host, extra_headers=headers ) as ws: self.race_changed = False self.logger.info( "connected to websocket:" " {self.race.websocket_url}" ) await self.process_messages(ws) except (ConnectionRefusedError, ConnectionClosedError): self.logger.error("websocket closed unexpectedly.") continue await asyncio.sleep(5.0) async def process_messages(self, ws: WebSocketClientProtocol): last_pong = datetime.now(timezone.utc) while True: try: if self.race_changed: self.logger.info("new race selected") self.race_changed = False break message = await asyncio.wait_for(ws.recv(), 5.0) self.logger.info(f"received message from websocket: {message}") data = json.loads(message) last_pong = self.process_ws_message(data, last_pong) except asyncio.TimeoutError: await self.ping_ws(ws, last_pong) continue except websockets.ConnectionClosed: self.logger.error("websocket connection closed") self.race = None break async def ping_ws(self, ws: WebSocketClientProtocol, last_pong: datetime): if datetime.now(timezone.utc) - last_pong > timedelta(seconds=20): await ws.send(json.dumps({"action": "ping"})) def process_ws_message(self, data: dict, last_pong: datetime): if data.get("type") == "race.data": self.update_race(data) elif data.get("type") == "chat.message": self.process_chat_message(data.get("message")) elif data.get("type") == "pong": last_pong = dateutil.parser.parse(data.get("date")) pass return last_pong def process_chat_message(self, data: dict): message = chat_message_from_dict(data) self.logger.debug( f"received chat message. chat sounds enabled is " f"{self.media_player.ping_chat_messages}" ) if ( self.media_player.ping_chat_messages and message.is_bot or message.highlight ): self.logger.debug( f"trying to play {self.media_player.chat_media_file}") self.event_loop.call_soon_threadsafe( self.media_player.play_media_callback, self.media_player.chat_media_file, self.media_player.monitoring_type ) def update_race(self, data: dict): r = race_from_dict(data.get("race")) self.logger.debug(f"race data parsed: {r}") self.logger.debug(f"current race is {self.race}") if r is not None and r.version > self.race.version: self.race = r self.logger.debug(f"self.race is {self.race}") self.coop.update_coop_text(self.race, self.full_name) self.qualifier.update_qualifier_text(self.race, self.full_name) self.event_loop.call_soon_threadsafe( self.media_player.race_updated, self.race, self.full_name) def update_logger( self, enabled: bool, log_to_file: bool, log_file: str, level: str ): self.logger.disabled = not enabled self.logger.handlers = [] handler = logging.StreamHandler() if log_to_file: try: handler = logging.FileHandler(log_file) except IOError as e: self.logger.error(f"IOError while opening open {log_file}:") self.logger.error(f"{e.errno} {e.strerror}") elif level == "Debug": handler.setLevel(logging.DEBUG) elif level == "Info": handler.setLevel(logging.INFO) else: handler.setLevel(logging.ERROR) handler.setFormatter(LogFormatter()) self.logger.addHandler(handler)