Exemple #1
0
    def test_data_assignment(self):
        ct = ContextTypes()

        with pytest.raises(AttributeError):
            ct.bot_data = bool
        with pytest.raises(AttributeError):
            ct.user_data = bool
        with pytest.raises(AttributeError):
            ct.chat_data = bool
Exemple #2
0
    def test_data_init(self):
        ct = ContextTypes(SubClass, int, float, bool)
        assert ct.context is SubClass
        assert ct.bot_data is int
        assert ct.chat_data is float
        assert ct.user_data is bool

        with pytest.raises(ValueError, match="subclass of CallbackContext"):
            ContextTypes(context=bool)
Exemple #3
0
    def test_all_application_args_custom(self, builder, bot, monkeypatch):
        job_queue = JobQueue()
        persistence = PicklePersistence("file_path")
        update_queue = asyncio.Queue()
        context_types = ContextTypes()
        concurrent_updates = 123

        async def post_init(app: Application) -> None:
            pass

        async def post_shutdown(app: Application) -> None:
            pass

        app = (builder.token(bot.token).job_queue(job_queue).persistence(
            persistence).update_queue(update_queue).context_types(
                context_types).concurrent_updates(concurrent_updates).
               post_init(post_init).post_shutdown(post_shutdown)).build()
        assert app.job_queue is job_queue
        assert app.job_queue.application is app
        assert app.persistence is persistence
        assert app.persistence.bot is app.bot
        assert app.update_queue is update_queue
        assert app.updater.update_queue is update_queue
        assert app.updater.bot is app.bot
        assert app.context_types is context_types
        assert app.concurrent_updates == concurrent_updates
        assert app.post_init is post_init
        assert app.post_shutdown is post_shutdown

        updater = Updater(bot=bot, update_queue=update_queue)
        app = ApplicationBuilder().updater(updater).build()
        assert app.updater is updater
        assert app.bot is updater.bot
        assert app.update_queue is updater.update_queue
 def test_slot_behaviour(self, mro_slots):
     instance = ContextTypes()
     for attr in instance.__slots__:
         assert getattr(instance, attr, 'err') != 'err', f"got extra slot '{attr}'"
     assert len(mro_slots(instance)) == len(set(mro_slots(instance))), "duplicate slot"
     with pytest.raises(AttributeError):
         instance.custom
    async def test_with_context_types(self, ud, cd, bd, singlefile):
        cc = ContextTypes(user_data=ud, chat_data=cd, bot_data=bd)
        persistence = PicklePersistence("pickletest",
                                        single_file=singlefile,
                                        context_types=cc)

        assert isinstance(await persistence.get_bot_data(), bd)
        assert await persistence.get_bot_data() == 0

        persistence.user_data = None
        persistence.chat_data = None
        await persistence.drop_user_data(123)
        await persistence.drop_chat_data(123)
        assert isinstance(await persistence.get_user_data(), dict)
        assert isinstance(await persistence.get_chat_data(), dict)
        persistence.user_data = None
        persistence.chat_data = None
        await persistence.update_user_data(1, ud(1))
        await persistence.update_chat_data(1, cd(1))
        await persistence.update_bot_data(bd(1))
        assert (await persistence.get_user_data())[1] == 1
        assert (await persistence.get_chat_data())[1] == 1
        assert await persistence.get_bot_data() == 1

        await persistence.flush()
        persistence = PicklePersistence("pickletest",
                                        single_file=singlefile,
                                        context_types=cc)
        assert isinstance((await persistence.get_user_data())[1], ud)
        assert (await persistence.get_user_data())[1] == 1
        assert isinstance((await persistence.get_chat_data())[1], cd)
        assert (await persistence.get_chat_data())[1] == 1
        assert isinstance(await persistence.get_bot_data(), bd)
        assert await persistence.get_bot_data() == 1
def main() -> None:
    """Run the bot."""
    context_types = ContextTypes(context=CustomContext, chat_data=ChatData)
    application = Application.builder().token("TOKEN").context_types(context_types).build()

    # run track_users in its own group to not interfere with the user handlers
    application.add_handler(TypeHandler(Update, track_users), group=-1)
    application.add_handler(CommandHandler("start", start))
    application.add_handler(CallbackQueryHandler(count_click))
    application.add_handler(CommandHandler("print_users", print_users))

    application.run_polling()
Exemple #7
0
    def test_custom_context_init(self, bot):
        cc = ContextTypes(
            context=CustomContext,
            user_data=int,
            chat_data=float,
            bot_data=complex,
        )

        dispatcher = Dispatcher(bot, Queue(), context_types=cc)

        assert isinstance(dispatcher.user_data[1], int)
        assert isinstance(dispatcher.chat_data[1], float)
        assert isinstance(dispatcher.bot_data, complex)
