Ejemplo n.º 1
0
 async def call(self, command: rc.Command, data: rc.CommandData,
                parameters: List[str]):
     log.info(f"Calling command: {command.name}")
     try:
         # Run the command
         await command.run(rc.CommandArgs(parameters), data)
     except rc.InvalidInputError as e:
         await data.reply(
             f"⚠️ {e.message}\n"
             f"Syntax: [c]{self.prefix}{command.name} {command.syntax}[/c]")
     except rc.UserError as e:
         await data.reply(f"⚠️ {e.message}")
     except rc.UnsupportedError as e:
         await data.reply(f"⚠️ {e.message}")
     except rc.ExternalError as e:
         await data.reply(f"⚠️ {e.message}")
     except rc.ConfigurationError as e:
         await data.reply(f"⚠️ {e.message}")
     except rc.ProgramError as e:
         await data.reply(f"⛔️ {e.message}")
     except rc.CommandError as e:
         await data.reply(f"⚠️ {e.message}")
     except Exception as e:
         ru.sentry_exc(e)
         await data.reply(f"⛔️ [b]{e.__class__.__name__}[/b]\n" +
                          '\n'.join(map(lambda a: repr(a), e.args)))
Ejemplo n.º 2
0
 def register_events(self, events: List[Type[Event]], pack_cfg: Dict[str,
                                                                     Any]):
     for SelectedEvent in events:
         # Create a new interface
         interface = self.Interface(config=pack_cfg)
         # Initialize the event
         try:
             event = SelectedEvent(interface)
         except Exception as e:
             log.error(
                 f"Skipping: "
                 f"{SelectedEvent.__qualname__} - {e.__class__.__qualname__} in the initialization."
             )
             ru.sentry_exc(e)
             continue
         # Register the event
         if SelectedEvent.name in self.events:
             log.warning(
                 f"Overriding (already defined): {SelectedEvent.__qualname__} -> {SelectedEvent.name}"
             )
         else:
             log.debug(
                 f"Registering: {SelectedEvent.__qualname__} -> {SelectedEvent.name}"
             )
         self.events[SelectedEvent.name] = event
Ejemplo n.º 3
0
 async def network_handler(
         self, message: Union[rh.Request, rh.Broadcast]) -> rh.Response:
     try:
         event: Event = self.events[message.handler]
     except KeyError:
         log.warning(f"No event for '{message.handler}'")
         return rh.ResponseFailure(
             "no_event",
             f"This serf does not have any event for {message.handler}.")
     log.debug(f"Event called: {event.name}")
     if isinstance(message, rh.Request):
         try:
             response_data = await event.run(**message.data)
             return rh.ResponseSuccess(data=response_data)
         except CommandError as e:
             return rh.ResponseFailure(
                 "error_in_event",
                 f"The event '{message.handler}' raised a {e.__class__.__qualname__}.",
                 extra_info={
                     "type": e.__class__.__qualname__,
                     "message": str(e)
                 })
         except Exception as e:
             ru.sentry_exc(e)
             return rh.ResponseFailure(
                 "unhandled_exception_in_event",
                 f"The event '{message.handler}' raised an unhandled"
                 f" {e.__class__.__qualname__}.",
                 extra_info={
                     "type": e.__class__.__qualname__,
                     "message": str(e)
                 })
     elif isinstance(message, rh.Broadcast):
         await event.run(**message.data)
Ejemplo n.º 4
0
 async def call(self, command: Command, data: CommandData,
                parameters: List[str]):
     log.info(f"Calling command: {command.name}")
     try:
         # Run the command
         await command.run(CommandArgs(parameters), data)
     except InvalidInputError as e:
         await data.reply(
             f"⚠️ {e.message}\n"
             f"Syntax: [c]{command.interface.prefix}{command.name} {command.syntax}[/c]"
         )
     except UserError as e:
         await data.reply(f"⚠️ {e.message}")
     except UnsupportedError as e:
         await data.reply(f"⚠️ {e.message}")
     except ExternalError as e:
         await data.reply(f"⚠️ {e.message}")
     except ConfigurationError as e:
         await data.reply(f"⚠️ {e.message}")
     except ProgramError as e:
         await data.reply(f"⛔️ {e.message}")
     except CommandError as e:
         await data.reply(f"⚠️ {e.message}")
     except Exception as e:
         ru.sentry_exc(e)
         await data.reply(f"⛔️ [b]{e.__class__.__name__}[/b]\n" +
                          '\n'.join(e.args))
     finally:
         await data.session_close()
