Beispiel #1
0
    async def run(self, skip_updates: bool, wait: int = DEFAULT_WAIT):
        """ Run bot polling forever
        Can be manually stopped with:
        bot.stop()
        """
        self.__wait = wait
        logger.debug("Polling will be started. Is it OK?")
        if self.__secret is not None:
            logger.warning("You set up secret and started polling. Removing secret")
            self.__secret = None

        if not self.status.dispatched:
            self.middleware.add_middleware(self.on.pre)
            await self.on.dispatch(self.get_current_rest)
            self.status.dispatched = True

        if not skip_updates:
            await self.get_updates()

        await self.get_server()
        logger.info("Polling successfully started. Press Ctrl+C to stop it")

        while not self._stop:
            event = await self.make_long_request(self.long_poll_server)
            if isinstance(event, dict) and event.get("ts"):
                self.loop.create_task(self.emulate(event))
                self.long_poll_server["ts"] = event["ts"]
            else:
                await self.get_server()

        logger.error("Polling was stopped")
Beispiel #2
0
    async def dispatch(self, get_current_rest: typing.Callable = None) -> None:
        """
        Dispatch handlers from only-handlers and both-handlers
        :param get_current_rest: REST from vkbottle-rest
        :return:
        """

        self.message_handler.rules += self.message.rules + self.chat_message.rules
        self.message_handler.payload.rules += (
            self.message.payload.rules + self.chat_message.payload.rules
        )

        self.rules = self.message_handler.payload.rules + self.message_handler.rules

        if get_current_rest:

            # Check updates from timoniq/vkbottle-rest
            current_rest = await get_current_rest()
            if current_rest["version"] != __version__:
                logger.info(
                    "You are using old version of VKBottle. Update is found: {} | {}",
                    current_rest["version"],
                    current_rest["description"],
                )
        logger.debug("Bot successfully dispatched")
Beispiel #3
0
    async def event_processor(self, obj: dict, event_type: str):
        """ Bot polling event processor
        #TODO Reduce difference
        :param event_type: VK Server Event Type
        :param obj: VK Server Event Object
        """

        logger.debug(
            '-> EVENT FROM {} TYPE "{}"',
            get_attr(obj, ["user_id", "from_id"]),
            event_type.upper(),
        )

        for rule in self.handler.event.rules:
            if await rule.check(event_type):
                event = rule.data["data"](**obj)
                await rule.call(event, *rule.context.args,
                                **rule.context.kwargs)

                logger.info(
                    'New event "{}" compiled with decorator <{}> (from: {})'.
                    format(
                        event_type.upper(),
                        rule.call.__name__,
                        get_attr(obj, ["user_id", "from_id"]),
                    ))
                break
Beispiel #4
0
    async def run(self, wait: int = DEFAULT_WAIT):
        """ Run user polling forever
        Can be manually stopped with:
        >> user.stop()
        """
        self.__wait = wait
        logger.info("Polling will be started. Is it OK?")

        await self.get_server()
        self.on.dispatch()
        logger.debug("User Polling successfully started")

        while not self._stop:
            try:
                event = await self.make_long_request(self.long_poll_server)
                if isinstance(event, dict) and event.get("ts"):
                    self.__loop.create_task(self.emulate(event))
                    self.long_poll_server["ts"] = event["ts"]
                else:
                    await self.get_server()

            except (
                    aiohttp.ClientConnectionError,
                    aiohttp.ServerTimeoutError,
                    TimeoutError,
            ):
                # No internet connection
                logger.warning("Server Timeout Error!")

            except:
                logger.error(
                    "While user lp was running error occurred \n\n{}".format(
                        traceback.format_exc()))

        logger.error("Polling was stopped")
Beispiel #5
0
    async def _event_processor(self, obj: dict, event_type: str):
        """
        LongPoll Events Processor
        :param event_type: VK Server Event Type
        :param obj: VK Server Event Object
        """

        logger.debug(
            '-> EVENT FROM {} TYPE "{}"',
            get_attr(obj, ["user_id", "from_id"]),
            event_type.upper(),
        )

        for rule in self.on.event.rules:
            if await rule.check(event_type):
                event = rule.data["data"](**obj)
                await rule.call(event, *rule.context.args,
                                **rule.context.kwargs)

                logger.info(
                    'New event "{}" compiled with decorator <{}> (from: {})'.
                    format(
                        event_type.upper(),
                        rule.call.__name__,
                        get_attr(obj, ["user_id", "from_id"]),
                    ))
                return True
