Add more reliable way to recover from crashes

This commit is contained in:
Deko
2024-03-10 15:01:32 +01:00
parent 231b83c8a7
commit 046c101f1c
3 changed files with 47 additions and 33 deletions

View File

@@ -9,7 +9,7 @@ from app.twitch_client import StreamInformation
class DiscordClient: class DiscordClient:
_notification_msg_id: str = "" notification_msg_id: str = ""
def __init__(self): def __init__(self):
self._webhook_url = os.environ["DISCORD_WEBHOOK_URL"] self._webhook_url = os.environ["DISCORD_WEBHOOK_URL"]
@@ -60,7 +60,7 @@ class DiscordClient:
response.raise_for_status() response.raise_for_status()
self._notification_msg_id = response.json()["id"] self.notification_msg_id = response.json()["id"]
logger.info("Stream information sent with ping to Discord.") logger.info("Stream information sent with ping to Discord.")
except (exceptions.ConnectionError, exceptions.HTTPError) as err: except (exceptions.ConnectionError, exceptions.HTTPError) as err:
logger.opt(exception=err).warning( logger.opt(exception=err).warning(
@@ -85,7 +85,7 @@ class DiscordClient:
streamer_url = f"https://www.twitch.tv/{stream.user_login}" streamer_url = f"https://www.twitch.tv/{stream.user_login}"
try: try:
response = requests.patch( response = requests.patch(
url=f"{self._webhook_url}/messages/{self._notification_msg_id}", url=f"{self._webhook_url}/messages/{self.notification_msg_id}",
json={ json={
"embeds": [ "embeds": [
{ {
@@ -127,7 +127,7 @@ class DiscordClient:
self, streamer_name, vod_url: str | None, retry_count: int = 0 self, streamer_name, vod_url: str | None, retry_count: int = 0
) -> None: ) -> None:
logger.info("Finalizing stream information on Discord...") logger.info("Finalizing stream information on Discord...")
if not self._notification_msg_id: if not self.notification_msg_id:
logger.info("Message ID not set, nothing to finalize.") logger.info("Message ID not set, nothing to finalize.")
return return
@@ -136,7 +136,7 @@ class DiscordClient:
try: try:
response = requests.patch( response = requests.patch(
url=f"{self._webhook_url}/messages/{self._notification_msg_id}", url=f"{self._webhook_url}/messages/{self.notification_msg_id}",
json={ json={
"username": "Oak Tree", "username": "Oak Tree",
"avatar_url": "https://i.imgur.com/DBOuwjx.png", "avatar_url": "https://i.imgur.com/DBOuwjx.png",

View File

@@ -1,6 +1,6 @@
import json
import os import os
import time import time
from datetime import datetime
from loguru import logger from loguru import logger
from requests import HTTPError from requests import HTTPError
@@ -11,14 +11,18 @@ from app.twitch_client import StreamInformation, TwitchClient
class Main: class Main:
is_live: bool = False is_live: bool = False
last_live_at = None current_stream_id: str = ""
_previous_stream: StreamInformation = None streams: dict[str, StreamInformation]
def __init__(self): def __init__(self):
self.twitch_client = TwitchClient(streamer=os.environ["STREAMER_NAME"]) self.twitch_client = TwitchClient(streamer=os.environ["STREAMER_NAME"])
self.twitch_client.update_access_token() self.twitch_client.update_access_token()
self.profile_image = self.twitch_client.get_streamer_profile_picture() self.profile_image = self.twitch_client.get_streamer_profile_picture()
self.discord_client = DiscordClient() self.discord_client = DiscordClient()
with open("streams.json", "r") as file:
saved_streams = json.load(file)
for stream_id, saved_stream in saved_streams:
self.streams[stream_id] = StreamInformation(**saved_stream)
def update_status(self): def update_status(self):
try: try:
@@ -28,41 +32,46 @@ class Main:
if self.is_live: if self.is_live:
logger.info("Streamer went offline.") logger.info("Streamer went offline.")
self.is_live = False self.is_live = False
stream = self.streams.get(self.current_stream_id)
if stream:
self.discord_client.finalize_information_on_discord( self.discord_client.finalize_information_on_discord(
streamer_name=self._previous_stream.user_name, streamer_name=stream.user_name,
vod_url=self.twitch_client.get_vod( vod_url=self.twitch_client.get_vod(
user_id=self._previous_stream.user_id user_id=stream.user_id
), ),
) )
return return
if not self.is_live: if not self.is_live:
logger.info("Streamer went live.") logger.info("Streamer went live.")
if ( existing_stream = self.streams.get(stream.id)
self.last_live_at if existing_stream:
and (datetime.now() - self.last_live_at).total_seconds()
< 300
):
logger.info( logger.info(
"Streamer was already live once in the past 5 minutes. " "Recovering from crash, updating discord if possible."
"This is probably due to a crash, not sending the Discord ping."
) )
self.last_live_at = datetime.now()
self.is_live = True self.is_live = True
return if existing_stream.discord_message_id:
self.discord_client.notification_msg_id = (
self.discord_client.send_information_to_discord( existing_stream.discord_message_id
stream=stream, profile_image=self.profile_image
) )
self.last_live_at = datetime.now()
self.is_live = True
else:
logger.info("Streamer still live.")
self.discord_client.update_information_on_discord( self.discord_client.update_information_on_discord(
stream=stream, profile_image=self.profile_image stream=stream, profile_image=self.profile_image
) )
return
self._previous_stream = stream self.is_live = True
self.current_stream_id = stream.id
self.streams[stream.id] = stream
self.discord_client.send_information_to_discord(
stream=stream, profile_image=self.profile_image
)
self.streams[
stream.id
].discord_message_id = self.discord_client.notification_msg_id
else:
self.discord_client.update_information_on_discord(
stream=stream, profile_image=self.profile_image
)
except HTTPError as e: except HTTPError as e:
logger.exception(e) logger.exception(e)
@@ -71,11 +80,11 @@ class Main:
return return
self.is_live = False self.is_live = False
stream = self.streams.get(self.current_stream_id)
if stream:
self.discord_client.finalize_information_on_discord( self.discord_client.finalize_information_on_discord(
streamer_name=self._previous_stream.user_login, streamer_name=stream.user_name,
vod_url=self.twitch_client.get_vod( vod_url=self.twitch_client.get_vod(user_id=stream.user_id),
user_id=self._previous_stream.user_id
),
) )
@@ -91,6 +100,8 @@ def entry() -> None:
try: try:
while True: while True:
main.update_status() main.update_status()
with open("streams.json", "w") as file:
json.dump(main.streams, file)
time.sleep( time.sleep(
DELAY_SECONDS - ((time.time() - start_time) % DELAY_SECONDS) DELAY_SECONDS - ((time.time() - start_time) % DELAY_SECONDS)
) )

View File

@@ -25,6 +25,7 @@ class CachePrevent:
@dataclass @dataclass
class StreamInformation: class StreamInformation:
id: str
user_id: str user_id: str
user_name: str user_name: str
user_login: str user_login: str
@@ -33,6 +34,7 @@ class StreamInformation:
viewer_count: int viewer_count: int
started_at: str started_at: str
_thumbnail_url: str _thumbnail_url: str
discord_message_id: str = ""
@property @property
def thumbnail_url(self): def thumbnail_url(self):
@@ -140,6 +142,7 @@ class TwitchClient:
stream_data = streams[0] stream_data = streams[0]
return StreamInformation( return StreamInformation(
id=stream_data.get("id"),
user_id=stream_data.get("user_id"), user_id=stream_data.get("user_id"),
user_name=stream_data.get("user_name"), user_name=stream_data.get("user_name"),
user_login=stream_data.get("user_login"), user_login=stream_data.get("user_login"),