Esempio n. 1
0
 async def _run(self, owner_id):
     if owner_id and owner_id.__class__ is not int:
         raise TypeError(
             'Owner_id must be positive integer, not {0.__class__.__name__}'
             .format(owner_id))
     if owner_id and owner_id < 0:
         raise VKApiError('Owner_id must be positive integer')
     user = await self.get_own_page()
     if isinstance(user, Group):
         self.is_group = True
         self.group = user
         if self.is_group and owner_id:
             raise VKApiError(
                 'Owner_id passed together with group access_token')
         ts = await self.get_longpoll_server()
         await self.print_warnings()
         self.dispatch('ready')
         updates = []
         while True:
             try:
                 lp = self.loop.create_task(self.longpoll(ts))
                 for update in updates:
                     self.handle_update(update)
                 ts, updates = await lp
             except Exception as e:
                 print(
                     'Ignoring exception in longpoll cycle:\n{}'.format(e),
                     file=sys.stderr)
                 ts = await self.get_longpoll_server()
     raise LoginError('User token passed to group client')
Esempio n. 2
0
    async def get_groups(self, *gids):
        """|coro|

        Alias for VK Api 'groups.get' method call

        Parameters
        ----------
        gids: List[:class:`int`]
            List of group ids to request

        Raises
        --------
        vk_botting.VKApiError
            When error is returned by VK API.

        Returns
        -------
        List[:class:`.Group`]
            List of :class:`.Group` instances for requested groups
        """
        groups = await self.vk_request('groups.getById',
                                       group_ids=','.join(map(str, gids)))
        if 'error' in groups.keys():
            raise VKApiError(
                '[{error_code}] {error_msg}'.format(**groups['error']))
        groups = groups.get('response')
        return [Group(group) for group in groups]
Esempio n. 3
0
    async def get_users(self, *uids, fields=None, name_case=None):
        """|coro|

        Alias for VK Api 'users.get' method call

        Parameters
        ----------
        uids: List[:class:`int`]
            List of user ids to request
        fields: List[:class:`str`]
            Optional. Fields that should be requested from VK Api. None by default
        name_case: :class:`str`
            Optional. Name case for users' names to be returned in. 'nom' by default. Can be 'nom', 'gen', 'dat', 'acc', 'ins' or 'abl'

        Raises
        --------
        vk_botting.VKApiError
            When error is returned by VK API.

        Returns
        -------
        List[:class:`.User`]
            List of :class:`.User` instances for requested users
        """
        if name_case is None:
            name_case = 'nom'
        users = await self.vk_request('users.get',
                                      user_ids=uids,
                                      fields=fields,
                                      name_case=name_case)
        if 'error' in users.keys():
            raise VKApiError(
                '[{error_code}] {error_msg}'.format(**users['error']))
        users = users.get('response')
        return [User(self, user) for user in users]
Esempio n. 4
0
    def add_vkpay_button(self, _hash, payload=None):
        """Adds button to current line

        Parameters
        ----------
        _hash: :class:`str`
            Hash for a VKPay button
        payload: :class:`dict`
            Payload for a VKPay button

        Raises
        ------
        vk_botting.VKApiError
            When there are already too many buttons on one line
        """
        current_line = self.lines[-1]

        if len(current_line) != 0:
            raise VKApiError(
                'This type of button takes the entire width of the line')

        if payload is not None and not isinstance(payload, str):
            payload = to_json(payload)

        button_type = KeyboardButton.VKPAY.value

        current_line.append({
            'action': {
                'type': button_type,
                'payload': payload,
                'hash': _hash
            }
        })
Esempio n. 5
0
 async def _run(self, owner_id):
     if owner_id and owner_id.__class__ is not int:
         raise TypeError(
             'Owner_id must be positive integer, not {0.__class__.__name__}'
             .format(owner_id))
     if owner_id and owner_id < 0:
         raise VKApiError('Owner_id must be positive integer')
     user = await self.get_own_page()
     if isinstance(user, User):
         self.is_group = False
         self.group = Group({})
         self.user = user
         ts = await self.get_user_longpoll()
         self.dispatch('ready')
         updates = []
         while True:
             try:
                 lp = self.loop.create_task(self.longpoll(ts))
                 for update in updates:
                     self.loop.create_task(self.handle_user_update(update))
                 ts, updates = await lp
             except Exception as e:
                 print(
                     'Ignoring exception in longpoll cycle:\n{}'.format(e),
                     file=sys.stderr)
                 ts = await self.get_user_longpoll()
     raise LoginError('Group token passed to user client')
