from aiogram import types
from aiogram.bot.api import FILE_URL
from aiogram.utils import executor, json
from aiohttp.client_exceptions import ContentTypeError
from bot import *
from config import *
from vk_messages import vk_polling_tasks, vk_polling
log = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG)
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):
if page != 0:
leftbutton = InlineKeyboardButton('◀', callback_data='page{}'.format(page - 1)) # callback
else:
leftbutton = InlineKeyboardButton('Поиск 🔍', callback_data='search')
if page + 1 < len(pages):
rightbutton = InlineKeyboardButton('▶', callback_data='page{}'.format(page + 1))
else:
rightbutton = None
if rightbutton:
markup.row(leftbutton, rightbutton)
else:
markup.row(leftbutton)
async def logged(uid, reply_to_message_id=None, to_chat=None):
vk_user = VkUser.objects.filter(owner__uid=uid).first()
if vk_user:
return True
else:
await bot.send_message(to_chat or uid, 'Вход не выполнен! /start для входа',
reply_to_message_id=reply_to_message_id)
return False
async def update_user_info(from_user: types.User):
return TgUser.objects.update_or_create(uid=from_user.id,
defaults={'first_name': from_user.first_name,
'last_name': from_user.last_name,
'username': from_user.username
})
async def update_chat_info(from_chat: types.Chat):
if from_chat.type == 'private':
return None, False
return TgChat.objects.update_or_create(cid=from_chat.id)
async def is_forwarding(text):
if not text:
return False, None
if text == '!':
return True, None
if text.startswith('!'):
return True, text[1:]
return False, text
async def is_bot_in_iterator(msg: types.Message):
iterator = msg.new_chat_members or [msg.left_chat_member] or []
me = await bot.me
for i in iterator:
if me.id == i.id:
return True
return False
import secrets
def generate_random_id():
return secrets.randbelow(2_147_483_647)
async def vk_sender(token, tg_message, **kwargs):
session = VkSession(access_token=token, driver=await get_driver(token))
kwargs['random_id'] = generate_random_id()
try:
api = API(session)
vk_msg_id = await api('messages.send', **kwargs)
except ContentTypeError:
kwargs['v'] = session.API_VERSION
kwargs['access_token'] = session.access_token
try:
url, html = await session.driver.post_text(url=session.REQUEST_URL + 'messages.send', data=kwargs)
response = json.loads(html)
vk_msg_id = response['response']
except:
log.exception(msg='Error in vk sender', exc_info=True)
return None
except VkAuthError:
vk_user = VkUser.objects.filter(token=token).first()
if vk_user:
vk_user.delete()
await bot.send_message(tg_message.chat.id, 'Вход не выполнен! /start для входа',
reply_to_message_id=tg_message.message_id)
return
except VkAPIError:
log.exception(msg='Error in vk sender', exc_info=True)
return None
except Exception:
log.exception(msg='Error in vk sender', exc_info=True)
return None
Message.objects.create(
vk_chat=kwargs['peer_id'],
vk_id=vk_msg_id,
tg_chat=tg_message.chat.id,
tg_id=tg_message.message_id
)
return vk_msg_id
async def generate_send_options(msg, forward=None, forward_messages_exists=False, message=None):
message_options = dict()
if forward:
if msg.reply_to_message is not None:
message_in_db = Message.objects.filter(tg_chat=msg.chat.id,
tg_id=msg.reply_to_message.message_id).first()
if message_in_db and message_in_db.vk_id:
message_options['forward_messages'] = message_in_db.vk_id
message_options['peer_id'] = forward.vkchat.cid
elif msg.reply_to_message is not None:
message_in_db = Message.objects.filter(tg_chat=msg.chat.id, tg_id=msg.reply_to_message.message_id).first()
if not message_in_db:
await msg.reply('Не знаю в какой чат ответить, нет информации в базе данных.')
return message_options
if forward_messages_exists and message_in_db.vk_id:
message_options['forward_messages'] = message_in_db.vk_id
message_options['peer_id'] = message_in_db.vk_chat
else:
await msg.reply('Не понимаю что делать. Нужна помощь? Используй команду /help')
return message_options
if message:
message_options['message'] = message
return message_options
async def send_vk_action(token, peer_id, action='typing'):
vksession = VkSession(access_token=token, driver=await get_driver(token))
api = API(vksession)
return # await api('messages.setActivity', peer_id=peer_id, activity=action)
async def upload_attachment(msg, vk_user, file_id, peer_id, attachment_type, upload_field, upload_method,
on_server_field='file', save_method='', upload_type=None, default_name='tgvkbot.document',
title='tgvkbot.document', rewrite_name=False, custom_ext=''):
try:
file_info = await bot.get_file(file_id)
path = file_info['file_path']
if msg.content_type == 'audio':
if not custom_ext and '.' in path and path.split('.')[-1] == 'mp3':
custom_ext = '.aac'
except NetworkError:
await msg.reply('Файл слишком большой, максимально допустимый размер 20мб!', parse_mode=ParseMode.HTML)
return
url = FILE_URL.format(token=bot._BaseBot__token, path=path)
await send_vk_action(vk_user.token, peer_id)
content = await get_content(url, default_name, chrome_headers=False, rewrite_name=rewrite_name,
custom_ext=custom_ext)
filename = (content.get('file_name', '') + content.get('custom_ext', '')) or None
if 'content' in content:
vksession = VkSession(access_token=vk_user.token, driver=await get_driver(vk_user.token))
api = API(vksession)
upload_options = {}
if attachment_type != 'photo' and upload_type:
upload_options['type'] = upload_type
if msg.content_type == 'sticker':
webp = Image.open(content['content']).convert('RGBA')
png = io.BytesIO()
webp.save(png, format='png')
content['content'] = png.getvalue()
if attachment_type == 'video':
upload_options['is_private'] = 1
upload_server = await api(upload_method, **upload_options)
with aiohttp.ClientSession() as session:
data = aiohttp.FormData()
field_data = {}
if filename:
field_data['filename'] = filename
data.add_field(upload_field, content['content'], content_type='multipart/form-data', **field_data)
async with session.post(upload_server['upload_url'], data=data) as upload:
file_on_server = json.loads(await upload.text())
if msg.content_type != 'sticker':
content['content'].close()
try:
os.remove(os.path.join(content['temp_path'], content['file_name'] + content['custom_ext']))
except:
pass
if attachment_type == 'photo':
save_options = {'server': file_on_server['server'], on_server_field: file_on_server[on_server_field],
'hash': file_on_server['hash']}
elif attachment_type == 'video':
return f'{attachment_type}{upload_server["owner_id"]}_{upload_server["video_id"]}_{upload_server["access_key"]}'
else:
if 'file' not in file_on_server:
await msg.reply('Ошибка Не удалось загрузить файл. Файл не должен быть исполняемым.',
parse_mode=ParseMode.HTML)
return
save_options = dict({'file': file_on_server['file']})
save_options['title'] = title
attachment = await api(save_method, **save_options)
if 'type' not in attachment:
attachment = attachment[0]
else:
attachment = attachment[attachment['type']]
return f'{attachment_type}{attachment["owner_id"]}_{attachment["id"]}'
async def get_dialogs(token, exclude=None):
if not exclude:
exclude = []
session = VkSession(access_token=token, driver=await get_driver(token))
api = API(session)
dialogs = await api('messages.getDialogs', count=200)
order = []
users_ids = []
group_ids = []
for chat in dialogs.get('items'):
chat = chat.get('message', '')
if chat:
if 'chat_id' in chat:
if 2000000000 + chat['chat_id'] not in exclude:
chat['title'] = chat['title']
order.append({'title': chat['title'], 'id': 2000000000 + chat['chat_id']})
elif chat['user_id'] > 0:
if chat['user_id'] not in exclude:
order.append({'title': 'Диалог ' + str(chat['user_id']), 'id': chat['user_id']})
users_ids.append(chat['user_id'])
elif chat['user_id'] < 0:
if chat['user_id'] not in exclude:
order.append({'title': 'Диалог ' + str(chat['user_id']), 'id': chat['user_id']})
group_ids.append(chat['user_id'])
if users_ids:
users = await api('users.get', user_ids=', '.join(str(x) for x in users_ids))
else:
users = []
if group_ids:
groups = await api('groups.getById', group_ids=', '.join(str(abs(x)) for x in group_ids))
else:
groups = []
for output in order:
if output['id'] > 0:
u = next((i for i in users if i['id'] == output['id']), None)
if u:
output['title'] = f'{u["first_name"]} {u["last_name"]}'
else:
g = next((i for i in groups if -i['id'] == output['id']), None)
if g:
output['title'] = g["name"]
for button in range(len(order)):
order[button] = InlineKeyboardButton(order[button]['title'], callback_data=f'chat{order[button]["id"]}')
rows = [order[x:x + 2] for x in range(0, len(order), 2)]
pages = [rows[x:x + 4] for x in range(0, len(rows), 4)]
return pages
async def search_dialogs(msg: types.Message, user=None):
if not user:
user, created = await update_user_info(msg.from_user)
vkuser = VkUser.objects.filter(owner=user).first()
vksession = VkSession(access_token=vkuser.token, driver=await get_driver(vkuser.token))
api = API(vksession)
markup = InlineKeyboardMarkup(row_width=1)
await bot.send_chat_action(msg.chat.id, 'typing')
result = await api('messages.searchDialogs', q=msg.text, limit=10)
for chat in result:
title = None
data = None
if chat['type'] == 'profile':
title = f'{chat["first_name"]} {chat["last_name"]}'
data = f'chat{chat["id"]}'
elif chat['type'] == 'chat':
title = chat['title']
data = f'chat{2000000000 + chat["id"]}'
elif chat['type'] == 'page':
title = await chat['name']
data = f'chat{-chat["id"]}'
if title and data:
markup.add(InlineKeyboardButton(text=title, callback_data=data))
markup.add(InlineKeyboardButton('Поиск 🔍', callback_data='search'))
if markup.inline_keyboard:
text = f'Результат поиска по {msg.text}'
else:
text = f'Результат поиска по {msg.text}'
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()
if vkuser:
await handle_join(call.message, edit=True, chat_id=call.message.chat.id, message_id=call.message.message_id,
exclude=True)
else:
await bot.answer_callback_query(call.id, 'Вход не выполнен! Сперва нужно выполнить вход в ВК через бота',
show_alert=True)
@dp.callback_query_handler(func=lambda call: call and call.message and call.data and call.data.startswith('page'))
async def page_switcher(call: types.CallbackQuery):
# user, created = await update_user_info(call.from_user)
# tgchat, tgchat_created = await update_chat_info(call.message.chat)
page = int(call.data.split('page')[-1])
message_markup = MessageMarkup.objects.filter(
chat_id=call.message.chat.id,
message_id=call.message.message_id,
).first()
if message_markup:
pages = json.loads(message_markup.buttons)
markup = InlineKeyboardMarkup()
for row in pages[page]:
markup.row(*[InlineKeyboardButton(**button) for button in row])
await get_pages_switcher(markup, page, pages)
await bot.edit_message_reply_markup(call.message.chat.id, call.message.message_id, reply_markup=markup)
await bot.answer_callback_query(call.id)
else:
await bot.answer_callback_query(call.id, 'Нет данных в Базе Данных', show_alert=True)
async def get_dialog_info(api, vk_chat_id, name_case='nom'):
title = ''
photo = ''
dialog_type = ''
if vk_chat_id >= 2000000000:
dialog_info = await api('messages.getChat', chat_id=vk_chat_id - 2000000000)
title = dialog_info['title']
max_photo = await get_max_photo(dialog_info)
if max_photo:
photo = dialog_info[max_photo]
else:
photo = None
dialog_type = 'chat'
elif vk_chat_id > 0:
dialog_info = await api('users.get', user_ids=vk_chat_id, fields='photo_max', name_case=name_case)
first_name = dialog_info[0]['first_name']
last_name = dialog_info[0]['last_name'] or ''
title = first_name + ' ' + last_name
photo = dialog_info[0]['photo_max']
dialog_type = 'user'
elif vk_chat_id < 0:
dialog_info = await api('groups.getById', group_ids=abs(vk_chat_id))
title = dialog_info[0]['name']
max_photo = await get_max_photo(dialog_info[0])
if max_photo:
photo = dialog_info[0][max_photo]
else:
photo = None
dialog_type = 'group'
return {'title': title, 'photo': photo, 'type': dialog_type}
@dp.callback_query_handler(func=lambda call: call and call.message and call.data and call.data.startswith('ping'))
async def ping_button(call: types.CallbackQuery):
tg_chat_id = int(call.data.split('ping')[-1])
try:
await bot.send_message(tg_chat_id, f'Ping!',
parse_mode=ParseMode.HTML)
await bot.answer_callback_query(call.id, 'Ping!')
except BadRequest:
await bot.answer_callback_query(call.id, 'Нет доступа к чату, бот кикнут или чат удалён!', show_alert=True)
@dp.callback_query_handler(
func=lambda call: call and call.message and call.data and call.data.startswith('deleteforward'))
async def delete_forward(call: types.CallbackQuery):
forward_id = int(call.data.split('deleteforward')[-1])
forward_in_db = Forward.objects.filter(id=forward_id).first()
if forward_in_db:
forward_in_db.delete()
markup = InlineKeyboardMarkup()
message_markup = MessageMarkup.objects.filter(
message_id=call.message.message_id,
chat_id=call.message.chat.id
).first()
buttons = json.loads(message_markup.buttons)
for row in buttons:
if row[1]['callback_data'] == call.data:
buttons.remove(row)
else:
markup.row(*[InlineKeyboardButton(**button) for button in row])
if message_markup:
if buttons:
message_markup.buttons = json.dumps(buttons)
message_markup.save()
await bot.edit_message_reply_markup(call.message.chat.id, call.message.message_id, reply_markup=markup)
else:
await bot.edit_message_text(
'У Вас нет связанных чатов. Чтобы привязать чат, добавьте бота в группу, а если бот уже добавлен - используйте команду /dialogs',
call.message.chat.id, call.message.message_id)
await bot.answer_callback_query(call.id, 'Успешно удалено!')
else:
await bot.edit_message_text('Что-то пошло не так, нет информации в Базе Данных',
message_id=call.message.message_id, chat_id=call.message.chat.id, reply_markup=None)
await bot.answer_callback_query(call.id, 'Ошибка!')
@dp.callback_query_handler(func=lambda call: call and call.message and call.data and call.data.startswith('setinfo'))
async def set_info(call: types.CallbackQuery):
user, created = await update_user_info(call.from_user)
tgchat, tgchat_created = await update_chat_info(call.message.chat)
vk_chat_id = int(call.data.split('setinfo')[-1])
vkuser = VkUser.objects.filter(owner=user).first()
if vkuser:
ME = await bot.me
can_edit = False
if not call.message.chat.all_members_are_administrators and (
(await bot.get_chat_member(call.message.chat.id, ME.id)).status == 'administrator'):
can_edit = True
if not can_edit:
admins = await bot.get_chat_administrators(call.message.chat.id)
for admin in admins:
if admin.user.id == ME.id and admin.can_change_info:
can_edit = True
break
if can_edit:
vksession = VkSession(access_token=vkuser.token, driver=await get_driver(vkuser.token))
api = API(vksession)
dialog_info = await get_dialog_info(api, vk_chat_id, name_case='nom')
if dialog_info.get('title', ''):
await bot.set_chat_title(call.message.chat.id, dialog_info['title'])
if dialog_info.get('photo', ''):
content = await get_content(dialog_info['photo'])
await bot.set_chat_photo(call.message.chat.id, content['content'])
content['content'].close()
try:
os.remove(os.path.join(content['temp_path'], content['file_name'] + content['custom_ext']))
except:
pass
if dialog_info['type'] == 'user':
dialog_info = await get_dialog_info(api, vk_chat_id, name_case='ins')
text = f'Чат успешно привязан к диалогу c {dialog_info["title"]}'
elif dialog_info['type'] == 'group':
text = f'Чат успешно привязан к диалогу с сообществом {dialog_info["title"]}'
else:
text = f'Чат успешно привязан к диалогу {dialog_info["title"]}'
await bot.edit_message_text(text, call.message.chat.id, call.message.message_id, parse_mode=ParseMode.HTML)
await bot.answer_callback_query(call.id)
else:
await bot.answer_callback_query(call.id,
'Недостаточно прав для редактирования информации о группе или бот не администратор!',
show_alert=True)
else:
await bot.answer_callback_query(call.id, 'Вход не выполнен! Сперва нужно выполнить вход в ВК через бота',
show_alert=True)
@dp.callback_query_handler(func=lambda call: call and call.message and call.data and call.data.startswith('chat'))
async def choose_chat(call: types.CallbackQuery):
user, created = await update_user_info(call.from_user)
tgchat, tgchat_created = await update_chat_info(call.message.chat)
vk_chat_id = int(call.data.split('chat')[-1])
vkuser = VkUser.objects.filter(owner=user).first()
if vkuser:
if call.message.chat.type == 'private':
vksession = VkSession(access_token=vkuser.token, driver=await get_driver(vkuser.token))
api = API(vksession)
dialog_info = await get_dialog_info(api, vk_chat_id, name_case='gen')
markup = types.ForceReply(selective=False)
if dialog_info['type'] == 'user':
text = f'Сообщение для {dialog_info["title"]}'
elif dialog_info['type'] == 'group':
text = f'Сообщение сообществу {dialog_info["title"]}'
else:
text = f'Сообщение в диалог {dialog_info["title"]}'
tg_message = await bot.send_message(call.message.chat.id, text, reply_markup=markup,
parse_mode=ParseMode.HTML)
Message.objects.create(
tg_chat=tg_message.chat.id,
tg_id=tg_message.message_id,
vk_chat=vk_chat_id
)
await bot.answer_callback_query(call.id)
else:
forward = Forward.objects.filter(tgchat=tgchat).first()
vkchat = (await get_vk_chat(int(vk_chat_id)))[0]
if forward:
forward.vkchat = vkchat
forward.save()
else:
Forward.objects.create(
tgchat=tgchat,
vkchat=vkchat,
owner=user
)
markup = InlineKeyboardMarkup()
markup.add(InlineKeyboardButton('Установить аватар и название', callback_data=f'setinfo{vkchat.cid}'))
text = 'Чат успешно привязан. Я могу автоматически изменить название и установить аватар, сделай бота администратором и убедись в наличии прав на редактирование информации группы'
if call.message.chat.type == 'group':
text += '\nВнимание! Параметр "All Members Are Administrators" должен быть отключён и боту должна быть присвоена админка в отдельном порядке!'
try:
await bot.edit_message_text(text, call.message.chat.id, call.message.message_id, reply_markup=markup,
parse_mode=ParseMode.HTML)
except MessageNotModified:
pass
await bot.answer_callback_query(call.id)
else:
await bot.answer_callback_query(call.id, 'Вход не выполнен! Сперва нужно выполнить вход в ВК через бота',
show_alert=True)
@dp.callback_query_handler(func=lambda call: call and call.message and call.data and call.data == 'search')
async def search_callback(call: types.CallbackQuery):
vkuser = VkUser.objects.filter(owner__uid=call.from_user.id).count()
if vkuser:
markup = types.ForceReply(selective=False)
await bot.send_message(call.message.chat.id, 'Поиск беседы 🔍', parse_mode=ParseMode.HTML,
reply_markup=markup)
await bot.answer_callback_query(call.id, 'Поиск беседы 🔍')
else:
await bot.answer_callback_query(call.id, 'Вход не выполнен! Сперва нужно выполнить вход в ВК через бота',
show_alert=True)
@dp.message_handler(commands=['start'])
async def send_welcome(msg: types.Message):
user, created = await update_user_info(msg.from_user)
tgchat, tgchat_created = await update_chat_info(msg.chat)
if not tgchat:
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,audio' \
'&response_type=token&v={}'.format(VK_APP_ID, API_VERSION)
mark = InlineKeyboardMarkup()
login = InlineKeyboardButton('ВХОД', url=link)
mark.add(login)
await msg.reply('Привет, этот бот поможет тебе общаться ВКонтакте, войди по кнопке ниже'
' и отправь мне то, что получишь в адресной строке.',
reply_markup=mark)
else:
await msg.reply('Вход уже выполнен!\n/stop для выхода.')
else:
markup = InlineKeyboardMarkup()
me = await bot.me
markup.add(InlineKeyboardButton('Перейти в бота', url=f'https://t.me/{me.username}?start=login'))
await msg.reply('Залогиниться можно только через личный чат с ботом', reply_markup=markup)
@dp.message_handler(commands=['stop'])
async def stop_command(msg: types.Message):
user, created = await update_user_info(msg.from_user)
tgchat, tgchat_created = await update_chat_info(msg.chat)
existing_vkuser = VkUser.objects.filter(owner=user).first()
if not existing_vkuser:
await msg.reply('Вход не выполнен! Используй команду /start для входа')
else:
polling = next((task for task in TASKS if task['token'] == existing_vkuser.token), None)
if polling:
polling['task'].cancel()
driver = DRIVERS.get(existing_vkuser.token, '')
if driver:
driver.close()
del DRIVERS[existing_vkuser.token]
existing_vkuser.delete()
await msg.reply('Успешный выход!')
@dp.message_handler(commands=['dialogs', 'd'])
async def dialogs_command(msg: types.Message):
if msg.chat.type == 'private':
await handle_join(msg, text='Выберите диалог для быстрого ответа')
else:
await handle_join(msg, exclude=True)
@dp.message_handler(commands=['read', 'r'])
async def read_command(msg: types.Message):
user, created = await update_user_info(msg.from_user)
tgchat, tgchat_created = await update_chat_info(msg.chat)
if await logged(msg.from_user.id, msg.message_id, msg.chat.id):
vk_user = VkUser.objects.filter(owner=user).first()
if msg.chat.type == 'private':
if msg.reply_to_message:
message_in_db = Message.objects.filter(tg_chat=msg.chat.id,
tg_id=msg.reply_to_message.message_id).first()
if message_in_db:
vksession = VkSession(access_token=vk_user.token, driver=await get_driver(vk_user.token))
api = API(vksession)
await api('messages.markAsRead', peer_id=message_in_db.vk_chat)
await bot.send_message(msg.chat.id, 'Диалог прочитан', parse_mode=ParseMode.HTML)
else:
await msg.reply('Не знаю какой чат прочесть, нет информации в базе данных')
else:
forward = Forward.objects.filter(tgchat=tgchat).first()
if forward:
vksession = VkSession(access_token=vk_user.token, driver=await get_driver(vk_user.token))
api = API(vksession)
await api('messages.markAsRead', peer_id=forward.vkchat.cid)
await bot.send_message(msg.chat.id, 'Диалог прочитан', parse_mode=ParseMode.HTML)
else:
await msg.reply('Этот чат не привязан к диалогу ВКонтакте, для привязки используй команду /dialogs')
@dp.message_handler(commands=['search', 's'])
async def search_command(msg: types.Message):
user, created = await update_user_info(msg.from_user)
vkuser = VkUser.objects.filter(owner=user).count()
if vkuser:
markup = types.ForceReply(selective=False)
await bot.send_message(msg.chat.id, 'Поиск беседы 🔍', parse_mode=ParseMode.HTML,
reply_markup=markup)
else:
await bot.answer_callback_query(msg, 'Вход не выполнен! Сперва нужно выполнить вход в ВК через бота',
show_alert=True)
@dp.message_handler(commands=['chat', 'chats'])
async def chat_command(msg: types.Message):
user, created = await update_user_info(msg.from_user)
tgchat, tgchat_created = await update_chat_info(msg.chat)
forwards = Forward.objects.filter(owner=user)
if await logged(msg.from_user.id, msg.message_id, msg.chat.id):
if forwards:
vk_user = VkUser.objects.filter(owner=user).first()
vksession = VkSession(access_token=vk_user.token, driver=await get_driver(vk_user.token))
api = API(vksession)
markup = InlineKeyboardMarkup()
for forward in forwards:
chat = await get_dialog_info(api, forward.vkchat.cid)
markup.row(*[InlineKeyboardButton(chat['title'], callback_data=f'ping{forward.tgchat.cid}'),
InlineKeyboardButton('❌', callback_data=f'deleteforward{forward.pk}')])
msg_with_markup = await bot.send_message(msg.chat.id,
'Список привязанных диалогов\nНажав на имя диалога, бот пинганёт Вас в соответствующем чате Telegram.\nНажав на "❌", привязка чата будет удалена и все сообщение из диалога ВКонтакте будут попадать напрямую к боту',
reply_markup=markup)
for row in markup.inline_keyboard:
for button in range(len(row)):
row[button] = row[button].to_python()
MessageMarkup.objects.create(
message_id=msg_with_markup.message_id,
chat_id=msg_with_markup.chat.id,
buttons=json.dumps(markup.inline_keyboard)
)
else:
await bot.send_message(msg.chat.id,
'У Вас нет связанных чатов. Чтобы привязать чат, добавьте бота в группу, а если бот уже добавлен - используйте команду /dialogs')
@dp.message_handler(commands=['help'])
async def help_command(msg: types.Message):
user, created = await update_user_info(msg.from_user)
tgchat, tgchat_created = await update_chat_info(msg.chat)
HELP_MESSAGE = '/start - Логин в Вконтакте\n' \
'/dialogs /d - Список диалогов\n' \
'/read /r - Прочесть диалог ВКонтакте\n' \
'/search /s - Поиск по диалогам\n' \
'/chat - Список связанных чатов с диалогами ВКонтакте, привязать чат к диалогу можно добавив бота в группу\n' \
'/stop - Выход из ВКонтакте\n' \
'/help - Помощь'
await bot.send_message(msg.chat.id, HELP_MESSAGE, parse_mode=ParseMode.HTML)
@dp.message_handler(content_types=['text'])
async def handle_text(msg: types.Message):
user, created = await update_user_info(msg.from_user)
tgchat, tgchat_created = await update_chat_info(msg.chat)
if msg.chat.type == 'private':
m = oauth_link.search(msg.text)
if m:
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))
api = API(session)
vkuserinfo = await api('account.getProfileInfo', name_case='gen')
vkuser, vkuser_created = VkUser.objects.update_or_create(
defaults={'token': token, 'is_polling': True}, owner=user)
existing_polling = next((task for task in TASKS if task['token'] == vkuser.token), None)
if existing_polling:
existing_polling['task'].cancel()
driver = DRIVERS.get(vkuser.token, '')
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))})
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:
await msg.reply('Вход уже выполнен!\n/stop для выхода.')
return
if await logged(msg.from_user.id, msg.message_id, msg.chat.id):
if msg.reply_to_message and msg.reply_to_message.text == 'Поиск беседы 🔍':
if msg.chat.type == 'private' or not Message.objects.filter(tg_id=msg.reply_to_message.message_id,
tg_chat=msg.reply_to_message.chat.id).exists():
await search_dialogs(msg, user)
return
vk_user = VkUser.objects.filter(owner=user).first()
forward = Forward.objects.filter(tgchat=tgchat).first()
forward_messages_exists, message = await is_forwarding(msg.text)
message_options = await generate_send_options(msg, forward, forward_messages_exists, message)
if message_options != {}:
vk_message = await vk_sender(vk_user.token, msg, **message_options)
if not vk_message:
await msg.reply('Произошла ошибка при отправке', parse_mode=ParseMode.HTML)
@dp.message_handler(content_types=['contact'])
async def handle_contact(msg: types.Message):
new_text = msg.contact.first_name
if msg.contact.last_name:
new_text += ' ' + msg.contact.last_name
new_text += '\n'
new_text += msg.contact.phone_number
msg.text = new_text
await handle_text(msg)
@dp.message_handler(content_types=['photo', 'sticker'])
async def handle_photo(msg: types.Message):
user, user_created = await update_user_info(msg.from_user)
tgchat, tgchat_created = await update_chat_info(msg.chat)
if await logged(msg.from_user.id, msg.message_id, msg.chat.id):
vk_user = VkUser.objects.filter(owner=user).first()
forward = Forward.objects.filter(tgchat=tgchat).first()
forward_messages_exists, message = await is_forwarding(msg.caption)
message_options = await generate_send_options(msg, forward, forward_messages_exists, message)
if msg.content_type == 'photo':
file_id = msg.photo[-1].file_id
elif msg.content_type == 'sticker':
if msg.sticker.to_python()['is_animated']:
file_id = msg.sticker.thumb.file_id
else:
file_id = msg.sticker.file_id
if message_options:
message_options['attachment'] = await upload_attachment(msg, vk_user, file_id, message_options['peer_id'],
attachment_type='photo',
upload_field='photo',
upload_method='photos.getMessagesUploadServer',
on_server_field='photo',
save_method='photos.saveMessagesPhoto')
if message_options['attachment']:
vk_message = await vk_sender(vk_user.token, msg, **message_options)
if not vk_message:
await msg.reply('Произошла ошибка при отправке', parse_mode=ParseMode.HTML)
else:
await msg.reply('Ошибка при загрузке файла. Сообщение не отправлено!', parse_mode=ParseMode.HTML)
@dp.message_handler(content_types=['document', 'voice', 'audio'])
async def handle_documents(msg: types.Message):
user, user_created = await update_user_info(msg.from_user)
tgchat, tgchat_created = await update_chat_info(msg.chat)
if await logged(msg.from_user.id, msg.message_id, msg.chat.id):
vk_user = VkUser.objects.filter(owner=user).first()
if tgchat:
forward = Forward.objects.filter(tgchat=tgchat).first()
else:
forward = None
forward_messages_exists, message = await is_forwarding(msg.caption)
message_options = await generate_send_options(msg, forward, forward_messages_exists, message)
file_id = getattr(msg, msg.content_type).file_id
if message_options:
upload_attachment_options = {
'attachment_type': 'doc',
'upload_field': 'file',
'upload_method': 'docs.getUploadServer',
'on_server_field': 'file',
'save_method': 'docs.save',
}
if hasattr(getattr(msg, msg.content_type), 'file_name') and getattr(msg, msg.content_type).file_name:
upload_attachment_options['title'] = getattr(msg, msg.content_type).file_name
if msg.content_type == 'voice':
upload_attachment_options['upload_type'] = 'audio_message'
# https://vk.com/wall-1_395554
# if msg.content_type == 'sticker':
# if msg.sticker.to_python()['is_animated']:
# file_id = msg.sticker.thumb.file_id
# upload_attachment_options['upload_type'] = 'graffiti'
# upload_attachment_options['rewrite_name'] = True
# upload_attachment_options['default_name'] = 'graffiti.png'
if msg.content_type == 'audio':
audioname = ''
if msg.audio.performer and msg.audio.title:
audioname += msg.audio.performer + ' - ' + msg.audio.title
elif msg.audio.performer:
audioname += msg.audio.performer
elif msg.audio.title:
audioname += msg.audio.title
else:
audioname = f'tgvkbot_audio_{file_id}'
upload_attachment_options['title'] = audioname
message_options['attachment'] = await upload_attachment(msg, vk_user, file_id, message_options['peer_id'],
**upload_attachment_options)
if message_options['attachment']:
vk_message = await vk_sender(vk_user.token, msg, **message_options)
if not vk_message:
await msg.reply('Произошла ошибка при отправке', parse_mode=ParseMode.HTML)
else:
await msg.reply('Ошибка при загрузке файла. Сообщение не отправлено!', parse_mode=ParseMode.HTML)
@dp.message_handler(content_types=['video', 'video_note'])
async def handle_videos(msg: types.Message):
user, user_created = await update_user_info(msg.from_user)
tgchat, tgchat_created = await update_chat_info(msg.chat)
if await logged(msg.from_user.id, msg.message_id, msg.chat.id):
vk_user = VkUser.objects.filter(owner=user).first()
if tgchat:
forward = Forward.objects.filter(tgchat=tgchat).first()
else:
forward = None
forward_messages_exists, message = await is_forwarding(msg.caption)
message_options = await generate_send_options(msg, forward, forward_messages_exists, message)
file_id = getattr(msg, msg.content_type).file_id
if message_options:
upload_attachment_options = {
'attachment_type': 'video',
'upload_field': 'video_file',
'upload_method': 'video.save',
}
if hasattr(getattr(msg, msg.content_type), 'file_name') and getattr(msg, msg.content_type).file_name:
upload_attachment_options['title'] = getattr(msg, msg.content_type).file_name
message_options['attachment'] = await upload_attachment(msg, vk_user, file_id, message_options['peer_id'],
**upload_attachment_options)
if message_options['attachment']:
vk_message = await vk_sender(vk_user.token, msg, **message_options)
if not vk_message:
await msg.reply('Произошла ошибка при отправке', parse_mode=ParseMode.HTML)
else:
await msg.reply('Ошибка при загрузке файла. Сообщение не отправлено!', parse_mode=ParseMode.HTML)
@dp.message_handler(content_types=['new_chat_members'], func=is_bot_in_iterator)
async def handle_join(msg: types.Message, edit=False, chat_id=None, message_id=None, text='', exclude=False):
user, user_created = await update_user_info(msg.from_user)
tgchat, tgchat_created = await update_chat_info(msg.chat)
forward = Forward.objects.filter(tgchat=tgchat).first()
try:
await bot.send_chat_action(msg.chat.id, 'typing')
except:
return
vk_user = VkUser.objects.filter(owner=user).first()
pages = None
reply_to_message_id = None
markup = None
if vk_user:
if forward:
text = text or 'Этот чат уже привязан к диалогу ВКонтакте, Вы можете выбрать новый диалог'
else:
text = text or 'Выберите диалог ВКонтакте к которому будет привязан этот чат'
markup = InlineKeyboardMarkup()
excluded_ids = []
if exclude:
excluded_ids = [forward.vkchat.cid for forward in Forward.objects.filter(owner=user)]
pages = await get_dialogs(vk_user.token, excluded_ids)
if pages:
for buttons_row in pages[0]:
markup.row(*buttons_row)
await get_pages_switcher(markup, 0, pages)
else:
me = await bot.me
if msg.chat.type == 'private':
text = 'Вход не выполнен! /start для входа'
reply_to_message_id = msg.message_id
else:
text = 'Вход не выполнен! Сперва нужно выполнить вход в ВК через бота'
markup = InlineKeyboardMarkup()
markup.add(InlineKeyboardButton('ВХОД', url=f'https://t.me/{me.username}?start=login'),
InlineKeyboardButton('✅ Я залогинился', callback_data=f'logged-{msg.from_user.id}'))
if edit:
msg_with_markup = await bot.edit_message_text(text=text, chat_id=chat_id, message_id=message_id,
reply_markup=markup, parse_mode=ParseMode.HTML)
else:
msg_with_markup = await bot.send_message(msg.chat.id, text=text, reply_markup=markup, parse_mode=ParseMode.HTML,
reply_to_message_id=reply_to_message_id)
if pages:
for page in pages:
for row in page:
for button in range(len(row)):
row[button] = row[button].to_python()
MessageMarkup.objects.create(
message_id=msg_with_markup.message_id,
chat_id=msg_with_markup.chat.id,
buttons=json.dumps(pages)
)
@dp.message_handler(content_types=types.ContentType.ANY, func=lambda msg: msg.group_chat_created is True)
async def handle_new_group(msg: types.Message):
await handle_join(msg)
@dp.message_handler(content_types=types.ContentType.ANY, func=lambda message: message.migrate_to_chat_id)
async def handle_chat_migration(msg: types.Message):
# Юзеру сначала нужно выбрать чат для привязки, а уже ПОТОМ делать миграцию. Иначе старый chatid останется на иналйнкнопках
forwards = Forward.objects.filter(tgchat__cid=msg.chat.id)
for forward in forwards:
forward.tgchat.cid = msg.migrate_to_chat_id
forward.tgchat.save()
markup = InlineKeyboardMarkup()
markup.add(InlineKeyboardButton('Установить аватар и название', callback_data=f'setinfo{forward.vkchat.cid}'))
await bot.send_message(msg.migrate_to_chat_id,
text='Отлично! Теперь можно установить аватар и название. Если кнопка выше не работает, то воспользуйтесь этой.',
reply_markup=markup)
if __name__ == '__main__':
TASKS = vk_polling_tasks()
asyncio.gather(*[task['task'] for task in TASKS])
executor.start_polling(dp)