ytptube/app/library/Webhooks.py

114 lines
3.8 KiB
Python

import asyncio
import logging
import os
from .ItemDTO import ItemDTO
import httpx
from .version import APP_VERSION
LOG = logging.getLogger("Webhooks")
class Webhooks:
targets: list[dict] = []
allowed_events: tuple = (
"added",
"completed",
"error",
"not_live",
)
def __init__(self, file: str):
if os.path.exists(file):
self.load(file)
def load(self, file: str):
try:
if os.path.getsize(file) < 3:
raise Exception("file is empty.")
LOG.info(f"Loading webhooks from '{file}'.")
from .Utils import load_file
(targets, status, error) = load_file(file, list)
if not status:
raise Exception(f"{error}")
for target in targets:
LOG.info(f"Will send '{target.get('on',['all'])}' events to '{target.get('name')}'.")
self.targets = targets
except Exception as e:
LOG.error(f"Error loading webhooks from '{file}'. '{e}'")
pass
async def send(self, event: str, item: ItemDTO | dict) -> list[dict]:
if len(self.targets) == 0:
return []
if not isinstance(item, ItemDTO) and not isinstance(item, dict):
LOG.debug(f"Received invalid item type '{type(item)}' with event '{event}'.")
return []
tasks = []
for target in self.targets:
allowed_events = target.get("on") if "on" in target else []
if len(allowed_events) > 0 and event not in allowed_events:
continue
tasks.append(self.__send(event, target, item))
return await asyncio.gather(*tasks)
async def __send(self, event: str, target: dict, item: ItemDTO | dict) -> dict:
from .config import Config
req: dict = target.get("request")
try:
itemId = item.get("id", item.get("_id", "??"))
except Exception:
itemId = "??"
try:
LOG.info(f"Sending event '{event}' id '{itemId}' to '{target.get('name')}'.")
async with httpx.AsyncClient() as client:
request_type = req.get("type", "json")
reqBody = {
"method": req.get("method", "POST"),
"url": req.get("url"),
"headers": {"User-Agent": f"YTPTube/{APP_VERSION}"},
}
if req.get("headers", None):
reqBody["headers"].update(req.get("headers"))
match request_type:
case "json":
reqBody["json"] = item.__dict__ if isinstance(item, ItemDTO) else item
reqBody["headers"]["Content-Type"] = "application/json"
case _:
reqBody["data"] = item.__dict__ if isinstance(item, ItemDTO) else item
reqBody["headers"]["Content-Type"] = "application/x-www-form-urlencoded"
response = await client.request(**reqBody)
respData = {"url": req.get("url"), "status": response.status_code, "text": response.text}
msg = f"Webhook target '{target.get('name')}' Responded to event '{event}' id '{itemId}' with status '{response.status_code}'."
if Config.get_instance().debug and respData.get("text"):
msg += f" body '{respData.get('text','??')}'."
LOG.info(msg)
return respData
except Exception as e:
LOG.error(f"Error sending event '{event}' id '{itemId}' to '{target.get('name')}'. '{e}'")
return {"url": req.get("url"), "status": 500, "text": str(e)}
def emit(self, event, data, **kwargs):
if len(self.targets) < 1 or event not in self.allowed_events:
return False
return self.send(event, data)