Esempio n. 6
0
    async def delete(self, delete_for_all=1):
        """|coro|

        Coroutine to delete the message.

        Parameters
        ----------
        delete_for_all: :class:`bool`
            ``True`` if message should be deleted for all users. Defaults to ``True``

        Raises
        --------
        vk_botting.VKApiError
            When error is returned by VK API.
        """
        params = {
            'group_id': self.bot.group.id,
            'delete_for_all': delete_for_all,
            'peer_id': self.peer_id,
            'conversation_message_ids': self.conversation_message_id,
        }
        res = await self.bot.vk_request('messages.delete', **params)
        if 'error' in res.keys():
            raise VKApiError(
                '[{error_code}] {error_msg}'.format(**res['error']))
Esempio n. 7
0
 async def get_longpoll_server(self):
     res = await self.vk_request('groups.getLongPollServer',
                                 group_id=self.group.id)
     error = res.get('error', None)
     if error and error['error_code'] == 100:
         if self.force:
             await self.enable_longpoll()
             return await self.get_longpoll_server()
         raise VKApiError(
             'Longpoll is disabled for this group. Enable longpoll or try force mode'
         )
     elif error:
         raise VKApiError('[{error_code}]{error_msg}'.format(**error))
     self.key = res['response']['key']
     self.server = res['response']['server'].replace(r'\/', '/')
     ts = res['response']['ts']
     return ts
Esempio n. 8
0
 async def _answer(self, event_data):
     res = await self.bot.vk_request('messages.sendMessageEventAnswer',
                                     event_id=self.event_id,
                                     user_id=self.user_id,
                                     peer_id=self.peer_id,
                                     event_data=event_data)
     if 'error' in res.keys():
         raise VKApiError(
             '[{error_code}] {error_msg}'.format(**res['error']))
     return res
Esempio n. 9
0
    def add_line(self):
        """Adds new line to the keyboard

        Raises
        ------
        vk_botting.VKApiError
            When there are already too many lines in a keyboard
        """
        if (len(self.lines) > 5 and self.inline) or len(self.lines) > 9:
            num = 6 if self.inline else 10
            raise VKApiError('Max {} lines'.format(num))
        self.lines.append([])
Esempio n. 10
0
    def _add_button(self, button_type, label, color, payload):
        current_line = self.lines[-1]

        if len(current_line) > 4:
            raise VKApiError('Max 5 buttons on a line')
        color_value = color
        if isinstance(color, KeyboardColor):
            color_value = color_value.value
        current_line.append({
            'color': color_value,
            'action': {
                'type': button_type,
                'payload': payload,
                'label': label,
            }
        })
Esempio n. 11
0
 async def get_user_longpoll(self):
     res = await self.vk_request('messages.getLongPollServer',
                                 group_id=self.group.id,
                                 lp_version=3)
     error = res.get('error', None)
     if error and error['error_code'] == 15:
         raise LoginError(
             'User has no access to messages API. Try generating token with vk_botting.auth methods'
         )
     elif error:
         raise VKApiError(
             '[{error_code}] {error_msg}'.format(**res['error']))
     self.key = res['response']['key']
     server = res['response']['server'].replace(r'\/', '/')
     self.server = 'https://{}'.format(server)
     ts = res['response']['ts']
     return ts
Esempio n. 12
0
    async def edit(self,
                   message=None,
                   *,
                   attachment=None,
                   keep_forward_messages=1,
                   keep_snippets=1):
        """|coro|

        Coroutine to edit the message.

        Parameters
        ----------
        message: :class:`str`
            New text of the message.
        attachment: Union[List[:class:`str`], :class:`str`, List[:class:`.Attachment`], :class:`.Attachment`]
            New attachment to the message.
        keep_forward_messages: :class:`bool`
            ``True`` if fwd_messages should be kept. Defaults to ``True``
        keep_snippets: :class:`bool`
            ``True`` if snippets should be kept. Defaults to ``True``

        Raises
        --------
        vk_botting.VKApiError
            When error is returned by VK API.

        Returns
        ---------
        :class:`.Message`
            The message that was sent.
        """
        params = {
            'group_id': self.bot.group.id,
            'peer_id': self.peer_id,
            'message': message,
            'attachment': attachment,
            'keep_forward_messages': keep_forward_messages,
            'keep_snippets': keep_snippets,
            'conversation_message_id': self.conversation_message_id
        }
        res = await self.bot.vk_request('messages.edit', **params)
        if 'error' in res.keys():
            raise VKApiError(
                '[{error_code}] {error_msg}'.format(**res['error']))
        return res