Exemple #8
0
def main() -> None:
    """Run the bot."""
    context_types = ContextTypes(context=CustomContext, chat_data=ChatData)
    updater = Updater("TOKEN", context_types=context_types)

    dispatcher = updater.dispatcher
    # run track_users in its own group to not interfere with the user handlers
    dispatcher.add_handler(TypeHandler(Update, track_users), group=-1)
    dispatcher.add_handler(CommandHandler("start", start))
    dispatcher.add_handler(CallbackQueryHandler(count_click))
    dispatcher.add_handler(CommandHandler("print_users", print_users))

    updater.start_polling()
    updater.idle()
    async def test_custom_context(self, bot, job_queue):
        application = (ApplicationBuilder().token(bot.token).context_types(
            ContextTypes(context=CustomContext,
                         bot_data=int,
                         user_data=float,
                         chat_data=complex)).build())
        job_queue.set_application(application)

        def callback(context):
            self.result = (
                type(context),
                context.user_data,
                context.chat_data,
                type(context.bot_data),
            )

        job_queue.run_once(callback, 0.1)
        await asyncio.sleep(0.15)
        assert self.result == (CustomContext, None, None, int)
    def test_custom_context(self, bot, job_queue):
        dispatcher = Dispatcher(
            bot,
            Queue(),
            context_types=ContextTypes(context=CustomContext,
                                       bot_data=int,
                                       user_data=float,
                                       chat_data=complex),
        )
        job_queue.set_dispatcher(dispatcher)

        def callback(context):
            self.result = (
                type(context),
                context.user_data,
                context.chat_data,
                type(context.bot_data),
            )

        job_queue.run_once(callback, 0.1)
        sleep(0.15)
        assert self.result == (CustomContext, None, None, int)
Exemple #11
0
    def test_custom_context_handler_callback(self, bot):
        def callback(_, context):
            self.received = (
                type(context),
                type(context.user_data),
                type(context.chat_data),
                type(context.bot_data),
            )

        dispatcher = Dispatcher(
            bot,
            Queue(),
            context_types=ContextTypes(context=CustomContext,
                                       bot_data=int,
                                       user_data=float,
                                       chat_data=complex),
        )
        dispatcher.add_handler(MessageHandler(Filters.all, callback))

        dispatcher.process_update(self.message_update)
        sleep(0.1)
        assert self.received == (CustomContext, float, complex, int)
Exemple #12
0
def main() -> None:
    """Setup and run ONGAbot"""
    context_types = ContextTypes(bot_data=BotData, user_data=UserData)

    persistence = PicklePersistence(filename=os.getenv("DB_PATH",
                                                       "ongabot.db"),
                                    context_types=context_types)

    updater = Updater(
        os.getenv("API_TOKEN"),
        persistence=persistence,
        use_context=True,
        context_types=context_types,
    )

    # Register handlers
    dispatcher = updater.dispatcher
    dispatcher.add_handler(StartCommandHandler())
    dispatcher.add_handler(HelpCommandHandler())
    dispatcher.add_handler(OngaCommandHandler())
    dispatcher.add_handler(NewEventCommandHandler())
    dispatcher.add_handler(CancelEventCommandHandler())
    dispatcher.add_handler(EventPollHandler())
    dispatcher.add_handler(EventPollAnswerHandler())
    dispatcher.add_handler(ScheduleCommandHandler())
    dispatcher.add_handler(DeScheduleCommandHandler())
    dispatcher.add_error_handler(error)

    bot_data: BotData = persistence.bot_data
    bot_data.schedule_all_event_jobs(updater.job_queue, create_event_callback)

    # Start the bot
    updater.start_polling()

    # Block until you press Ctrl-C or the process receives SIGINT, SIGTERM or
    # SIGABRT. This should be used most of the time, since start_polling() is
    # non-blocking and will stop the bot gracefully.
    updater.idle()
