diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2f5bdf0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +* text=auto eol=lf +*.db binary diff --git a/code/bot.py b/code/bot.py index f953e9a..bc006c3 100644 --- a/code/bot.py +++ b/code/bot.py @@ -1,306 +1,69 @@ -import logging, config, asyncio, time, sql, random - -from aiogram import Bot, Dispatcher, executor, types -from emoji import emojize - -from vk_scripts import VkParser -from scripts import login, pars_post - -# log level -logging.basicConfig(level=logging.INFO) - -# bot init -bot = Bot(token=config.botToken) -dp = Dispatcher(bot) - -# connection db -du = sql.Users('../db/users.db') -db = sql.Base('../db/base.db') - - -# events -@dp.message_handler(commands=["start", "help"]) -async def start(message: types.Message): - login(message.chat.id, du, db) - - buttons = [types.InlineKeyboardButton(text="КОМАНДЫ", callback_data="com"), - types.InlineKeyboardButton(text="АВТОР", callback_data="auth")] - - keyboard = types.InlineKeyboardMarkup(row_width=2) - keyboard.add(*buttons) - - await message.answer(text=f'*Portal in VK* - это бот для перепоста постов со страниц в социальной сети ВКонтакте. ' - f'Для начала работы вам нужно всего лишь вызвать команду */add* и добавить к ней короткое ' - f'имя личной или публичной страницы. После этого, бот начнёт присылать каждый новый пост ' - f'с этой страницы форматируя данные с неё под сообщение в Telegram. С полным функционалом ' - f'бота, вы можете ознакомиться по кнопке *КОМАНДЫ*\n\n' - - f'Если вы хотите ставить лайки или получать уведомления от закрытых страниц на которые ' - f'подписаны в социальной сети, вам необходимо скинуть ссылку из поисковой строки в чат, ' - f'после подтверждения на [сайте]({config.loginUrl})', - parse_mode=types.ParseMode.MARKDOWN, reply_markup=keyboard) - - -# ИЛАЙН КЛАВИАТУРА HELP -@dp.callback_query_handler(text="com") -async def function(call: types.CallbackQuery): - await call.message.answer(text='*| КОМАНДЫ |*\n\n' - f'*/like* - лайк на пост, который вы отметили\n' - f'*/list* - список страниц от, которых вы получаете уведомления\n' - f'*/notif* - отписка или подписка от всех уведомлений\n' - f'*/last_post* - получение последнего поста по короткому имени страницы\n' - f'*/add* и */del* - добавление и удаление странички из списка подписок\n', - parse_mode=types.ParseMode.MARKDOWN) - - -@dp.callback_query_handler(text="auth") -async def author(call: types.CallbackQuery): - await call.message.answer(text='*| АВТОР |*\n\n*>>* Этот бот не коммерческий проект, для получения постов из ' - 'социальной сети ВКонтакте. Бот работает на сервисном токене VK API, пока вы не ' - 'предоставите собственный. Ваш токен нужен для получения постов со страниц, ' - 'доступ к которым есть исключительно у вас. Также это даст вам возможность ставить ' - 'через Telegram лайки на посты в самой социальной сети!' - - '\n\nЯ же пишу подобные небольшие проекты, о которых вы можете узнать ' - 'больше на моём [GitHub](https://github.com/IGlek).', - parse_mode=types.ParseMode.MARKDOWN) - - -# КОМАНДЫ -@dp.message_handler(commands=["notif"]) -async def notification(message: types.Message): - user_id = login(message.chat.id, du, db) - status = db.get_status(user_id) - - groups = list(map(int, db.get_user_groups(user_id).split(";"))) - for group in groups: db.update_countGroup(group, -1 if status else 1) - - if status: await message.answer("Получение постов из ВК выключено!") - else: await message.answer("Получение постов из ВК включено!") - db.update_status(user_id) - - -@dp.message_handler(commands=["add", "del"]) -async def add_del(message: types.Message): - user_id = login(message.chat.id, du, db) - domain = message.get_args() - - if domain: - try: - token = db.get_token(user_id) - if token: vk = VkParser(token) - else: vk = VkParser(config.serviceToken) - - group_id, typ, last_post = vk.login(domain) - - if not du.group_exists(group_id): - du.add_group(group_id) - group_id = du.get_group_id(group_id) - - if not db.infoGroup_exists(group_id): - db.add_infoGroup(group_id, typ, last_post) - - groups = db.get_user_groups(user_id) - user_groups = groups.split(";") if groups else [] - - if str(group_id) not in user_groups: - if message.get_command() == "/del": - await message.answer("Вы и так не подписаны на эту группу!") - else: - if db.get_countUser(user_id) < 10: - user_groups.append(str(group_id)) - db.update_user_groups(user_id, ";".join(user_groups)) - - db.update_countUser(user_id, 1) - if db.get_status(user_id): db.update_countGroup(group_id, 1) - - await message.answer("Группа успешно добавлена в список уведомлений!") - else: - await message.answer("У вас добавлено максимально количество групп!") - else: - if message.get_command() == "/add": - await message.answer("Вы уже подписаны на эту страницу!") - else: - user_groups.remove(str(group_id)) - db.update_user_groups(user_id, ";".join(user_groups)) - - db.update_countUser(user_id, -1) - if db.get_status(user_id): db.update_countGroup(group_id, -1) - - await message.answer("Группа успешно удалена из списка уведомлений!") - except Exception as e: - await message.answer("Произошла ошибка! Проверьте правильность написания короткого адреса страницы " - "и имеете ли вы доступ к этой странице!") - else: - if message.get_command() == "/add": - await message.answer("Команда введена некорректно, такой страницы не существует или она закрыта для " - "просмотра!\n\nКорректный ввод команд: /add example", types.ParseMode.HTML) - elif message.get_command() == "/del": - await message.answer("Команда введена некорректно, такой страницы не существует или она закрыта для " - "просмотра!\n\nКорректный ввод команд: /del example", types.ParseMode.HTML) - - -@dp.message_handler(commands=["list"]) -async def lst(message: types.Message): - user_id = login(message.chat.id, du, db) - groups = db.get_user_groups(user_id) - user_groups = list(map(int, groups.split(";") if groups else [])) - - if user_groups: - group_ids = [du.get_vk_id(id) for id in user_groups] - - vk = VkParser(config.serviceToken) - peoples, groups = vk.info(group_ids) - - text = "* Список страниц, от которых вы получаете уведомления!\n" - - if peoples: - text += "\nЛичные страницы пользователей\n" - for i, people in enumerate(peoples): - text += f'{emojize(":green_circle:") if people[2] else emojize(":red_circle:")} ' \ - f'{i + 1}. {people[1]} @{people[0]}\n' - - if groups: - text += "\nГруппы / сообщества / паблики\n" - for i, group in enumerate(groups): - text += f'{i + 1}. {group[1]} @{group[0]}\n' - - await message.answer(text, types.ParseMode.HTML) - else: - await message.answer("Вы не добавили ни одной страницы для отслеживания публикаций!") - - -@dp.message_handler(commands=["like"]) -async def like(message: types.Message): - user_id = login(message.chat.id, du, db) - token = db.get_token(user_id) - - if token and 'reply_to_message' in message: - url = '' - - if 'entities' in message.reply_to_message: - for i in message.reply_to_message.entities: - if i['type'] == 'text_link': url = i['url']; break - elif 'caption_entities' in message.reply_to_message: - for i in message.reply_to_message.caption_entities: - if i['type'] == 'text_link': url = i['url']; break - - if url: - try: - [group_id, last_post] = list(map(int, url.split('wall')[-1].split('_'))) - - vk = VkParser(token) - vk.like(group_id, last_post) - - await message.answer(text='Вы поставили лайк 😉') - except Exception as e: - await message.answer(text='По неизвестной причине не удалось поставить лайк!') - else: - await message.answer(text='Вы ответили не на тот пост, для отправки лайка!') - else: - await message.answer(text='Вы не отметили пост который хотите лайкнуть или не прислали свой токен для ' - 'возможности лайкать посты!') - - -# ВЗАИМОДЕЙСТВИЕ С ДАННЫМИ ВК ПОСТОВ -@dp.message_handler(commands=["last_post"]) -async def last_post(message: types.Message): - user_id = message.chat.id - short_id = login(user_id, du, db) - domain = message.get_args() - - if domain: - try: - token = db.get_token(short_id) - if token: vk = VkParser(token) - else: vk = VkParser(config.serviceToken) - - output = vk.last_post(domain=domain); vk.login(domain) - text, audio, media = pars_post(types, output) - - if audio and media: - post_message = await bot.send_media_group(chat_id=user_id, media=media) - await bot.send_media_group(chat_id=user_id, media=audio, reply_to_message_id=post_message[0].message_id) - elif audio and media == []: - post_message = await bot.send_message(chat_id=user_id, text=text, parse_mode=types.ParseMode.MARKDOWN) - await bot.send_media_group(chat_id=user_id, media=audio, reply_to_message_id=(post_message[0].message_id)) - elif audio == [] and media: - await bot.send_media_group(chat_id=user_id, media=media) - else: - await bot.send_message(chat_id=user_id, text=text, parse_mode=types.ParseMode.MARKDOWN) - - except Exception as e: - await message.answer("Короткое имя страницы неверное или у вас нет доступа к этой странице!") - else: - await message.answer("Команда введена некорректно! Корректный ввод: /last_post example") - - -# ПОЛУЧЕНИЕ ТОКЕНА ПОЛЬЗОВАТЕЛЯ -@dp.message_handler(content_types="text") -async def url(message: types.Message): - user_id = login(message.chat.id, du, db) - - if config.checkUrl in message.text: - try: - token = message.text.split("&")[0].split("=")[-1] - info = VkParser(token).check() - - db.update_token(user_id, token) - await message.answer("Токен успешно сохранён!") - except Exception as e: - await message.answer("Ваша ссылка не корректна, попробуйте повторить копирование!") - else: - await message.answer("Ваше сообщение не корректно, необходима ссылка, что отобразилась в строке поиске после" - " подтверждения доступа!") - - -# ПРОВЕРКА СТРАНИЦ НА НОВЫЕ ПОСТЫ -async def timer(wait_for): - while True: - await asyncio.sleep(wait_for) - - notif_group = list(map(lambda x: x[0], db.all_notifGroup())) - activ_user = list(map(lambda x: [x[0], list(map(int, x[1].split(';')))], db.all_activUser())) - sub_user = list(map(lambda x: [x[0], list(map(int, x[1].split(';')))], db.all_subUser())) - - for group_id in notif_group: - time.sleep(1) - - tokens = [x[0] for x in activ_user if group_id in x[1]] - users = [x[0] for x in sub_user if group_id in x[1]] - - if tokens: - user_id = tokens[random.randrange(len(tokens))] - vk = VkParser(db.get_token(user_id)) - else: vk = VkParser(config.serviceToken) - - try: - output = vk.last_post(owner_id=du.get_vk_id(group_id)) - last_post = db.get_postGroup(group_id) - - if output[3] > last_post: - text, audio, media = pars_post(types, output) - db.update_postGroup(group_id, output[3]) - - for sub in users: - tg_id = du.get_tg_id(sub) - - if audio and media: - post_message = await bot.send_media_group(chat_id=tg_id, media=media) - await bot.send_media_group(chat_id=tg_id, media=audio, reply_to_message_id=post_message[0].message_id) - elif audio and media == []: - post_message = await bot.send_message(chat_id=tg_id, text=text, parse_mode=types.ParseMode.MARKDOWN) - await bot.send_media_group(chat_id=tg_id, media=audio, reply_to_message_id=post_message[0].message_id) - elif audio == [] and media: - await bot.send_media_group(chat_id=tg_id, media=media) - else: - await bot.send_message(chat_id=tg_id, text=text, parse_mode=types.ParseMode.MARKDOWN) - - except Exception as e: - print(repr(e)) - - -if __name__ == '__main__': - loop = asyncio.get_event_loop() - loop.create_task(timer(60)) # ПРОВЕРКА КАЖДУЮ МИНУТУ - executor.start_polling(dp, skip_updates=True) +import asyncio, logging, time, random, scripts, vk_scripts + +from init import * +from config import * +from handlers import router + + +async def timer(wait_for) -> None: + while True: + await asyncio.sleep(wait_for) + + notif_group = list(map(lambda x: x[0], db.all_notifGroup())) + activ_user = list(map(lambda x: [x[0], list(map(int, x[1].split(';')))], db.all_activUser())) + sub_user = list(map(lambda x: [x[0], list(map(int, x[1].split(';')))], db.all_subUser())) + + for group_id in notif_group: + time.sleep(1) + + tokens = [x[0] for x in activ_user if group_id in x[1]] + users = [x[0] for x in sub_user if group_id in x[1]] + + if tokens: + user_id = tokens[random.randrange(len(tokens))] + vk = vk_scripts.VkParser(db.get_token(user_id)) + else: vk = vk_scripts.VkParser(serviceToken) + + try: + output = vk.last_post(owner_id=du.get_vk_id(group_id)) + last_post = db.get_postGroup(group_id) + + if output[3] > last_post: + text, audio, media = scripts.pars_post(types, output) + db.update_postGroup(group_id, output[3]) + + for sub in users: + tg_id = du.get_tg_id(sub) + + if audio and media: + post_message = await bot.send_media_group(chat_id=tg_id, media=media) + await bot.send_media_group(chat_id=tg_id, media=audio, + reply_to_message_id=post_message[0].message_id) + elif audio and media == []: + post_message = await bot.send_message(chat_id=tg_id, text=text) + await bot.send_media_group(chat_id=tg_id, media=audio, + reply_to_message_id=post_message[0].message_id) + elif audio == [] and media: + await bot.send_media_group(chat_id=tg_id, media=media) + else: + await bot.send_message(chat_id=tg_id, text=text, disable_web_page_preview=True) + + except Exception as err: + logging.error("Ошибка обновления поста:", exc_info=True) + + +async def main() -> None: + dp.include_router(router) + await bot.delete_webhook(drop_pending_updates=True) + await dp.start_polling(bot, allowed_updates=dp.resolve_used_update_types(), skipUpdates=True) + + +async def process() -> None: + await asyncio.gather(main(), timer(60)) + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + + try: asyncio.run(process()) + except KeyboardInterrupt: pass diff --git a/code/config.py b/code/config.py index 93d5fef..95f35ab 100644 --- a/code/config.py +++ b/code/config.py @@ -1,14 +1,14 @@ -# TOKENS -botToken = '**********' -serviceToken = "**********" - -# URL -loginUrl = "************" -checkUrl = "https://oauth.vk.com/blank.html#access_token=" - -# VARIABILITY -unvote = {True: 'без возможности переголосовать', False: 'с возможностью переголосовать'} -anonymous = {True: 'анонимный опрос', False: 'не анонимный опрос'} - -# DATA -data = {0: 'Б', 1: 'КБ', 2: 'МБ', 3: 'ГБ', 4: 'ТБ'} +# TOKENS +botToken = '**********' +serviceToken = "**********" + +# URL +loginUrl = "************" +checkUrl = "https://oauth.vk.com/blank.html#access_token=" + +# VARIABILITY +unvote = {True: 'без возможности переголосовать', False: 'с возможностью переголосовать'} +anonymous = {True: 'анонимный опрос', False: 'не анонимный опрос'} + +# DATA +data = {0: 'Б', 1: 'КБ', 2: 'МБ', 3: 'ГБ', 4: 'ТБ'} diff --git a/code/handlers.py b/code/handlers.py new file mode 100644 index 0000000..1dd8b3e --- /dev/null +++ b/code/handlers.py @@ -0,0 +1,304 @@ +from aiogram import types, F, Router +from aiogram.types import Message, CallbackQuery, ContentType +from aiogram.filters import Command + +from emoji import emojize + +from init import * +from config import * +from vk_scripts import VkParser +from scripts import login, pars_post + +router = Router() + + +@router.message(Command("start", "help")) +async def start(msg: Message) -> None: + login(msg.chat.id, du, db) + + buttons = [[types.InlineKeyboardButton(text="КОМАНДЫ", callback_data="com"), + types.InlineKeyboardButton(text="АВТОР", callback_data="auth")]] + keyboard = types.InlineKeyboardMarkup(inline_keyboard=buttons, row_width=2) + + await msg.answer(text=f'Portal in VK - это бот для перепоста постов со страниц в социальной сети ВКонтакте. ' + f'Для начала работы вам нужно всего лишь вызвать команду /add и добавить к ней ' + f'короткое имя личной или публичной страницы. После этого, бот начнёт присылать каждый новый ' + f'пост с этой страницы форматируя данные с неё под сообщение в Telegram. С полным ' + f'функционалом бота, вы можете ознакомиться по кнопке КОМАНДЫ\n\n' + + f'Если вы хотите ставить лайки или получать уведомления от закрытых страниц на которые ' + f'подписаны в социальной сети, вам необходимо скинуть ссылку из поисковой строки в чат, ' + f'после подтверждения на сайте', reply_markup=keyboard) + + +# ИЛАЙН КЛАВИАТУРА HELP +@router.callback_query(F.data == "com") +async def function(call: CallbackQuery) -> None: + await call.message.answer(text='| КОМАНДЫ |\n\n' + '/like - лайк на пост, который вы отметили\n' + '/list - список страниц от, которых вы получаете уведомления\n' + '/notif - отписка или подписка от всех уведомлений\n' + '/last_post - получение последнего поста по короткому имени страницы\n' + '/add и /del - добавление и удаление странички из списка подписок\n') + + +@router.callback_query(F.data == "auth") +async def author(call: CallbackQuery) -> None: + await call.message.answer(text='| АВТОР |\n\n>> Этот бот не коммерческий проект, для получения ' + 'постов из социальной сети ВКонтакте. Бот работает на сервисном токене VK API, ' + 'пока вы не предоставите собственный. Ваш токен нужен для получения постов со ' + 'страниц, доступ к которым есть исключительно у вас. Также это даст вам ' + 'возможность ставить через Telegram лайки на посты в самой социальной сети!' + + '\n\nЯ же пишу подобные небольшие проекты, о которых вы можете узнать ' + 'больше на моём GitHub.') + + +# КОМАНДЫ +@router.message(Command("notif")) +async def notification(msg: Message) -> None: + user_id = login(msg.chat.id, du, db) + status = db.get_status(user_id) + + groups = list(map(int, db.get_user_groups(user_id).split(";"))) + for group in groups: db.update_countGroup(group, -1 if status else 1) + + if status: await msg.answer("Получение постов из ВК выключено!") + else: await msg.answer("Получение постов из ВК включено!") + db.update_status(user_id) + + +@router.message(Command("add", "del")) +async def add_del(msg: Message) -> None: + user_id = login(msg.chat.id, du, db) + + domain = msg.text.split()[-1] + command = msg.text.split()[0] + + if "/" not in domain: + try: + token = db.get_token(user_id) + if token: vk = VkParser(token) + else: vk = VkParser(serviceToken) + + group_id, typ, last_post = vk.login(domain) + + if not du.group_exists(group_id): + du.add_group(group_id) + group_id = du.get_group_id(group_id) + + if not db.infoGroup_exists(group_id): + db.add_infoGroup(group_id, typ, last_post) + + groups = db.get_user_groups(user_id) + user_groups = groups.split(";") if groups else [] + + if str(group_id) not in user_groups: + if command == "/del": + await msg.answer("Вы и так не подписаны на эту группу!") + else: + if db.get_countUser(user_id) < 10: + user_groups.append(str(group_id)) + db.update_user_groups(user_id, ";".join(user_groups)) + + db.update_countUser(user_id, 1) + if db.get_status(user_id): db.update_countGroup(group_id, 1) + + await msg.answer("Группа успешно добавлена в список уведомлений!") + else: + await msg.answer("У вас добавлено максимально количество групп!") + else: + if command == "/add": + await msg.answer("Вы уже подписаны на эту страницу!") + else: + user_groups.remove(str(group_id)) + db.update_user_groups(user_id, ";".join(user_groups)) + + db.update_countUser(user_id, -1) + if db.get_status(user_id): db.update_countGroup(group_id, -1) + + await msg.answer("Группа успешно удалена из списка уведомлений!") + except Exception as err: + logging.info("Нет доступа к страницы:", exc_info=True) + await msg.answer("Произошла ошибка! Проверьте правильность написания короткого адреса " + "страницы, имеете ли вы доступ к этой странице и есть ли на ней хотя бы " + "один пост!") + else: + if command == "/add": + await msg.answer("Команда введена некорректно, такой страницы не существует или она закрыта для " + "просмотра!\n\nКорректный ввод команд: /add example") + elif command == "/del": + await msg.answer("Команда введена некорректно, такой страницы не существует или она закрыта для " + "просмотра!\n\nКорректный ввод команд: /del example") + + +@router.message(Command("list")) +async def lst(msg: Message) -> None: + user_id = login(msg.chat.id, du, db) + groups = db.get_user_groups(user_id) + user_groups = list(map(int, groups.split(";") if groups else [])) + + if user_groups: + group_ids = [du.get_vk_id(id) for id in user_groups] + + vk = VkParser(serviceToken) + peoples, groups = vk.info(group_ids) + + text = "* Список страниц, от которых вы получаете уведомления!\n" + + if peoples: + text += "\nЛичные страницы пользователей\n" + for i, people in enumerate(peoples): + text += f'{emojize(":green_circle:") if people[2] else emojize(":red_circle:")} ' \ + f'{i + 1}. {people[1]} @{people[0]}\n' + + if groups: + text += "\nГруппы / сообщества / паблики\n" + for i, group in enumerate(groups): + text += f'{i + 1}. {group[1]} @{group[0]}\n' + + await msg.answer(text, disable_web_page_preview=True) + else: + await msg.answer("Вы не добавили ни одной страницы для отслеживания публикаций!") + + +@router.message(Command("like")) +async def like(msg: Message) -> None: + user_id = login(msg.chat.id, du, db) + token = db.get_token(user_id) + + if token and (msg.reply_to_message is not None): + url = '' + + if msg.reply_to_message.entities is not None: + for i in list(msg.reply_to_message.entities): + i = dict(i) + if i['type'] == 'text_link': + url = i['url']; break + elif msg.reply_to_message.caption_entities is not None: + for i in list(msg.reply_to_message.caption_entities): + i = dict(i) + if i['type'] == 'text_link': + url = i['url']; break + + if url: + try: + [group_id, last_post] = list(map(int, url.split('wall')[-1].split('_'))) + + vk = VkParser(token) + vk.like(group_id, last_post) + + await msg.answer('Вы поставили лайк 😉') + except Exception as err: + logging.error("Не удалось поставить лайк:", exc_info=True) + await msg.answer('По неизвестной причине не удалось поставить лайк!') + else: + await msg.answer('Вы ответили не на тот пост, для отправки лайка!') + else: + await msg.answer('Вы не отметили пост который хотите лайкнуть или не прислали свой токен для ' + 'возможности лайкать посты!') + + +@router.message(Command("update")) +async def update(msg: Message) -> None: + user_id = msg.chat.id + short_id = login(msg.chat.id, du, db) + + if msg.reply_to_message is not None: + url = '' + + if msg.reply_to_message.entities is not None: + for i in list(msg.reply_to_message.entities): + i = dict(i) + if i['type'] == 'text_link': + url = i['url']; break + elif msg.reply_to_message.caption_entities is not None: + for i in list(msg.reply_to_message.caption_entities): + i = dict(i) + if i['type'] == 'text_link': + url = i['url']; break + + if url: + try: + [group_id, last_post] = list(map(int, url.split('wall')[-1].split('_'))) + + token = db.get_token(short_id) + if token: vk = VkParser(token) + else: vk = VkParser(serviceToken) + + output = vk.last_post(post=f"{group_id}_{last_post}") + text, audio, media = pars_post(types, output) + + delete_r = await bot.delete_message(chat_id=user_id, message_id=msg.reply_to_message.message_id) + delete = await bot.delete_message(chat_id=user_id, message_id=msg.message_id) + + if audio and media: + post_message = await bot.send_media_group(chat_id=user_id, media=media) + await bot.send_media_group(chat_id=user_id, media=audio, + reply_to_message_id=post_message[0].message_id) + elif audio and media == []: + post_message = await bot.send_message(chat_id=user_id, text=text) + await bot.send_media_group(chat_id=user_id, media=audio, + reply_to_message_id=post_message[0].message_id) + elif audio == [] and media: + await bot.send_media_group(chat_id=user_id, media=media) + else: + await bot.send_message(chat_id=user_id, text=text, disable_web_page_preview=True) + + except Exception as err: + logging.danger("Ошибка обновления поста:", exc_info=True) + await msg.answer(text='По неизвестной причине не удалось обновить пост!') + else: await msg.answer(text='Вы ответили не на тот пост!') + else: await msg.answer(text='Вы не отметили пост который хотите обновить!') + + +# ВЗАИМОДЕЙСТВИЕ С ДАННЫМИ ВК ПОСТОВ +@router.message(Command("last_post")) +async def last_post(msg: Message) -> None: + user_id = msg.chat.id + short_id = login(user_id, du, db) + + domain = msg.text.split()[-1] + if "/" not in domain: + try: + token = db.get_token(short_id) + if token: vk = VkParser(token) + else: vk = VkParser(serviceToken) + + output = vk.last_post(domain=domain); vk.login(domain) + text, audio, media = pars_post(types, output) + + if audio and media: + post_message = await bot.send_media_group(chat_id=user_id, media=media) + await bot.send_media_group(chat_id=user_id, media=audio, reply_to_message_id=post_message[0].message_id) + elif audio and media == []: + post_message = await bot.send_message(chat_id=user_id, text=text) + await bot.send_media_group(chat_id=user_id, media=audio, reply_to_message_id=post_message[0].message_id) + elif audio == [] and media: + await bot.send_media_group(chat_id=user_id, media=media) + else: + await bot.send_message(chat_id=user_id, text=text, disable_web_page_preview=True) + + except Exception as err: + logging.info("Некорректное/недоступное короткое имя:", exc_info=True) + await msg.answer("Короткое имя страницы неверное или у вас нет доступа к этой странице!") + else: await msg.answer("Команда введена некорректно! Корректный ввод: /last_post example") + + +# ПОЛУЧЕНИЕ ТОКЕНА ПОЛЬЗОВАТЕЛЯ +@router.message(F.content_type == ContentType.TEXT) +async def url(msg: Message) -> None: + user_id = login(msg.chat.id, du, db) + + if checkUrl in msg.text: + try: + token = msg.text.split("&")[0].split("=")[-1] + info = VkParser(token).check() + + db.update_token(user_id, token) + await msg.answer("Токен успешно сохранён!") + except Exception as err: + logging.info("Некорректная ссылка:", exc_info=True) + await msg.answer("Ваша ссылка не корректна, попробуйте повторить копирование!") + else: await msg.answer("Ваше сообщение не корректно, необходима ссылка, что отобразилась в строке " + "поиске после подтверждения доступа!") diff --git a/code/init.py b/code/init.py new file mode 100644 index 0000000..7f5bbd0 --- /dev/null +++ b/code/init.py @@ -0,0 +1,22 @@ +from aiogram import Bot, Dispatcher +from aiogram.enums.parse_mode import ParseMode +from aiogram.fsm.storage.memory import MemoryStorage +from aiogram.client.bot import DefaultBotProperties + + +from config import botToken + +bot = Bot(token=botToken, default=DefaultBotProperties(parse_mode=ParseMode.HTML)) +dp = Dispatcher(storage=MemoryStorage()) + + +from sql import Users, Base + +du = Users('../db/users.db') +db = Base('../db/base.db') + + +import logging, functools + +logging.basicConfig(level=logging.INFO, filename='../info.log',filemode="w", + format="%(asctime)s %(levelname)s %(message)s") diff --git a/code/scripts.py b/code/scripts.py index d774876..00fa9a1 100644 --- a/code/scripts.py +++ b/code/scripts.py @@ -1,57 +1,74 @@ -import config - - -def login(user_id, du, db): - user_id = int(user_id) - if not du.user_exists(user_id): du.add_user(user_id) - - user_id = du.get_user_id(user_id) - if not db.infoUser_exists(user_id): db.add_infoUser(user_id) - - return user_id - - -def pars_post(types, output): - media, ttl, url, audio, poll, link = [], [], [], [], [], [] - - for group in output[0]: - if group[0] == "poll": poll = group[1] - elif group[0] == "link": link.append(group[1]) - elif group[0] == "video": ttl.append(group[1]) - elif group[0] == "doc" and group[1][2] != 4: url.append([group[1][0], group[1][1], group[1][3]]) - elif group[0] == "audio": audio.append(types.InputMediaAudio(media=group[1][1], parse_mode=types.ParseMode.MARKDOWN, - caption=f"*Название: {group[1][0]} / Автор: {group[1][2]}*")) - - if output[1] != "": text = f"{output[2]}\n\n*{output[1]}*\n" - else: text = f"{output[2]}\n" - - if poll: - text += f"\n*> Проводиться {config.anonymous[poll[3]]} {config.unvote[poll[2]]}!" - text += f"*\n*---------------------*\n*- Вопрос: {poll[0]} [{poll[1]}]*\n" - text += ''.join([f'*{i}. Ответ: {ans[0]} [{ans[1]}]*\n' for i, ans in enumerate(poll[4])]) - text += "*---------------------*\n" - - if ttl: text += f"\n*Видео -* находятся в оригинальном посте в количестве {len(ttl)} записей\n" - - if url: - for data in url: - k, dt = 0, data[2] - while True: - if dt > 1024: dt = dt // 1024; k += 1 - else: break - text += f"\n*Документ - [* [{data[0]}]({data[1]}) ({dt} {config.data[k]}) *]*" - text += "\n" - - if link: text += ''.join([f"\n*Ссылка - [* [{s[0]}]({s[1]}) *]*" for s in link]) + "\n" - if audio: text += '\n*« Также присутствуют аудио файлы »*' - - for i, group in enumerate(output[0]): - if group[0] == "photo": - if i: media.append(types.InputMediaPhoto(media=group[1])) - else: media.append(types.InputMediaPhoto(media=group[1], caption=text, parse_mode=types.ParseMode.MARKDOWN)) - elif group[0] == "doc": - if group[1][2] == 4: - if i: media.append(types.InputMediaPhoto(media=group[1][1])) - else: media.append(types.InputMediaPhoto(media=group[1][1], caption=text, parse_mode=types.ParseMode.MARKDOWN)) - - return text, audio, media +import config + + +def login(user_id, du, db): + user_id = int(user_id) + if not du.user_exists(user_id): du.add_user(user_id) + + user_id = du.get_user_id(user_id) + if not db.infoUser_exists(user_id): db.add_infoUser(user_id) + + return user_id + + +def pars_post(types, output): + media, video, url, audio, poll, link = [], [], [], [], [], [] + + for group in output[0]: + if group[0] == "poll": poll = group[1] + elif group[0] == "link": link.append(group[1]) + elif group[0] == "video": video.append(group[1]) + elif group[0] == "doc" and group[1][2] != 4: url.append([group[1][0], group[1][1], group[1][3]]) + elif group[0] == "audio": audio.append(types.InputMediaAudio( + media=group[1][1], caption=f"Название: {group[1][0]} / Автор: {group[1][2]}")) + + if output[1] != "": + post = [elem_2 for elem_1 in output[1].split("[") for elem_2 in elem_1.split("]")] + for i, elem in enumerate(post): + if "|" in elem and i != 0: + elem = elem.split("|") + post[i] = f"{elem[1]}" + + text = f"{output[2]}\n\n{''.join(post)}\n" + else: text = f"{output[2]}\n" + + if poll: + text += f"\n> Проводиться {config.anonymous[poll[3]]} {config.unvote[poll[2]]}! Вопрос: {poll[0]} [{poll[1]}]\n" + text += ''.join([f'{i + 1}. Ответ: {ans[0]} [{ans[1]}]\n' for i, ans in enumerate(poll[4])]) + "\n" + + if video: + for frame in video: + text += f"\nВидео « {frame[0]} »\n" + + k, dura = 0, frame[1] + while k < 1: + if dura > 60: dura = dura // 60; k += 1 + else: break + + text += f"Длительность: {dura} {config.dura[k]}; Лайков: {frame[2]};\n" + + if url: + for data in url: + k, dt = 0, data[2] + while True: + if dt > 1024: dt = dt // 1024; k += 1 + else: break + text += f"\nДокумент - [ {data[0]} ({dt} {config.data[k]}) ]" + text += "\n" + + if link: text += ''.join([f"\nСсылка - [ [{s[0]}]({s[1]}) ]" for s in link]) + "\n" + if audio: text += '\n« Также присутствуют аудио файлы »' + + for i, group in enumerate(output[0]): + if group[0] == "photo": + if i: media.append(types.InputMediaPhoto(media=group[1])) + else: media.append(types.InputMediaPhoto(media=group[1], caption=text)) + elif group[0] == "video": + if i: media.append(types.InputMediaPhoto(media=group[1][3])) + else: media.append(types.InputMediaPhoto(media=group[1][3], caption=text)) + elif group[0] == "doc": + if group[1][2] == 4: + if i: media.append(types.InputMediaPhoto(media=group[1][1])) + else: media.append(types.InputMediaPhoto(media=group[1][1], caption=text)) + + return text, audio, media diff --git a/code/sql.py b/code/sql.py index cc98bf9..a1b249b 100644 --- a/code/sql.py +++ b/code/sql.py @@ -166,3 +166,4 @@ class Base: def close(self): """Закрываем соединение с БД""" self.connection.close() + diff --git a/code/vk_scripts.py b/code/vk_scripts.py index 9f02e09..0d5b56c 100644 --- a/code/vk_scripts.py +++ b/code/vk_scripts.py @@ -1,76 +1,109 @@ -import vk_api - - -class VkParser: - def __init__(self, token): - """Подключаемся к API VK""" - self.session = vk_api.VkApi(token=token) - self.vk = self.session.get_api() - - def check(self): - return self.vk.account.getInfo(fields="lang") - - def like(self, owner_id, post_id): - return self.vk.likes.add(type="post", owner_id=owner_id, item_id=post_id) - - def login(self, domain): - posts = self.vk.wall.get(domain=domain, count=3) - post = max([[int(pt["id"]), int(pt["owner_id"])] for pt in posts["items"]]) - - [last_post, group_id] = post - typ = 0 if group_id > 0 else 1 - return group_id, typ, last_post - - def info(self, group_ids): - groups, peoples = [], [] - - for id in group_ids: - if id > 0: peoples.append(id) - else: groups.append(id) - - if groups: - data = self.vk.groups.getById(group_ids=", ".join(list(map(lambda x: str(x)[1:], groups)))) - groups = [[elem['screen_name'], elem['name']] for elem in data] - - if peoples: - data = self.vk.users.get(user_ids=", ".join(list(map(str, peoples))), fields="domain, online") - peoples = [[elem['domain'], elem['first_name'] + ' ' + elem['last_name'], elem['online']] for elem in data] - - return peoples, groups - - def last_post(self, owner_id=None, domain=None): - wall = self.vk.wall.get(owner_id=owner_id, domain=domain, count=2, extended=1) - post = max([[int(pt["id"]), pt] for pt in wall["items"]]) - - [post_id, post] = post - owner_id, output = post['owner_id'], [] - - if owner_id > 0: [name, domain] = [[per['first_name'] + ' ' + per['last_name'], per['screen_name']] - for per in wall["profiles"] if per["id"] == owner_id][0] - else: [name, domain] = [[gro['name'], gro['screen_name']] - for gro in wall["groups"] if gro["id"] == int(str(owner_id)[1:])][0] - - attachments = post['attachments'] - types = [[typ['type'], typ] for typ in attachments] - - for typ in types: - if typ[0] == 'photo': - output.append([typ[0], typ[1]['photo']['sizes'][-1]['url']]) - elif typ[0] == 'video': - output.append([typ[0], typ[1]['video']['title']]) - elif typ[0] == "doc": - output.append([typ[0], [typ[1]['doc']['title'], typ[1]['doc']['url'], - typ[1]['doc']['type'], typ[1]['doc']['size']]]) - elif typ[0] == "audio": - output.append([typ[0], [typ[1]['audio']['title'], typ[1]['audio']['url'], typ[1]['audio']['artist']]]) - elif typ[0] == "poll": - answer = [[que['text'], que['votes']] for que in typ[1]['poll']['answers']] - output.append([typ[0], [typ[1]['poll']['question'], typ[1]['poll']['votes'], - typ[1]['poll']['disable_unvote'], typ[1]['poll']['anonymous'], answer]]) - elif typ[0] == "link": - output.append([typ[0], [typ[1]['link']['title'], typ[1]['link']['url']]]) - - return [output, post['text'], - f"*Автор поста -* [{name}](https://vk.com/{domain}?w=wall{owner_id}_{post_id})", post_id] - - +import vk_api + + +class VkParser: + def __init__(self, token): + """Подключаемся к API VK""" + self.session = vk_api.VkApi(token=token) + self.vk = self.session.get_api() + + def check(self): + return self.vk.account.getInfo(fields="lang") + + def like(self, owner_id, post_id): + return self.vk.likes.add(type="post", owner_id=owner_id, item_id=post_id) + + def login(self, domain): + posts = self.vk.wall.get(domain=domain, count=3) + post = max([[int(pt["id"]), int(pt["owner_id"])] for pt in posts["items"]]) + + [last_post, group_id] = post + typ = 0 if group_id > 0 else 1 + return group_id, typ, last_post + + def info(self, group_ids): + groups, peoples = [], [] + + for id in group_ids: + if id > 0: peoples.append(id) + else: groups.append(id) + + if groups: + data = self.vk.groups.getById(group_ids=", ".join(list(map(lambda x: str(x)[1:], groups)))) + groups = [[elem['screen_name'], elem['name']] for elem in data] + + if peoples: + data = self.vk.users.get(user_ids=", ".join(list(map(str, peoples))), fields="domain, online") + peoples = [[elem['domain'], elem['first_name'] + ' ' + elem['last_name'], elem['online']] for elem in data] + + return peoples, groups + + def last_post(self, owner_id=None, domain=None, post=None): + if post: wall = self.vk.wall.getById(posts=post, extended=1) + else: wall = self.vk.wall.get(owner_id=owner_id, domain=domain, count=2, extended=1) + + post = max([[int(pt["id"]), pt] for pt in wall["items"]]) + owner_id = post[1]['owner_id'] + + if owner_id > 0: + [name, domain] = [[per['first_name'] + ' ' + per['last_name'], per['screen_name']] for per in wall["profiles"] if per["id"] == owner_id][0] + else: + [name, domain] = [[gro['name'], gro['screen_name']] for gro in wall["groups"] if gro["id"] == int(str(owner_id)[1:])][0] + + if 'copy_history' in post[1]: + comment, orig_id = post[1]['text'], post[0] + + post = [post[1]['copy_history'][0]['id'], post[1]['copy_history'][0]] + owner_id_r = post[1]['owner_id'] + + if owner_id_r > 0: + [name_r, domain_r] = [[per['first_name'] + ' ' + per['last_name'], per['screen_name']] for per in wall["profiles"] if per["id"] == owner_id_r][0] + else: + [name_r, domain_r] = [[gro['name'], gro['screen_name']] for gro in wall["groups"] if gro["id"] == int(str(owner_id_r)[1:])][0] + + return get_output(post, name_r, domain_r, [orig_id, owner_id, name, domain, comment]) + else: + return get_output(post, name, domain) + + +def get_output(post, name, domain, repost=None): + [post_id, post] = post + owner_id, output = post['owner_id'], [] + + attachments = post['attachments'] + types = [[typ['type'], typ] for typ in attachments] + + for typ in types: + if typ[0] == 'photo': + output.append([typ[0], typ[1]['photo']['sizes'][-1]['url']]) + elif typ[0] == 'video': + video = typ[1]['video'] + if 'photo_1280' in video: + frame = video['photo_1280'] + else: + frame = video['photo_800'] + + output.append([typ[0], [video['title'], video['duration'], video['views'], frame]]) + elif typ[0] == "doc": + output.append([typ[0], [typ[1]['doc']['title'], typ[1]['doc']['url'], + typ[1]['doc']['type'], typ[1]['doc']['size']]]) + elif typ[0] == "audio": + output.append([typ[0], [typ[1]['audio']['title'], typ[1]['audio']['url'], typ[1]['audio']['artist']]]) + elif typ[0] == "poll": + answer = [[que['text'], que['votes']] for que in typ[1]['poll']['answers']] + output.append([typ[0], [typ[1]['poll']['question'], typ[1]['poll']['votes'], + typ[1]['poll']['disable_unvote'], typ[1]['poll']['anonymous'], answer]]) + elif typ[0] == "link": + output.append([typ[0], [typ[1]['link']['title'], typ[1]['link']['url']]]) + + if repost: + [author_id, author_owner_id, author_name, author_domain, author_comment] = repost + + comment = f"Автор репоста - {author_name}" + if author_comment != "": comment += f"\n
{author_comment}
\n" + comment += f"\nАвтор поста - {name}" + + return [output, f"{post['text']}", comment, author_id] + else: + return [output, f"{post['text']}", + f"Автор поста - {name}", post_id] \ No newline at end of file