Ejemplo n.º 5
0
    async def run_updater(self):
        log.info(f"Starting updater: {self.name}")

        while True:
            log.debug(f"Updater cycle: {self.name}")
            session = self.alchemy.Session()
            objects = await self.get_updatables(session)

            for obj in objects:
                log.debug(f"Updating: {obj} ({self.name})")

                async def change(attribute: str, value: Any):
                    """A shortcut for self.__change."""
                    await self._change(session=session,
                                       obj=obj,
                                       attribute=attribute,
                                       new=value)

                try:
                    await self.update(session=session, obj=obj, change=change)
                except Exception as e:
                    ru.sentry_exc(e)

                delay = self.delay()
                log.debug(f"Waiting for: {delay} seconds (delay)")
                await aio.sleep(delay)

            log.debug(f"Committing updates: {self.name}")
            await ru.asyncify(session.commit)
            session.close()

            period = self.period()
            log.debug(f"Waiting for: {period} seconds (period)")
            await aio.sleep(period)
Ejemplo n.º 6
0
    async def api_call(f: Callable, *args, **kwargs) -> Optional:
        """Call a :class:`telegram.Bot` method safely, without getting a mess of errors raised.

        The method may return None if it was decided that the call should be skipped."""
        while True:
            try:
                return await ru.asyncify(f, *args, **kwargs)
            except telegram.error.TimedOut as error:
                log.debug(f"Timed out during {f.__qualname__} (retrying immediatly): {error}")
                continue
            except telegram.error.NetworkError as error:
                log.debug(f"Network error during {f.__qualname__} (skipping): {error}")
                break
            except telegram.error.Unauthorized as error:
                log.info(f"Unauthorized to run {f.__qualname__} (skipping): {error}")
                break
            except telegram.error.RetryAfter as error:
                log.warning(f"Rate limited during {f.__qualname__} (retrying in 15s): {error}")
                await aio.sleep(15)
                continue
            except urllib3.exceptions.HTTPError as error:
                log.warning(f"urllib3 HTTPError during {f.__qualname__} (retrying in 15s): {error}")
                await aio.sleep(15)
                continue
            except Exception as error:
                log.error(f"{error.__class__.__qualname__} during {f} (skipping): {error}")
                ru.sentry_exc(error)
                break
        return None
Ejemplo n.º 7
0
 def _test_all(self):
     for spell in self._dnddata:
         try:
             log.debug(f"Testing: {spell['name']}")
             result = self._parse_spell(spell)
         except Exception as e:
             log.error(f"Failed: {spell['name']}")
             sentry_exc(e)
     log.info(f"All spell tests complete!")
Ejemplo n.º 8
0
 def _test_all(self):
     for item in self._dnddata:
         try:
             log.debug(f"Testing: {item['name']}")
             result = self._parse_item(item)
         except Exception as e:
             log.error(f"Failed: {item['name']}")
             sentry_exc(e)
             breakpoint()
     log.info(f"All item tests complete!")
Ejemplo n.º 9
0
 def register_exc_stars(self, exc_stars: List[Type[ExceptionStar]], pack_cfg: Dict[str, Any]):
     for SelectedPageStar in exc_stars:
         log.debug(f"Registering: {SelectedPageStar.error} -> {SelectedPageStar.__qualname__}")
         try:
             page_star_instance = SelectedPageStar(constellation=self, config=pack_cfg)
         except Exception as e:
             log.error(f"Skipping: "
                       f"{SelectedPageStar.__qualname__} - {e.__class__.__qualname__} in the initialization.")
             ru.sentry_exc(e)
             continue
         self.starlette.add_exception_handler(page_star_instance.error, page_star_instance.page)