Esempio n. 13
0
 async def edit(self,
                message=None,
                *,
                attachment=None,
                keep_forward_messages='true',
                keep_snippets='true'):
     params = {
         'peer_id': self.peer_id,
         'message': message,
         'attachment': attachment,
         'message_id': self.id,
         'keep_forward_messages': keep_forward_messages,
         'keep_snippets': keep_snippets
     }
     res = await self.bot.vk_request('messages.edit', **params)
     if 'error' in res.keys():
         raise VKApiError(
             '[{error_code}] {error_msg}'.format(**res['error']))
     return res
Esempio n. 14
0
    def add_vkapps_button(self, app_id, owner_id, label, _hash, payload=None):
        """Adds button to current line

        Parameters
        ----------
        app_id: :class:`int`
            Id of VK App
        owner_id: :class:`int`
            Id of VK App owner
        label: :class:`str`
            Button label to be displayed
        _hash: :class:`str`
            Hash for a VK App button
        payload: :class:`dict`
            Optional. Should be used for some button types

        Raises
        ------
        vk_botting.VKApiError
            When there are already too many buttons on one line
        """
        current_line = self.lines[-1]

        if len(current_line) != 0:
            raise VKApiError(
                'This type of button takes the entire width of the line')

        if payload is not None and not isinstance(payload, str):
            payload = to_json(payload)

        button_type = KeyboardButton.VKAPPS.value

        current_line.append({
            'action': {
                'type': button_type,
                'app_id': app_id,
                'owner_id': owner_id,
                'label': label,
                'payload': payload,
                'hash': _hash
            }
        })
Esempio n. 15
0
 async def reply(self,
                 message=None,
                 *,
                 attachment=None,
                 sticker_id=None,
                 keyboard=None):
     peer_id = await self._get_conversation()
     params = {
         'random_id': randint(-2**63, 2**63 - 1),
         'peer_id': peer_id,
         'message': message,
         'attachment': attachment,
         'reply_to': self.id,
         'sticker_id': sticker_id,
         'keyboard': keyboard
     }
     res = await self.bot.vk_request('messages.send', **params)
     if 'error' in res.keys():
         raise VKApiError(
             '[{error_code}] {error_msg}'.format(**res['error']))
     params['id'] = res['response']
     return self.bot.build_msg(params)
Esempio n. 16
0
 async def _vk_request(self, method, post, calln=1, **kwargs):
     if calln > 10:
         raise VKApiError('VK API call failed after 10 retries')
     for param in kwargs:
         if isinstance(kwargs[param], (list, tuple)):
             kwargs[param] = ','.join(map(str, kwargs[param]))
         elif isinstance(kwargs[param], dict):
             kwargs[param] = to_json(kwargs[param])
     res = await self.general_request(
         'https://api.vk.com/method/{}'.format(method), post=post, **kwargs)
     if isinstance(res, str):
         await asyncio.sleep(0.1)
         return await self._vk_request(method, post, calln + 1, **kwargs)
     error = res.get('error', None)
     if error and error.get('error_code', None) == 6:
         await asyncio.sleep(1)
         return await self._vk_request(method, post, calln + 1, **kwargs)
     elif error and error.get(
             'error_code', None
     ) == 10 and 'could not check access_token now' in error.get(
             'error_msg', ''):
         await asyncio.sleep(0.1)
         return await self._vk_request(method, post, calln + 1, **kwargs)
     return res
Esempio n. 17
0
    def add_callback_button(self,
                            label,
                            color=KeyboardColor.PRIMARY,
                            payload=None):
        """Adds a callback button (https://vk.com/dev/bots_docs_5) to current line

        Parameters
        ----------
        label: :class:`str`
            Button label to be displayed
        color: :class:Union[`str`, `KeyboardColor`]
            Button color. Can be value from :class:`.KeyboardColor` enum
        payload: :class:`dict`
            Optional. Should be used for some buttons

        Raises
        ------
        vk_botting.VKApiError
            When there are already too many buttons on one line
        """
        if not self.inline:
            raise VKApiError(
                'Cannot add a callback button to non-inline keyboard')
        self._add_button(KeyboardButton.CALLBACK.value, label, color, payload)