Beispiel #6
0
    async def _branched_processor(self, message: Message,
                                  middleware_args: list):
        logger.debug(
            '-> BRANCHED MESSAGE FROM {} TEXT "{}"',
            message.peer_id,
            message.text.replace("\n", " "),
        )

        disposal, branch = await self.branch.load(message.peer_id)

        for n, member in disposal.items():
            rules = member[1]
            for rule in rules:
                if not await rule(message):
                    break
            else:
                task = types.MethodType(member[0], branch)
                args = [a for rule in rules for a in rule.context.args]
                kwargs = {
                    k: v
                    for rule in rules for k, v in rule.context.kwargs.items()
                }
                await task(message, *middleware_args, *args, **kwargs),
                break

        logger.info(
            'New BRANCHED "{0}" compiled with branch <{2}> (from: {1})'.format(
                message.text.replace("\n", " "),
                message.from_id,
                '"{}" with {} kwargs'.format(
                    branch.key,
                    branch.context if len(branch.context) < 100 else
                    branch.context[1:99] + "...",
                ),
            ))
Beispiel #7
0
    async def _processor(self, obj: dict, client_info: dict):
        processor = dict(obj=obj, client_info=client_info)
        middleware_args = []

        message = Message(**{
            **obj, "text":
            init_bot_mention(self.group_id, obj["text"])
        },
                          client_info=client_info)

        async for mr in self.middleware.run_middleware(message):
            if self.status.middleware_expressions:
                if mr is False:
                    return
                elif mr is not None:
                    middleware_args.append(mr)

        if message.peer_id in await self.branch.queue:
            await self._branched_processor(obj, client_info, middleware_args)
            return

        logger.debug(
            '-> MESSAGE FROM {} TEXT "{}"',
            message.from_id,
            message.text.replace("\n", " "),
        )

        task = None
        for rules in self.on.rules:

            for rule in rules:
                if not await rule(message):
                    break

            else:

                args = [a for rule in rules for a in rule.context.args]
                kwargs = {
                    k: v
                    for rule in rules for k, v in rule.context.kwargs.items()
                }

                args = [message, *middleware_args, *args]

                task = await rules[0].call(*args, **kwargs)

                logger.info(
                    'New message "{}" compiled with decorator <{}> (from: {}/{})'
                    .format(
                        message.text.replace("\n", " "),
                        rules[0].call.__name__,
                        message.peer_id,
                        message.from_id,
                    ))
                break

        await self._handler_return(task, **processor)
Beispiel #8
0
    async def branch_processor(self, obj: dict, client_info: dict,
                               middleware_args: list):
        """ Process messages for users in branch storage
        #TODO Make a simple branch extendable processor
        #TODO Move middleware_args
        """
        obj["text"] = sub(r"\[club" + str(self.group_id) + r"\|.*?\] ", "",
                          obj["text"])

        message = Message(**obj, client_info=client_info)
        branch_checkup_key = message.dict()[self.branch.checkup_key.value]

        logger.debug(
            '-> BRANCHED MESSAGE FROM {} TEXT "{}"',
            message.peer_id,
            message.text.replace("\n", " "),
        )

        disposal, branch = await self.branch.load(branch_checkup_key)
        edited = None

        for n, member in disposal.items():
            rules = member[1]
            for rule in rules:
                if not await rule(message):
                    break
            else:
                task = types.MethodType(member[0], branch)
                args = [a for rule in rules for a in rule.context.args]
                kwargs = {
                    k: v
                    for rule in rules for k, v in rule.context.kwargs.items()
                }
                edited = await self.handler_return(
                    await task(message, *middleware_args, *args, **kwargs),
                    data={
                        "object": obj,
                        "client_info": client_info
                    },
                )
                break

        # FIXME
        if (edited is False
                and self.branch.__class__.generator is GeneratorType.DATABASE):
            if branch_checkup_key in await self.branch.queue:
                logger.debug("Updating branch context")
                await self.branch.add(branch_checkup_key, branch.key,
                                      **branch.context)

        logger.info(
            'New BRANCHED "{0}" compiled with branch <{2}> (from: {1})'.format(
                message.text.replace("\n", " "),
                message.from_id,
                repr(branch.key),
            ))