Ejemplo n.º 10
0
 def register_page_stars(self, page_stars: List[Type[PageStar]], pack_cfg: Dict[str, Any]):
     for SelectedPageStar in page_stars:
         log.debug(f"Registering: {SelectedPageStar.path} -> {SelectedPageStar.__qualname__}")
         try:
             page_star_instance = SelectedPageStar(interface=self.Interface(pack_cfg))
         except Exception as e:
             log.error(f"Skipping: "
                       f"{SelectedPageStar.__qualname__} - {e.__class__.__qualname__} in the initialization.")
             ru.sentry_exc(e)
             continue
         self.stars.append(page_star_instance)
         self.starlette.add_route(*self._page_star_wrapper(page_star_instance))
Ejemplo n.º 11
0
 def register_commands(self, commands: List[Type[Command]],
                       pack_cfg: Dict[str, Any]) -> None:
     """Initialize and register all commands passed as argument."""
     # Instantiate the Commands
     for SelectedCommand in commands:
         # Create a new interface
         interface = self.Interface(config=pack_cfg)
         # Try to instantiate the command
         try:
             command = SelectedCommand(interface)
         except Exception as e:
             log.error(
                 f"Skipping: "
                 f"{SelectedCommand.__qualname__} - {e.__class__.__qualname__} in the initialization."
             )
             ru.sentry_exc(e)
             continue
         # Link the interface to the command
         interface.command = command
         # Warn if the command would be overriding something
         if f"{self.Interface.prefix}{SelectedCommand.name}" in self.commands:
             log.info(
                 f"Overriding (already defined): "
                 f"{SelectedCommand.__qualname__} -> {self.Interface.prefix}{SelectedCommand.name}"
             )
         else:
             log.debug(
                 f"Registering: "
                 f"{SelectedCommand.__qualname__} -> {self.Interface.prefix}{SelectedCommand.name}"
             )
         # Register the command in the commands dict
         self.commands[
             f"{interface.prefix}{SelectedCommand.name}"] = command
         # Register aliases, but don't override anything
         for alias in SelectedCommand.aliases:
             if f"{interface.prefix}{alias}" not in self.commands:
                 log.debug(
                     f"Aliasing: {SelectedCommand.__qualname__} -> {interface.prefix}{alias}"
                 )
                 self.commands[f"{interface.prefix}{alias}"] = \
                     self.commands[f"{interface.prefix}{SelectedCommand.name}"]
             else:
                 log.warning(
                     f"Ignoring (already defined): {SelectedCommand.__qualname__} -> {interface.prefix}{alias}"
                 )
Ejemplo n.º 12
0
    async def page(self, request: Request) -> JSONResponse:
        if request.query_params:
            data = request.query_params
        else:
            try:
                data = await request.json()
            except JSONDecodeError:
                data = {}
        apidata = ApiData(data=data, star=self)

        method = request.method.lower()

        try:
            if method == "get":
                response = await self.get(apidata)
            elif method == "post":
                response = await self.post(apidata)
            elif method == "put":
                response = await self.put(apidata)
            elif method == "delete":
                response = await self.delete(apidata)
            elif method == "options":
                return api_success("Preflight allowed.",
                                   methods=self.methods())
            else:
                raise MethodNotImplementedError("Unknown method")
        except UnauthorizedError as e:
            return api_error(e, code=401, methods=self.methods())
        except NotFoundError as e:
            return api_error(e, code=404, methods=self.methods())
        except ForbiddenError as e:
            return api_error(e, code=403, methods=self.methods())
        except MethodNotImplementedError as e:
            return api_error(e, code=405, methods=self.methods())
        except BadRequestError as e:
            return api_error(e, code=400, methods=self.methods())
        except Exception as e:
            ru.sentry_exc(e)
            return api_error(e, code=500, methods=self.methods())
        else:
            return api_success(response, methods=self.methods())
        finally:
            await apidata.session_close()