Esempio n. 18
0
    async def send_message(self,
                           peer_id=None,
                           message=None,
                           attachment=None,
                           sticker_id=None,
                           keyboard=None,
                           reply_to=None,
                           forward_messages=None,
                           forward=None,
                           **kwargs):
        """|coro|

        Sends a message to the given destination with the text given.

        The content must be a type that can convert to a string through ``str(message)``.

        If the content is set to ``None`` (the default), then the ``attachment`` or ``sticker_id`` parameter must
        be provided.

        If the ``attachment`` parameter is provided, it must be :class:`str`, List[:class:`str`], :class:`.Attachment` or List[:class:`.Attachment`]

        If the ``keyboard`` parameter is provided, it must be :class:`str` or :class:`.Keyboard` (recommended)

        Parameters
        ------------
        peer_id: :class:`int`
            Id of conversation to send message to
        message: :class:`str`
            The text of the message to send.
        attachment: Union[List[:class:`str`], :class:`str`, List[:class:`.Attachment`], :class:`.Attachment`]
            The attachment to the message sent.
        sticker_id: Union[:class:`str`, :class:`int`]
            Sticker_id to be sent.
        keyboard: :class:`.Keyboard`
            The keyboard to send along message.
        reply_to: Union[:class:`str`, :class:`int`]
            A message id to reply to.
        forward_messages: Union[List[:class:`int`], List[:class:`str`]]
            Message ids to be forwarded along with message.
        forward: :class:`dict`
            Check docs for more details on this one.
        as_user: :class:`bool`
            If message should be sent as user (using attached user token).

        Raises
        --------
        vk_botting.VKApiError
            When error is returned by VK API.

        Returns
        ---------
        :class:`.Message`
            The message that was sent.
        """
        as_user = kwargs.pop('as_user', False)
        if kwargs:
            print('Unknown parameters passed to send_message: {}'.format(
                ', '.join(kwargs.keys())),
                  file=sys.stderr)
        if isinstance(attachment, str) or attachment is None:
            pass
        elif isinstance(attachment, Iterable):
            attachment = ','.join(map(str, attachment))
        else:
            attachment = str(attachment)
        if message:
            message = str(message)
            if len(message) > 4096:
                w = textwrap.TextWrapper(width=4096, replace_whitespace=False)
                messages = w.wrap(message)
                for message in messages[:-1]:
                    await self.send_message(peer_id, message, **kwargs)
                return await self.send_message(
                    peer_id,
                    messages[-1],
                    attachment=attachment,
                    sticker_id=sticker_id,
                    keyboard=keyboard,
                    reply_to=reply_to,
                    forward_messages=forward_messages,
                    **kwargs)
        params = {
            'random_id': getrandbits(64),
            'message': message,
            'attachment': attachment,
            'reply_to': reply_to,
            'forward_messages': forward_messages,
            'sticker_id': sticker_id,
            'keyboard': keyboard,
            'forward': forward
        }
        if self.is_group and not as_user:
            params['group_id'] = self.group.id
            params['peer_ids'] = peer_id
        else:
            params['peer_id'] = peer_id
        res = await self.vk_request(
            'messages.send', **
            params) if not as_user else await self.user_vk_request(
                'messages.send', **params)
        if 'error' in res.keys():
            if res['error'].get('error_code') == 9:
                await asyncio.sleep(1)
                return await self.send_message(
                    peer_id,
                    message,
                    attachment=attachment,
                    sticker_id=sticker_id,
                    keyboard=keyboard,
                    reply_to=reply_to,
                    forward_messages=forward_messages,
                    **kwargs)
            raise VKApiError(
                '[{error_code}] {error_msg}'.format(**res['error']))
        if self.is_group and not as_user:
            params['from_id'] = -self.group.id
            params['conversation_message_id'] = res['response'][0][
                'conversation_message_id']
            params['id'] = res['response'][0]['message_id']
            params['peer_id'] = peer_id
            params.pop('peer_ids')
        else:
            params['from_id'] = self.user.id
            params['id'] = res['response']
        return self.build_msg(params)