owncast-clipper/helpers/websocket.py
2025-04-26 23:45:24 -05:00

99 lines
3.1 KiB
Python

# Standard Imports
import re
import sys
import html
import time
import json
from datetime import datetime
from zoneinfo import ZoneInfo
# Package Imports
import websocket
# Local Imports
from helpers.clips import generate_and_post_clip
def owncast_on_message(ws, message):
"""
Handles incoming messages from websocket, filters for commands, and dispatches.
Args:
ws: The websocket object calling this handler.
message: The message received by the websocket.
Returns:
None
"""
try:
data = json.loads(message)
if 'type' in data and data['type'] == 'CHAT':
# TODO: add anti-spam and authentication checks
current_time = datetime.now(ZoneInfo("America/Chicago"))
chat_message = html.unescape(
re.sub('<.*?>', '', data['body'])).strip()
if chat_message == '!clip' or chat_message.startswith('!clip '):
if len(chat_message) == 5:
clip_title = f'Clip by {data["user"]["displayName"]} on {current_time.strftime("%Y-%m-%d %H:%M:%S")}'
generate_and_post_clip(
data['user']['displayName'], clip_title, current_time)
elif len(chat_message) > 5 and len(chat_message) < 126:
generate_and_post_clip(
data['user']['displayName'], chat_message[6:], current_time)
else:
# TODO: report this error back to the chat
return
except json.JSONDecodeError as e:
print(f'Failed to decode incoming owncast websocket message.', file=sys.stderr)
def owncast_on_error(ws, error):
print(f'[WS-ERR] {error}', file=sys.stderr)
def owncast_on_close(ws, code, message):
print(f'Owncast websocket closed (code: {code}): {message}')
def owncast_on_open(ws):
print('Owncast websocket opened successfully.')
def owncast_connect(url: str, retries: int, initial_backoff: float):
"""
Attempts to connect to the Owncast websocket and auto-reconnect with backoff
Args:
url (str): The websocket service url to connect to.
retries (int): The maximum number of reconnect attemps before failing.
initial_backoff (float): The base amount of reconnection backoff time in seconds
Returns:
None
"""
attempt = 0
backoff = initial_backoff
while attempt < retries:
ws = websocket.WebSocketApp(url,
on_open=owncast_on_open,
on_message=owncast_on_message,
on_error=owncast_on_error,
on_close=owncast_on_close
)
try:
ws.run_forever()
except KeyboardInterrupt:
print('\nInterrupted by user. Exiting.')
sys.exit(0)
attempt += 1
if attempt < retries:
print(f'Reconnecting in {backoff:.1f}s ({attempt} of {retries})')
time.sleep(backoff)
backoff *= 2
print(f'Max reconnect attempts reached. Exiting.')