87 lines
2.5 KiB
Python
87 lines
2.5 KiB
Python
from __future__ import annotations
|
||
|
||
import asyncio
|
||
|
||
from zvk.util.network import Network
|
||
from zvk.util.zlogging import logger
|
||
|
||
# BOT_MESSAGE_RANDOM_ID_MIN = 1337000000
|
||
# BOT_MESSAGE_RANDOM_ID_MAX = 1338000000
|
||
|
||
API_VERSION = '5.85'
|
||
|
||
|
||
class MagicAccumulatingAttributeCatcher:
|
||
api: VKApi
|
||
full_method_name: str
|
||
|
||
def __init__(self, api, full_method_name=None):
|
||
self.api = api
|
||
|
||
if full_method_name is None:
|
||
full_method_name = ''
|
||
|
||
self.full_method_name = full_method_name
|
||
|
||
def __getattr__(self, item):
|
||
return MagicAccumulatingAttributeCatcher(self.api, f'{self.full_method_name}.{item}')
|
||
|
||
async def __call__(self, **kwargs):
|
||
return await self.api.call_method(self.full_method_name, **kwargs)
|
||
|
||
|
||
API_CALL_RETRY_COUNT = 3
|
||
|
||
|
||
class VKApi:
|
||
"""
|
||
VK api asynchronous interaction interface. Supports dope syntax like
|
||
`await api.messages.get()`.
|
||
"""
|
||
|
||
_access_token: str
|
||
_net: Network
|
||
|
||
def __init__(self, config, net):
|
||
self._access_token = config['api']['access_token']
|
||
self._net = net
|
||
|
||
def __getattr__(self, name):
|
||
return MagicAccumulatingAttributeCatcher(self, name)
|
||
|
||
async def call_method(self, full_method_name, **params):
|
||
url = f'https://api.vk.com/method/{full_method_name}'
|
||
|
||
params['access_token'] = self._access_token
|
||
params['v'] = API_VERSION
|
||
|
||
# TODO: random_id management
|
||
# if self.name == 'messages.send' and 'random_id' not in params:
|
||
# params['random_id'] = random.randint(BOT_MESSAGE_RANDOM_ID_MIN, BOT_MESSAGE_RANDOM_ID_MAX)
|
||
# if self.name == 'messages.send' and 'message' in params:
|
||
# params['message'] = params['message'].replace('--', '--')
|
||
|
||
# filter empty values
|
||
params = {k: v for k, v in params.items() if v is not None}
|
||
|
||
for retry in range(API_CALL_RETRY_COUNT):
|
||
response, result = await self._net.post_json(url, sequential=True, data=params)
|
||
|
||
if 'error' in result:
|
||
error = result['error']
|
||
if error['error_code'] == 6:
|
||
logger.warning('Too many requests, waiting and retrying...')
|
||
await asyncio.sleep(1)
|
||
continue
|
||
|
||
raise RuntimeError(f'A significant VKApi error occurred {full_method_name}({params}) -> {error}')
|
||
|
||
if 'response' not in result:
|
||
raise RuntimeError(f'Malformed api response {full_method_name}({params}) -> {result}')
|
||
|
||
logger.debug(f'VKApi call {full_method_name}({params}) -> {result}')
|
||
|
||
return result['response']
|
||
|
||
raise RuntimeError(f'VKApi call unsuccessful after retries: {full_method_name}({params})')
|