Ejemplo n.º 13
0
    def run_process(cls, **kwargs):
        """Blockingly create and run the Serf.

        This should be used as the target of a :class:`multiprocessing.Process`."""
        ru.init_logging(kwargs["logging_cfg"])

        if kwargs["sentry_cfg"] is None or not kwargs["sentry_cfg"]["enabled"]:
            log.info("Sentry: disabled")
        else:
            try:
                ru.init_sentry(kwargs["sentry_cfg"])
            except ImportError:
                log.info("Sentry: not installed")

        loop = aio.get_event_loop()

        serf = cls(loop=loop, **kwargs)

        try:
            serf.loop.run_until_complete(serf.run())
        except Exception as e:
            ru.sentry_exc(e, level="fatal")
Ejemplo n.º 14
0
 async def press(key: rc.KeyboardKey, data: rc.CommandData):
     log.info(f"Calling key_callback: {repr(key)}")
     try:
         await key.press(data)
     except rc.InvalidInputError as e:
         await data.reply(f"⚠️ {e.message}")
     except rc.UserError as e:
         await data.reply(f"⚠️ {e.message}")
     except rc.UnsupportedError as e:
         await data.reply(f"⚠️ {e.message}")
     except rc.ExternalError as e:
         await data.reply(f"⚠️ {e.message}")
     except rc.ConfigurationError as e:
         await data.reply(f"⚠️ {e.message}")
     except rc.ProgramError as e:
         await data.reply(f"⛔️ {e.message}")
     except rc.CommandError as e:
         await data.reply(f"⚠️ {e.message}")
     except Exception as e:
         ru.sentry_exc(e)
         await data.reply(f"⛔️ [b]{e.__class__.__name__}[/b]\n" +
                          '\n'.join(map(lambda a: repr(a), e.args)))
Ejemplo n.º 15
0
 def register_commands(self, commands: List[Type[rc.Command]],
                       pack_cfg: rc.ConfigDict) -> None:
     """Initialize and register all commands passed as argument."""
     # Instantiate the Commands
     for SelectedCommand in commands:
         # Try to instantiate the command
         try:
             command = SelectedCommand(serf=self, config=pack_cfg)
         except Exception as e:
             log.error(
                 f"Skipping: "
                 f"{SelectedCommand.__qualname__} - {e.__class__.__qualname__} in the initialization."
             )
             ru.sentry_exc(e)
             continue
         # Warn if the command would be overriding something
         if SelectedCommand.name in self.commands:
             log.info(
                 f"Overriding (already defined): "
                 f"{SelectedCommand.__qualname__} -> {SelectedCommand.name}"
             )
         else:
             log.debug(
                 f"Registering: "
                 f"{SelectedCommand.__qualname__} -> {SelectedCommand.name}"
             )
         # Register the command in the commands dict
         self.commands[SelectedCommand.name] = command
         # Register aliases, but don't override anything
         for alias in SelectedCommand.aliases:
             if alias not in self.commands:
                 log.debug(
                     f"Aliasing: {SelectedCommand.__qualname__} -> {alias}")
                 self.commands[alias] = self.commands[SelectedCommand.name]
             else:
                 log.warning(
                     f"Ignoring (already defined): {SelectedCommand.__qualname__} -> {alias}"
                 )