Beispiel #9
0
 async def get_current_rest(self) -> dict:
     """
     Get current actual info about package from GitHub REST page
     """
     rest_status = await self.request.get(url=VERSION_REST,
                                          content_type="text/plain")
     if rest_status is None:
         logger.info("Unable to check version! Skipping it")
         return {"version": __version__}
     return rest_status
Beispiel #10
0
    async def _branched_processor(self, obj: dict, client_info: dict,
                                  middleware_args: list):
        """
        Branched messages processor manager
        :param obj: VK Server Event Object
        """
        obj["text"] = sub(r"\[club" + str(self.group_id) + r"\|.*?\] ", "",
                          obj["text"])

        answer = Message(**obj, client_info=client_info)

        logger.debug(
            '-> BRANCHED MESSAGE FROM {} TEXT "{}"',
            answer.peer_id,
            answer.text.replace("\n", " "),
        )

        disposal, branch = await self.branch.load(answer.peer_id)
        edited = None

        for n, member in disposal.items():
            rules = member[1]
            for rule in rules:
                if not await rule(answer):
                    break
            else:
                task = types.MethodType(member[0], branch)
                args = [a for rule in rules for a in rule.context.args]
                kwargs = {
                    k: v
                    for rule in rules for k, v in rule.context.kwargs.items()
                }
                edited = await self._handler_return(
                    await task(answer, *middleware_args, *args, **kwargs),
                    obj,
                    client_info,
                )
                break

        if edited is False and self.branch.generator is GeneratorType.DATABASE:
            if answer.peer_id in await self.branch.queue:
                await self.branch.add(answer.peer_id, branch.key,
                                      **branch.context)

        logger.info(
            'New BRANCHED "{0}" compiled with branch <{2}> (from: {1})'.format(
                answer.text.replace("\n", " "),
                answer.from_id,
                '"{}" with {} kwargs'.format(
                    branch.key,
                    branch.context if len(branch.context) < 100 else
                    branch.context[1:99] + "...",
                ),
            ))
Beispiel #11
0
    async def branch_processor(self, message: Message, middleware_args: list):
        """ Process messages for users in branch storage
        #TODO Make a simple branch extendable processor
        #TODO Move middleware_args
        """
        branch_checkup_key = message.dict()[self.branch.checkup_key.value]
        logger.debug(
            '-> BRANCHED MESSAGE FROM {} TEXT "{}"',
            branch_checkup_key,
            message.text.replace("\n", " "),
        )

        disposal, branch = await self.branch.load(branch_checkup_key)
        edited = None

        for n, member in disposal.items():
            rules = member[1]
            for rule in rules:
                if not await rule(message):
                    break
            else:
                task = types.MethodType(member[0], branch)
                args = [a for rule in rules for a in rule.context.args]
                kwargs = {
                    k: v
                    for rule in rules for k, v in rule.context.kwargs.items()
                }
                handler_return = await task(message, *middleware_args, *args,
                                            **kwargs)
                edited = await self.handler_return(handler_return,
                                                   message.dict())
                break

        # FIXME
        if edited is False and self.branch.generator is GeneratorType.DATABASE:
            if branch_checkup_key in await self.branch.queue:
                logger.debug("Updating branch context")
                await self.branch.add(branch_checkup_key, branch.key,
                                      **branch.context)

        logger.info(
            'New BRANCHED "{0}" compiled with branch <{2}> (from: {1})'.format(
                message.text.replace("\n", " "),
                branch_checkup_key,
                repr(branch.key),
            ))
