# Path: sygnal/exceptions.py
# class InvalidNotificationException(Exception):
#     pass
# 
# class NotificationDispatchException(Exception):
#     pass
# 
# Path: sygnal/notifications.py
# class Notification:
#     def __init__(self, notif):
#         # optional attributes
#         self.room_name: Optional[str] = notif.get("room_name")
#         self.room_alias: Optional[str] = notif.get("room_alias")
#         self.prio: Optional[str] = notif.get("prio")
#         self.membership: Optional[str] = notif.get("membership")
#         self.sender_display_name: Optional[str] = notif.get("sender_display_name")
#         self.content: Optional[Dict[str, Any]] = notif.get("content")
#         self.event_id: Optional[str] = notif.get("event_id")
#         self.room_id: Optional[str] = notif.get("room_id")
#         self.user_is_target: Optional[bool] = notif.get("user_is_target")
#         self.type: Optional[str] = notif.get("type")
#         self.sender: Optional[str] = notif.get("sender")
# 
#         if "devices" not in notif or not isinstance(notif["devices"], list):
#             raise InvalidNotificationException("Expected list in 'devices' key")
# 
#         if "counts" in notif:
#             self.counts = Counts(notif["counts"])
#         else:
#             self.counts = Counts({})
# 
#         self.devices = [Device(d) for d in notif["devices"]]
# 
# class NotificationContext(object):
#     def __init__(self, request_id: str, opentracing_span: Span, start_time: float):
#         """
#         Args:
#             request_id: An ID for the request, or None to have it
#                 generated automatically.
#             opentracing_span: The span for the API request triggering
#                 the notification.
#             start_time: Start timer value, `time.perf_counter()`
#         """
#         self.request_id = request_id
#         self.opentracing_span = opentracing_span
#         self.start_time = start_time
# 
# class Pushkin(abc.ABC):
#     def __init__(self, name: str, sygnal: "Sygnal", config: Dict[str, Any]):
#         self.name = name
#         self.appid_pattern = glob_to_regex(name, ignore_case=False)
#         self.cfg = config
#         self.sygnal = sygnal
# 
#     @overload
#     def get_config(self, key: str, type_: Type[T], default: T) -> T:
#         ...
# 
#     @overload
#     def get_config(self, key: str, type_: Type[T], default: None = None) -> Optional[T]:
#         ...
# 
#     def get_config(
#         self, key: str, type_: Type[T], default: Optional[T] = None
#     ) -> Optional[T]:
#         if key not in self.cfg:
#             return default
#         if not isinstance(self.cfg[key], type_):
#             raise PushkinSetupException(
#                 f"{key} is of incorrect type, please check that the entry for {key} is "
#                 f"formatted correctly in the config file. "
#             )
#         return self.cfg[key]
# 
#     def handles_appid(self, appid: str) -> bool:
#         """Checks whether the pushkin is responsible for the given app ID"""
#         return self.name == appid or self.appid_pattern.match(appid) is not None
# 
#     @abc.abstractmethod
#     async def dispatch_notification(
#         self, n: Notification, device: Device, context: "NotificationContext"
#     ) -> List[str]:
#         """
#         Args:
#             n: The notification to dispatch via this pushkin
#             device: The device to dispatch the notification for.
#             context: the request context
# 
#         Returns:
#             A list of rejected pushkeys, to be reported back to the homeserver
#         """
#         ...
# 
#     @classmethod
#     async def create(cls, name: str, sygnal: "Sygnal", config: Dict[str, Any]):
#         """
#         Override this if your pushkin needs to call async code in order to
#         be constructed. Otherwise, it defaults to just invoking the Python-standard
#         __init__ constructor.
# 
#         Returns:
#             an instance of this Pushkin
#         """
#         return cls(name, sygnal, config)
# 
# Path: sygnal/utils.py
# async def twisted_sleep(delay: float, twisted_reactor: "SygnalReactor") -> None:
#     def process(
#         self, msg: str, kwargs: MutableMapping[str, Any]
#     ) -> Tuple[str, MutableMapping[str, Any]]:
# def _reject_invalid_json(val: Any) -> None:
# class NotificationLoggerAdapter(LoggerAdapter):


