Support for audio 🎵. Messaging api with Kate Mobile app id.

And miserable stability improvements.
This commit is contained in:
Kylmakalle 2019-02-19 22:57:46 +03:00
parent ae21a3cfba
commit 324e2f0ae3
7 changed files with 109 additions and 23 deletions

View File

@ -11,5 +11,9 @@ chmod +x install.sh
...
Telegram Token: 123456789:AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLL
VK APP ID: 1234567
VK APP ID: 1234567 # можно пропустить, будет использован идентификатор Kate Mobile
```
# Сервисы музыки
API - https://github.com/Kylmakalle/thatmusic-api
Token Refresher - https://github.com/Kylmakalle/vk-audio-token/tree/refresh-api

16
bot.py
View File

@ -102,3 +102,19 @@ async def get_content(url, docname='tgvkbot.document', chrome_headers=True, rewr
bot = Bot(token=BOT_TOKEN)
dp = Dispatcher(bot)
dp.loop.set_task_factory(context.task_factory)
import traceback
@dp.errors_handler()
async def all_errors_handler(dp, update, e):
if 'message' in dir(update) and update.message:
user = update.message.from_user.full_name
user_id = update.message.from_user.id
else:
user = update.callback_query.from_user.full_name
user_id = update.callback_query.from_user.id
logging.error(f'The update was: {update}')
logging.exception(traceback.print_exc())
return True

View File

@ -28,7 +28,15 @@ DATABASE_HOST = os.environ.get('DATABASE_HOST', 'db')
DATABASE_PORT = os.environ.get('DATABASE_PORT', '5432')
DATABASE_NAME = os.environ.get('POSTGRES_DB', 'tgvkbot')
VK_APP_ID = os.environ.get('VK_APP_ID')
VK_APP_ID = os.environ.get('VK_APP_ID', '2685278') # Kate mobile
AUDIO_URL = os.environ.get('AUDIO_URL', 'http://thatmusic.akentev.com/id/{owner_id}/{audio_id}')
AUDIO_ACCESS_URL = os.environ.get('AUDIO_ACCESS_URL',
'http://thatmusic.akentev.com/access_id/{token}/{owner_id}/{audio_id}')
TOKEN_REFRESH_URL = os.environ.get('TOKEN_REFRESH_URL', 'http://thatmusic.akentev.com/refresh')
AUDIO_HEADERS = {
'user-agent': 'KateMobileAndroid/52.1 lite-445 (Android 4.4.2; SDK 19; x86; unknown Android SDK built for x86; en)'}
BOT_TOKEN = os.environ.get('BOT_TOKEN')
@ -36,7 +44,7 @@ SETTINGS_VAR = os.environ.get('SETTINGS_VAR', 'DJANGO_TGVKBOT_SETTINGS_MODULE')
MAX_FILE_SIZE = os.environ.get('MAX_FILE_SIZE', 52428800)
API_VERSION = os.environ.get('API_VERSION', '5.73')
API_VERSION = os.environ.get('API_VERSION', '5.78')
# https://www.miniwebtool.com/django-secret-key-generator/
# Возможно достаточно заглушки в стиле 'tgvkbot-super-secret-key(nope)'

View File

@ -42,4 +42,4 @@ networks:
web_nw:
driver: bridge
volumes:
dbdata:
dbdata:

View File

@ -1,4 +1,4 @@
from config import API_VERSION
from config import API_VERSION, VK_APP_ID
from urllib.request import urlopen, Request
from urllib.error import HTTPError
from urllib.parse import urlencode
@ -48,14 +48,15 @@ def set_env():
while True:
vk_app_id = input('VK APP ID: ')
vk_app_id = vk_app_id.strip()
try:
get_auth_page(vk_app_id)
break
except HTTPError:
print('VK APP ID is invalid, try again!')
if vk_app_id:
try:
get_auth_page(vk_app_id)
break
except HTTPError:
print('VK APP ID is invalid, try again!')
with open('env_file', 'w') as env_file:
env_file.write(ENV_FILE_TEMPLATE % {'tg_token': tg_token, 'vk_app_id': vk_app_id})
env_file.write(ENV_FILE_TEMPLATE % {'tg_token': tg_token, 'vk_app_id': vk_app_id or VK_APP_ID})
print('Success!')

View File

@ -9,7 +9,8 @@ from vk_messages import vk_polling_tasks, vk_polling
log = logging.getLogger('telegram')
oauth_link = re.compile('https://oauth\.vk\.com/blank\.html#access_token=([a-z0-9]*)&expires_in=[0-9]*&user_id=[0-9]*')
oauth_link = re.compile(
'https://(oauth|api)\.vk\.com/blank\.html#access_token=([a-z0-9]*)&expires_in=[0-9]*&user_id=[0-9]*')
async def get_pages_switcher(markup, page, pages):
@ -273,6 +274,22 @@ async def search_dialogs(msg: types.Message, user=None):
await bot.send_message(msg.chat.id, text, reply_markup=markup, parse_mode=ParseMode.HTML)
async def refresh_token(vkuser):
try:
with aiohttp.ClientSession() as session:
r = await session.request('GET', TOKEN_REFRESH_URL, params={'token': vkuser.token})
data = await r.json()
if data['ok']:
vkuser.token = data['token']
vkuser.save()
session.close()
else:
return False
return True
except:
pass
@dp.callback_query_handler(func=lambda call: call and call.message and call.data and call.data.startswith('logged'))
async def check_logged(call: types.CallbackQuery):
vkuser = VkUser.objects.filter(owner__uid=call.from_user.id).count()
@ -512,7 +529,7 @@ async def send_welcome(msg: types.Message):
existing_vkuser = VkUser.objects.filter(owner=user).count()
if not existing_vkuser:
link = 'https://oauth.vk.com/authorize?client_id={}&' \
'display=page&redirect_uri=https://oauth.vk.com/blank.html&scope=friends,messages,offline,docs,photos,video,stories' \
'display=page&redirect_uri=https://oauth.vk.com/blank.html&scope=friends,messages,offline,docs,photos,video,stories,audio' \
'&response_type=token&v={}'.format(VK_APP_ID, API_VERSION)
mark = InlineKeyboardMarkup()
login = InlineKeyboardButton('ВХОД', url=link)
@ -651,7 +668,8 @@ async def handle_text(msg: types.Message):
if msg.chat.type == 'private':
m = oauth_link.search(msg.text)
if m:
token = m.group(1)
await bot.send_chat_action(msg.from_user.id, ChatActions.TYPING)
token = m.group(2)
if not VkUser.objects.filter(token=token).exists():
try:
session = VkSession(access_token=token, driver=await get_driver(token))
@ -666,10 +684,13 @@ async def handle_text(msg: types.Message):
if driver:
driver.close()
del DRIVERS[vkuser.token]
refreshed_token = await refresh_token(vkuser)
TASKS.append({'token': vkuser.token, 'task': asyncio.ensure_future(vk_polling(vkuser))})
await msg.reply(
'Вход выполнен в аккаунт {} {}!\n[Использование](https://asergey.me/tgvkbot/usage/)'.format(
logged_in = await msg.reply(
'Вход выполнен в аккаунт {} {}!\n[Использование](https://akentev.com/tgvkbot/usage/)'.format(
vkuserinfo['first_name'], vkuserinfo.get('last_name', '')), parse_mode='Markdown')
if refreshed_token:
await logged_in.reply('*Вам доступна музыка 🎵*', parse_mode='Markdown')
except VkAuthError:
await msg.reply('Неверная ссылка, попробуйте ещё раз!')
else:
@ -724,7 +745,7 @@ async def handle_photo(msg: types.Message):
if not vk_message:
await msg.reply('<b>Произошла ошибка при отправке</b>', parse_mode=ParseMode.HTML)
else:
msg.reply('<b>Ошибка при загрузке файла. Сообщение не отправлено!</b>', parse_mode=ParseMode.HTML)
await msg.reply('<b>Ошибка при загрузке файла. Сообщение не отправлено!</b>', parse_mode=ParseMode.HTML)
@dp.message_handler(content_types=['document', 'voice', 'audio', 'sticker'])

View File

@ -555,7 +555,7 @@ async def process_message(msg, token=None, is_multichat=None, vk_chat_id=None, u
disable_notify = bool(vk_msg.get('push_settings', False))
attaches_scheme = []
if vk_msg.get('attachments'):
attaches_scheme = [await process_attachment(attachment) for attachment in
attaches_scheme = [await process_attachment(attachment, token) for attachment in
vk_msg['attachments']]
if vk_msg.get('geo'):
location = vk_msg['geo']['coordinates'].split(' ')
@ -608,7 +608,10 @@ async def process_message(msg, token=None, is_multichat=None, vk_chat_id=None, u
if check_url:
body_parts[body_part] = body_parts[body_part].replace(i.group(0),
hlink(f'{i.group(2)}', url=vk_url))
await bot.send_chat_action(to_tg_chat, ChatActions.TYPING)
try:
await bot.send_chat_action(to_tg_chat, ChatActions.TYPING)
except:
return
tg_message = await bot.send_message(vkuser.owner.uid, body_parts[body_part],
parse_mode=ParseMode.HTML,
reply_to_message_id=main_message,
@ -630,7 +633,10 @@ async def process_message(msg, token=None, is_multichat=None, vk_chat_id=None, u
check_url = await check_vk_url(vk_url)
if check_url:
body = body.replace(i.group(0), hlink(f'{i.group(2)}', url=vk_url))
await bot.send_chat_action(to_tg_chat, ChatActions.TYPING)
try:
await bot.send_chat_action(to_tg_chat, ChatActions.TYPING)
except:
return
header_message = tg_message = await bot.send_message(to_tg_chat, header + body,
parse_mode=ParseMode.HTML,
reply_to_message_id=main_message,
@ -697,7 +703,10 @@ async def process_message(msg, token=None, is_multichat=None, vk_chat_id=None, u
await bot.send_chat_action(to_tg_chat, ChatActions.FIND_LOCATION)
tg_message = await tgsend(bot.send_venue, to_tg_chat, *attachment['content'],
reply_to_message_id=main_message, disable_notification=disable_notify)
elif attachment['type'] == 'audio':
await bot.send_chat_action(to_tg_chat, ChatActions.UPLOAD_DOCUMENT)
tg_message = await tgsend(bot.send_audio, to_tg_chat, audio=attachment['content'],
reply_to_message_id=main_message, disable_notification=disable_notify)
Message.objects.create(
vk_chat=vk_chat_id,
vk_id=vk_msg_id,
@ -753,14 +762,41 @@ async def check_vk_url(url):
return False
async def process_attachment(attachment):
async def process_attachment(attachment, token=None):
atype = attachment.get('type')
if atype == 'photo':
photo_url = attachment[atype][await get_max_photo(attachment[atype])]
return {'content': photo_url, 'type': 'photo'}
elif atype == 'audio':
pass
if AUDIO_ACCESS_URL:
if token:
try:
with aiohttp.ClientSession() as session:
r = await session.request('GET', AUDIO_ACCESS_URL.format(token=token,
owner_id=attachment[atype]['owner_id'],
audio_id=attachment[atype]['id']))
if r.status != 200:
raise Exception
audio = await r.read()
audio = io.BytesIO(audio)
return {'content': audio, 'type': 'audio'}
except:
pass
elif AUDIO_URL:
try:
with aiohttp.ClientSession() as session:
r = await session.request('GET', AUDIO_URL.format(owner_id=attachment[atype]['owner_id'],
audio_id=attachment[atype]['id']))
if r.status != 200:
raise Exception
audio = await r.read()
audio = io.BytesIO(audio)
return {'content': audio, 'type': 'audio'}
except:
pass
return {'content': '<i>Аудио</i>', 'type': 'text'}
elif atype == 'video':
title = attachment[atype]['title']