Beispiel #12
0
    async def get_updates(self):
        # noqa
        logger.info("Receiving updates from conversations")
        updates = []
        close, offset = False, 0

        while not close:
            conversations = await self.api.messages.get_conversations(
                offset, 200, filter="unanswered"
            )
            if offset == 0:
                logger.info(f"Conversation count - {conversations.count}")
                if conversations.count == 0:
                    return
            offset += 200

            updates.extend([item.conversation.out_read for item in conversations.items])
            if len(conversations.items) < 200:
                close = True

        logger.warning("Answering...")

        chunk = list(chunks(updates, 100))
        for mid in chunk:
            try:
                messages = await self.api.messages.get_by_id(mid)
                await self.emulate(
                    {
                        "updates": [
                            {
                                "type": "message_new",
                                "object": {"message": m.dict(), "client_info": {}},
                            }
                            for m in messages.items
                        ]
                    }
                )
            except VKError:
                continue
Beispiel #13
0
 async def enter(self):
     logger.info("Branch {} entered at", self.key)
Beispiel #14
0
    async def message_processor(self, obj: dict, client_info: dict):
        processor = dict(object=obj, client_info=client_info)
        middleware_args = []

        message = Message(
            **{
                **obj, "text": init_bot_mention(self.group_id, obj["text"])
            },
            client_info=client_info,
        )
        branch_checkup_key = message.dict()[self.branch.checkup_key.value]

        async for mr in self.middleware.run_middleware(
                message, flag=MiddlewareFlags.PRE):
            if self.status.middleware_expressions:
                if mr is False:
                    return
                elif mr is not None:
                    middleware_args.append(mr)

        if branch_checkup_key in await self.branch.queue:
            await self.branch_processor(obj, client_info, middleware_args)
            return

        logger.debug(
            '-> MESSAGE FROM {} TEXT "{}"',
            message.from_id,
            message.text.replace("\n", " "),
        )

        for rules in self.handler.message_rules:

            for rule in rules:
                if not await rule(message):
                    break

            else:

                args = [a for rule in rules for a in rule.context.args]
                kwargs = {
                    k: v
                    for rule in rules for k, v in rule.context.kwargs.items()
                }
                args = [message, *middleware_args, *args]

                task = await rules[0].call(*args, **kwargs)
                await self.handler_return(task, data=processor)

                logger.info(
                    'New message "{}" compiled with decorator <{}> (from: {}/{})'
                    .format(
                        message.text.replace("\n", " "),
                        rules[0].call.__name__,
                        message.peer_id,
                        message.from_id,
                    ))
                break

        async for mr in self.middleware.run_middleware(message,
                                                       MiddlewareFlags.POST,
                                                       *middleware_args):
            logger.debug(f"POST Middleware handler returned: {mr}")
Beispiel #15
0
    def __init__(
        self,
        tokens: Token = None,
        *,
        group_id: int = None,
        debug: typing.Union[str, bool] = True,
        loop: asyncio.AbstractEventLoop = None,
        throw_errors: bool = True,
        log_to_path: typing.Union[str, bool] = None,
        patcher: Patcher = None,
        mobile: bool = False,
        secret: str = None,
        extension: AbstractExtension = None,
        logs_folder: typing.Optional[str] = None,
        only_asyncio_loop: bool = False,
        **context,
    ):
        """
        Init bot
        :param tokens: bot tokens
        :param group_id:
        :param debug: should bot debug messages for emulating
        :param log_to_path: make logs
        :param secret: secret vk code for callback
        :param extension:
        """
        # Base bot classifiers
        self.__tokens: typing.List[str] = [tokens] if isinstance(
            tokens, str
        ) else tokens
        self.__debug: bool = debug
        self.__wait = None
        self.__secret = secret
        self._status: BotStatus = BotStatus()

        self.context: dict = context

        if uvloop is not None:
            if not only_asyncio_loop:
                asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

        if isinstance(debug, bool):
            debug = "INFO" if debug else "ERROR"

        self.logger = LoggerLevel(debug)

        if not Patcher.get_current():
            Patcher.set_current(
                patcher
                if patcher is not None
                else Patcher(pattern="^{}$", validators=DefaultValidators)
            )

        logger.remove()
        logger.add(
            sys.stderr,
            colorize=True,
            format="<level>[<blue>VKBottle</blue>] {message}</level> <white>[TIME {time:HH:MM:ss}]</white>",
            filter=self.logger,
            level=0,
            enqueue=mobile is False,
        )
        logger.level("INFO", color="<white>")
        logger.level("ERROR", color="<red>")
        if log_to_path:
            logger.add(
                (logs_folder or "") + "log_{time}.log"
                if log_to_path is True
                else log_to_path,
                rotation="20 MB",
            )

        self.group_id = group_id or self.get_id_by_token(self.__tokens[0])
        self.__loop = loop or asyncio.get_event_loop()

        # Sign assets
        self.api: Api = Api(self.__tokens, throw_errors=throw_errors)
        self.extension: AbstractExtension = extension if extension is not None else StandardExtension()

        self._throw_errors: bool = throw_errors
        Api.set_current(self.api)
        AbstractExtension.set_current(self.extension)

        # Main workers
        self.branch: typing.Union[AbstractBranchGenerator, DictBranch] = DictBranch()
        self.middleware: MiddlewareExecutor = MiddlewareExecutor()
        self.on: Handler = Handler(self.group_id)
        self.error_handler: VKErrorHandler = DefaultErrorHandler()

        self._stop: bool = False

        logger.info("Using JSON_MODULE - {}".format(USAGE))
        logger.info(
            "Using asyncio loop - {}".format(
                asyncio.get_event_loop_policy().__class__.__module__
            )
        )