import json
import logging
import sys
import time
import traceback
from typing import TYPE_CHECKING, Callable, List, Union
from uuid import uuid4
from opentracing import Format, Span, logs, tags
from prometheus_client import Counter, Gauge, Histogram
from twisted.internet.defer import ensureDeferred
from twisted.web import server
from twisted.web.http import (
    Request,
    combinedLogFormatter,
    datetimeToLogString,
    proxiedLogFormatter,
)
from twisted.web.resource import Resource
from twisted.web.server import NOT_DONE_YET
from sygnal.exceptions import (
    InvalidNotificationException,
    NotificationDispatchException,
)
from sygnal.notifications import Notification, NotificationContext, Pushkin
from sygnal.utils import NotificationLoggerAdapter, json_decoder
    from sygnal.sygnal import Sygnal
            if not root_span_accounted_for:
                root_span.finish()

    def find_pushkins(self, appid: str) -> List[Pushkin]:
        """Finds matching pushkins in self.sygnal.pushkins according to the appid.

        Args:
            appid: app identifier to search in self.sygnal.pushkins.

        Returns:
            list of `Pushkin`: If it finds a specific pushkin with
                the exact app id, immediately returns it.
                Otherwise returns possible pushkins.
        """
        # if found a specific appid, just return it as a list
        if appid in self.sygnal.pushkins:
            return [self.sygnal.pushkins[appid]]

        # otherwise, find any pushkins whose appid patterns match
        return [
            pushkin
            for pushkin in self.sygnal.pushkins.values()
            if pushkin.handles_appid(appid)
        ]

    async def _handle_dispatch(
        self,
        root_span: Span,
        request: Request,
        log: NotificationLoggerAdapter,

# Path: sygnal/exceptions.py
# class InvalidNotificationException(Exception):
#     pass
# 
# class NotificationDispatchException(Exception):
#     pass
# 
# Path: sygnal/notifications.py
# class Notification:
#     def __init__(self, notif):
#         # optional attributes
#         self.room_name: Optional[str] = notif.get("room_name")
#         self.room_alias: Optional[str] = notif.get("room_alias")
#         self.prio: Optional[str] = notif.get("prio")
#         self.membership: Optional[str] = notif.get("membership")
#         self.sender_display_name: Optional[str] = notif.get("sender_display_name")
#         self.content: Optional[Dict[str, Any]] = notif.get("content")
#         self.event_id: Optional[str] = notif.get("event_id")
#         self.room_id: Optional[str] = notif.get("room_id")
#         self.user_is_target: Optional[bool] = notif.get("user_is_target")
#         self.type: Optional[str] = notif.get("type")
#         self.sender: Optional[str] = notif.get("sender")
# 
#         if "devices" not in notif or not isinstance(notif["devices"], list):
#             raise InvalidNotificationException("Expected list in 'devices' key")
# 
#         if "counts" in notif:
#             self.counts = Counts(notif["counts"])
#         else:
#             self.counts = Counts({})
# 
#         self.devices = [Device(d) for d in notif["devices"]]
# 
# class NotificationContext(object):
#     def __init__(self, request_id: str, opentracing_span: Span, start_time: float):
#         """
#         Args:
#             request_id: An ID for the request, or None to have it
#                 generated automatically.
#             opentracing_span: The span for the API request triggering
#                 the notification.
#             start_time: Start timer value, `time.perf_counter()`
#         """
#         self.request_id = request_id
#         self.opentracing_span = opentracing_span
#         self.start_time = start_time
# 
# class Pushkin(abc.ABC):
#     def __init__(self, name: str, sygnal: "Sygnal", config: Dict[str, Any]):
#         self.name = name
#         self.appid_pattern = glob_to_regex(name, ignore_case=False)
#         self.cfg = config
#         self.sygnal = sygnal
# 
#     @overload
#     def get_config(self, key: str, type_: Type[T], default: T) -> T:
#         ...
# 
#     @overload
#     def get_config(self, key: str, type_: Type[T], default: None = None) -> Optional[T]:
#         ...
# 
#     def get_config(
#         self, key: str, type_: Type[T], default: Optional[T] = None
#     ) -> Optional[T]:
#         if key not in self.cfg:
#             return default
#         if not isinstance(self.cfg[key], type_):
#             raise PushkinSetupException(
#                 f"{key} is of incorrect type, please check that the entry for {key} is "
#                 f"formatted correctly in the config file. "
#             )
#         return self.cfg[key]
# 
#     def handles_appid(self, appid: str) -> bool:
#         """Checks whether the pushkin is responsible for the given app ID"""
#         return self.name == appid or self.appid_pattern.match(appid) is not None
# 
#     @abc.abstractmethod
#     async def dispatch_notification(
#         self, n: Notification, device: Device, context: "NotificationContext"
#     ) -> List[str]:
#         """
#         Args:
#             n: The notification to dispatch via this pushkin
#             device: The device to dispatch the notification for.
#             context: the request context
# 
#         Returns:
#             A list of rejected pushkeys, to be reported back to the homeserver
#         """
#         ...
# 
#     @classmethod
#     async def create(cls, name: str, sygnal: "Sygnal", config: Dict[str, Any]):
#         """
#         Override this if your pushkin needs to call async code in order to
#         be constructed. Otherwise, it defaults to just invoking the Python-standard
#         __init__ constructor.
# 
#         Returns:
#             an instance of this Pushkin
#         """
#         return cls(name, sygnal, config)
# 
# Path: sygnal/utils.py
# async def twisted_sleep(delay: float, twisted_reactor: "SygnalReactor") -> None:
#     def process(
#         self, msg: str, kwargs: MutableMapping[str, Any]
#     ) -> Tuple[str, MutableMapping[str, Any]]:
# def _reject_invalid_json(val: Any) -> None:
# class NotificationLoggerAdapter(LoggerAdapter):


# Path: sygnal/http.py
import json
import logging
import sys
import time
import traceback
from typing import TYPE_CHECKING, Callable, List, Union
from uuid import uuid4
from opentracing import Format, Span, logs, tags
from prometheus_client import Counter, Gauge, Histogram
from twisted.internet.defer import ensureDeferred
from twisted.web import server
from twisted.web.http import (
    Request,
    combinedLogFormatter,
    datetimeToLogString,
    proxiedLogFormatter,
)
from twisted.web.resource import Resource
from twisted.web.server import NOT_DONE_YET
from sygnal.exceptions import (
    InvalidNotificationException,
    NotificationDispatchException,
)
from sygnal.notifications import Notification, NotificationContext, Pushkin
from sygnal.utils import NotificationLoggerAdapter, json_decoder
    from sygnal.sygnal import Sygnal

            if not root_span_accounted_for:
                root_span.finish()

    def find_pushkins(self, appid: str) -> List[Pushkin]:
        """Finds matching pushkins in self.sygnal.pushkins according to the appid.

        Args:
            appid: app identifier to search in self.sygnal.pushkins.

        Returns:
            list of `Pushkin`: If it finds a specific pushkin with
                the exact app id, immediately returns it.
                Otherwise returns possible pushkins.
        """
        # if found a specific appid, just return it as a list
        if appid in self.sygnal.pushkins:
            return [self.sygnal.pushkins[appid]]

        # otherwise, find any pushkins whose appid patterns match
        return [
            pushkin
            for pushkin in self.sygnal.pushkins.values()
            if pushkin.handles_appid(appid)
        ]

    async def _handle_dispatch(
        self,
        root_span: Span,
        request: Request,
        log: NotificationLoggerAdapter,

        notif: Notification,