diff --git a/README.md b/README.md
index 56b1760..2798dba 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/bot.py b/bot.py
index 6f74d11..d7a3fce 100644
--- a/bot.py
+++ b/bot.py
@@ -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
diff --git a/config.py b/config.py
index 8e14bdf..b78617c 100644
--- a/config.py
+++ b/config.py
@@ -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)'
diff --git a/docker-compose.yml b/docker-compose.yml
index cb5df37..c1d41e5 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -42,4 +42,4 @@ networks:
web_nw:
driver: bridge
volumes:
- dbdata:
\ No newline at end of file
+ dbdata:
diff --git a/set_env.py b/set_env.py
index 2f00213..9838197 100644
--- a/set_env.py
+++ b/set_env.py
@@ -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!')
diff --git a/telegram.py b/telegram.py
index 76f47cd..b965464 100644
--- a/telegram.py
+++ b/telegram.py
@@ -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('Произошла ошибка при отправке', parse_mode=ParseMode.HTML)
else:
- msg.reply('Ошибка при загрузке файла. Сообщение не отправлено!', parse_mode=ParseMode.HTML)
+ await msg.reply('Ошибка при загрузке файла. Сообщение не отправлено!', parse_mode=ParseMode.HTML)
@dp.message_handler(content_types=['document', 'voice', 'audio', 'sticker'])
diff --git a/vk_messages.py b/vk_messages.py
index 3b9a9a5..3d41939 100644
--- a/vk_messages.py
+++ b/vk_messages.py
@@ -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': 'Аудио', 'type': 'text'}
elif atype == 'video':
title = attachment[atype]['title']