Exemple #13
0
async def main() -> None:
    """Set up the application and a custom webserver."""
    url = "https://domain.tld"
    admin_chat_id = 123456
    port = 8000

    context_types = ContextTypes(context=CustomContext)
    # Here we set updater to None because we want our custom webhook server to handle the updates
    # and hence we don't need an Updater instance
    application = (Application.builder().token("TOKEN").updater(
        None).context_types(context_types).build())
    # save the values in `bot_data` such that we may easily access them in the callbacks
    application.bot_data["url"] = url
    application.bot_data["admin_chat_id"] = admin_chat_id

    # register handlers
    application.add_handler(CommandHandler("start", start))
    application.add_handler(
        TypeHandler(type=WebhookUpdate, callback=webhook_update))

    # Pass webhook settings to telegram
    await application.bot.set_webhook(url=f"{url}/telegram")

    # Set up webserver
    async def telegram(request: Request) -> Response:
        """Handle incoming Telegram updates by putting them into the `update_queue`"""
        await application.update_queue.put(
            Update.de_json(data=await request.json(), bot=application.bot))
        return Response()

    async def custom_updates(request: Request) -> PlainTextResponse:
        """
        Handle incoming webhook updates by also putting them into the `update_queue` if
        the required parameters were passed correctly.
        """
        try:
            user_id = int(request.query_params["user_id"])
            payload = request.query_params["payload"]
        except KeyError:
            return PlainTextResponse(
                status_code=HTTPStatus.BAD_REQUEST,
                content=
                "Please pass both `user_id` and `payload` as query parameters.",
            )
        except ValueError:
            return PlainTextResponse(
                status_code=HTTPStatus.BAD_REQUEST,
                content="The `user_id` must be a string!",
            )

        await application.update_queue.put(
            WebhookUpdate(user_id=user_id, payload=payload))
        return PlainTextResponse(
            "Thank you for the submission! It's being forwarded.")

    async def health(_: Request) -> PlainTextResponse:
        """For the health endpoint, reply with a simple plain text message."""
        return PlainTextResponse(content="The bot is still running fine :)")

    starlette_app = Starlette(routes=[
        Route("/telegram", telegram, methods=["POST"]),
        Route("/healthcheck", health, methods=["GET"]),
        Route("/submitpayload", custom_updates, methods=["POST", "GET"]),
    ])
    webserver = uvicorn.Server(config=uvicorn.Config(
        app=starlette_app,
        port=port,
        use_colors=False,
        host="127.0.0.1",
    ))

    # Run application and webserver together
    async with application:
        await application.start()
        await webserver.serve()
        await application.stop()
    def __init__(
        self,
        bot: 'Bot',
        update_queue: Queue,
        workers: int = 4,
        exception_event: Event = None,
        job_queue: 'JobQueue' = None,
        persistence: BasePersistence = None,
        use_context: bool = True,
        context_types: ContextTypes[CCT, UD, CD, BD] = None,
    ):
        self.bot = bot
        self.update_queue = update_queue
        self.job_queue = job_queue
        self.workers = workers
        self.use_context = use_context
        self.context_types = cast(ContextTypes[CCT, UD, CD, BD], context_types
                                  or ContextTypes())

        if not use_context:
            warnings.warn(
                'Old Handler API is deprecated - see https://git.io/fxJuV for details',
                TelegramDeprecationWarning,
                stacklevel=3,
            )

        if self.workers < 1:
            warnings.warn(
                'Asynchronous callbacks can not be processed without at least one worker thread.'
            )

        self.user_data: DefaultDict[int, UD] = defaultdict(
            self.context_types.user_data)
        self.chat_data: DefaultDict[int, CD] = defaultdict(
            self.context_types.chat_data)
        self.bot_data = self.context_types.bot_data()
        self.persistence: Optional[BasePersistence] = None
        self._update_persistence_lock = Lock()
        if persistence:
            if not isinstance(persistence, BasePersistence):
                raise TypeError(
                    "persistence must be based on telegram.ext.BasePersistence"
                )
            self.persistence = persistence
            self.persistence.set_bot(self.bot)
            if self.persistence.store_user_data:
                self.user_data = self.persistence.get_user_data()
                if not isinstance(self.user_data, defaultdict):
                    raise ValueError("user_data must be of type defaultdict")
            if self.persistence.store_chat_data:
                self.chat_data = self.persistence.get_chat_data()
                if not isinstance(self.chat_data, defaultdict):
                    raise ValueError("chat_data must be of type defaultdict")
            if self.persistence.store_bot_data:
                self.bot_data = self.persistence.get_bot_data()
                if not isinstance(self.bot_data, self.context_types.bot_data):
                    raise ValueError(
                        f"bot_data must be of type {self.context_types.bot_data.__name__}"
                    )
            if self.persistence.store_callback_data:
                self.bot = cast(telegram.ext.extbot.ExtBot, self.bot)
                persistent_data = self.persistence.get_callback_data()
                if persistent_data is not None:
                    if not isinstance(persistent_data,
                                      tuple) and len(persistent_data) != 2:
                        raise ValueError('callback_data must be a 2-tuple')
                    self.bot.callback_data_cache = CallbackDataCache(
                        self.bot,
                        self.bot.callback_data_cache.maxsize,
                        persistent_data=persistent_data,
                    )
        else:
            self.persistence = None

        self.handlers: Dict[int, List[Handler]] = {}
        """Dict[:obj:`int`, List[:class:`telegram.ext.Handler`]]: Holds the handlers per group."""
        self.groups: List[int] = []
        """List[:obj:`int`]: A list with all groups."""
        self.error_handlers: Dict[Callable, Union[bool, DefaultValue]] = {}
        """Dict[:obj:`callable`, :obj:`bool`]: A dict, where the keys are error handlers and the
        values indicate whether they are to be run asynchronously."""

        self.running = False
        """:obj:`bool`: Indicates if this dispatcher is running."""
        self.__stop_event = Event()
        self.__exception_event = exception_event or Event()
        self.__async_queue: Queue = Queue()
        self.__async_threads: Set[Thread] = set()

        # For backward compatibility, we allow a "singleton" mode for the dispatcher. When there's
        # only one instance of Dispatcher, it will be possible to use the `run_async` decorator.
        with self.__singleton_lock:
            if self.__singleton_semaphore.acquire(blocking=False):  # pylint: disable=R1732
                self._set_singleton(self)
            else:
                self._set_singleton(None)
Exemple #15
0
 def test_slot_behaviour(self, mro_slots):
     instance = ContextTypes()
     for attr in instance.__slots__:
         assert getattr(instance, attr, "err") != "err", f"got extra slot '{attr}'"
     assert len(mro_slots(instance)) == len(set(mro_slots(instance))), "duplicate slot"