Beispiel #16
0
    def __init__(
        self,
        tokens: Token = None,
        *,
        login: str = None,
        password: str = None,
        user_id: int = None,
        debug: typing.Union[str, bool] = True,
        loop: asyncio.AbstractEventLoop = None,
        expand_models: bool = True,
        mobile: bool = False,
        log_to_path: typing.Union[str, bool] = None,
        vbml_patcher: vbml.Patcher = None,
        mode: int = 234,
        only_asyncio_loop: bool = False,
        **context,
    ):
        self.__tokens = [tokens] if isinstance(tokens, str) else tokens

        self.context: dict = context

        if uvloop is not None:
            if not only_asyncio_loop:
                asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

        if login and password:
            self.__tokens = self.get_tokens(login, password)

        self.loop = loop or asyncio.get_event_loop()
        self.__debug: bool = debug
        self._api: UserApi = UserApi(self.__tokens)
        self.mode = mode

        self._expand_models: bool = expand_models
        self._patcher: vbml.Patcher = vbml_patcher or vbml.Patcher(
            pattern="^{}$")

        self.user_id: typing.Optional[int] = user_id or self.get_id_by_token(
            self.__tokens[0])

        self._api.user_id = user_id
        self.error_handler: VKErrorHandler = DefaultErrorHandler()
        UserApi.set_current(self._api)
        VKErrorHandler.set_current(self.error_handler)

        self.on: UserHandler = UserHandler()
        self.branch: ABCBranchGenerator = DictBranch()
        self.middleware: MiddlewareExecutor = MiddlewareExecutor()
        self.error_handler: VKErrorHandler = DefaultErrorHandler()
        self.deconstructed_handle: UserProcessor = UserProcessor(
            self.user_id, expand_models=expand_models)

        self._stop: bool = False
        self.status = UserStatus()

        if isinstance(debug, bool):
            debug = "INFO" if debug else "ERROR"

        self.logger = LoggerLevel(debug)
        logger.remove()
        logger.add(
            sys.stderr,
            colorize=True,
            format="<level>[<blue>VKBottle</blue>] {message}</level>",
            filter=self.logger,
            level=0,
            enqueue=mobile is False,
        )
        if log_to_path:
            logger.add(
                "log_user_{time}.log" if log_to_path is True else log_to_path,
                rotation="20 MB",
            )

        logger.info("Using JSON_MODULE - {}".format(USAGE))
        logger.info("Using asyncio loop - {}".format(
            asyncio.get_event_loop_policy().__class__.__module__))
Beispiel #17
0
 async def exit(self):
     logger.info("Branch {} exit at", self.key or self.data["call"].__name__)
Beispiel #18
0
 async def enter(self):
     logger.info("Branch {} entered at", self.key or self.data["call"].__name__)
Beispiel #19
0
 async def exit(self):
     logger.info("Branch {} exit at", self.key)