Ejemplo n.º 16
0
 def register_events(self, events: List[Type[rc.HeraldEvent]],
                     pack_cfg: rc.ConfigDict):
     for SelectedEvent in events:
         # Initialize the event
         try:
             event = SelectedEvent(parent=self, config=pack_cfg)
         except Exception as e:
             log.error(
                 f"Skipping: "
                 f"{SelectedEvent.__qualname__} - {e.__class__.__qualname__} in the initialization."
             )
             ru.sentry_exc(e)
             continue
         # Register the event
         if SelectedEvent.name in self.events:
             log.warning(
                 f"Overriding (already defined): {SelectedEvent.__qualname__} -> {SelectedEvent.name}"
             )
         else:
             log.debug(
                 f"Registering: {SelectedEvent.__qualname__} -> {SelectedEvent.name}"
             )
         self.events[SelectedEvent.name] = event
Ejemplo n.º 17
0
 async def press(self, key: KeyboardKey, data: CommandData):
     log.info(f"Calling key_callback: {repr(key)}")
     try:
         await key.press(data)
     except InvalidInputError as e:
         await data.reply(f"⚠️ {e.message}")
     except UserError as e:
         await data.reply(f"⚠️ {e.message}")
     except UnsupportedError as e:
         await data.reply(f"⚠️ {e.message}")
     except ExternalError as e:
         await data.reply(f"⚠️ {e.message}")
     except ConfigurationError as e:
         await data.reply(f"⚠️ {e.message}")
     except ProgramError as e:
         await data.reply(f"⛔️ {e.message}")
     except CommandError as e:
         await data.reply(f"⚠️ {e.message}")
     except Exception as e:
         ru.sentry_exc(e)
         await data.reply(f"⛔️ [b]{e.__class__.__name__}[/b]\n" +
                          '\n'.join(e.args))
     finally:
         await data.session_close()
Ejemplo n.º 18
0
 async def on_error(self, event_method, *args, **kwargs):
     exc_type, exc_obj, exc_tb = sys.exc_info()
     sentry_exc(exc_obj)
Ejemplo n.º 19
0
    async def get(self, data: rca.ApiData) -> ru.JSON:
        """Login to Royalnet with your osu! account."""
        OsuT = self.alchemy.get(Osu)
        TokenT = self.alchemy.get(rbt.Token)

        code = data.str("code")
        state = data.str("state", optional=True)

        if state is not None:
            serializer = itsdangerous.URLSafeSerializer(
                self.config["secret_key"], salt="osu")
            uid = serializer.loads(state)
            user = await rbt.User.find(self.alchemy, data.session, uid)
        else:
            user = None

        try:
            t = await oauth_auth(url="https://osu.ppy.sh/oauth/token",
                                 client_id=self.client_id,
                                 client_secret=self.client_secret,
                                 redirect_uri=f"{self.base_url}{self.path}",
                                 auth_code=code)
        except aiohttp.client_exceptions.ClientResponseError as e:
            ru.sentry_exc(e)
            raise rca.ForbiddenError(
                "osu! API returned an error in the OAuth token exchange")

        async with aiohttp.ClientSession(
                headers={"Authorization": f"Bearer {t['access_token']}"
                         }) as session:
            async with session.get(
                    "https://osu.ppy.sh/api/v2/me/") as response:
                m = await response.json()

        if user is not None:
            osu = OsuT(user=user,
                       access_token=t["access_token"],
                       refresh_token=t["refresh_token"],
                       expiration_date=datetime.datetime.now() +
                       datetime.timedelta(seconds=t["expires_in"]),
                       osu_id=m["id"],
                       username=m["username"])

            data.session.add(osu)
        else:
            osu = await ru.asyncify(
                data.session.query(OsuT).filter_by(osu_id=m["id"]).all)
            if osu is None:
                raise rcae.ForbiddenError("Unknown osu! account")
            user = osu.user

        if self.config["osu"]["login"]["enabled"]:
            token: rbt.Token = TokenT.generate(
                alchemy=self.alchemy,
                user=user,
                expiration_delta=datetime.timedelta(days=7))
            data.session.add(token)
            await data.session_commit()

            return token.json()
        else:
            await data.session_commit()
            raise rcae.ForbiddenError(
                "Account linked successfully; cannot use this account to generate a Royalnet"
                " login token, as osu! login is currently disabled on this Royalnet instance."
            )