123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 |
- import asyncio
- import json
- import sqlite3
- import traceback
- from sqlite3 import OperationalError
- from typing import NamedTuple, Iterable
- from aiogram import Bot
- from aiogram.dispatcher import Dispatcher, FSMContext
- from aiogram.dispatcher.filters import BoundFilter, AdminFilter, Command
- from aiogram.dispatcher.handler import SkipHandler, CancelHandler
- from aiogram.dispatcher.middlewares import BaseMiddleware
- from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton, Update, ChatPermissions, BotCommand, \
- Chat, BotCommandScopeChat, \
- BotCommandScopeAllChatAdministrators
- from aiogram.utils import executor
- from aiogram.contrib.fsm_storage.memory import MemoryStorage
- from aiogram import types
- from aiogram.utils.exceptions import ChatNotFound, MessageToDeleteNotFound, Unauthorized, MessageCantBeDeleted
- from aiogram.utils.markdown import hlink, hbold, hpre, hitalic, underline, hunderline
- from loguru import logger
- from sqliter import Database
- import config
- bot = Bot(token=config.bot_token, parse_mode='HTML')
- dp = Dispatcher(bot, storage=MemoryStorage())
- db = Database('database.db')
- logger.add('bot.log', format="{time:DD-MM-YYYY HH:mm:ss} | {level} | {message}",
- rotation='1MB', compression='zip', enqueue=True)
- filter_worlds = [word[1] for word in db.select_all_word()]
- chats = {}
- async def update_chat_list():
- '''
- :return: dict{chat_id: [require_channel_id]}
- '''
- new_chats = {}
- chats_db = db.select_all_chats()
- for i in chats_db:
- new_chats[i[0]] = []
- for i in chats_db:
- new_chats[i[0]].append(i[1])
- return new_chats
- async def check_subs(m: types.Message, user_id=None):
- if user_id is None:
- user_id = m.from_user.id
- try:
- req_channels = chats[m.chat.id]
- except KeyError:
- return 2
- subscribe_channels = []
- for channel in req_channels:
- res = await dp.bot.get_chat_member(channel, user_id)
- if res.status == 'left':
- return 1
- elif res.status == 'member':
- subscribe_channels.append(channel)
- if len(req_channels) == len(subscribe_channels):
- return 0
- async def subs_keys(chat_id, user_id) -> InlineKeyboardMarkup:
- global chats
- chats_entity = [await dp.bot.get_chat(c) for c in chats[chat_id]]
- chats_button = [(chat.title, chat.invite_link) for chat in chats_entity]
- keyboard = InlineKeyboardMarkup(row_width=1)
- keyboard.add(*[InlineKeyboardButton(text=n, url=url) for n, url in chats_button])
- keyboard.add(*[InlineKeyboardButton(text=n, callback_data=c) for n, c in
- {'✅ Проверить подписку': json.dumps({'subs': user_id})}.items()])
- return keyboard
- class JoinUsers(BaseMiddleware):
- async def on_pre_process_update(self, update: Update, data: dict):
- global chats
- try:
- if update.my_chat_member.new_chat_member.status == "administrator":
- if update.my_chat_member.chat.id not in chats:
- db.add_channel(0, update.my_chat_member.chat.id)
- except:
- pass
- try:
- if update.message.new_chat_members:
- await welcome_message(update.message)
- raise CancelHandler
- except Exception:
- return
- class IsAdmin(BoundFilter):
- async def check(self, m: types.Message) -> bool:
- if m.from_user.id in config.ADMIN_ID:
- return True
- async def welcome_message(m: types.Message):
- await dp.bot.delete_message(m.chat.id, m.message_id)
- user_id = m.from_user.id
- chat_id = m.chat.id
- if chat_id in chats:
- text = f'Привет, {hlink(m.from_user.full_name, f"tg://user?id={user_id}")}\n' \
- f'Чтобы начать общение в этом чате подпишись по ссылкам ниже и нажми кнопку чтобы проверить подписку\n'
- text += '\nНа подписку у тебя еcть 10 минут'
- keyboard = await subs_keys(chat_id, user_id)
- mess = await dp.bot.send_message(m.chat.id, text, reply_markup=keyboard)
- await asyncio.sleep(10 * 60)
- await mess.delete()
- logger.info(f'New User: {m.from_user.full_name}(id: {m.from_user.id})')
- @dp.callback_query_handler()
- async def check_subscribe_button(c: types.CallbackQuery):
- global chats
- m = c.message
- data = json.loads(c.data)
- if c.from_user.id not in [data['subs']]:
- await c.answer(f'Нажми на кнопку под сообщением, где упоминают именно тебя\n', show_alert=True)
- return
- result = await check_subs(m, data['subs'])
- if result == 0:
- await c.answer('Умничка, теперь твои сообщения не будут удаляться. Пиши на здоровье', show_alert=True)
- try:
- await m.delete()
- except MessageToDeleteNotFound:
- pass
- elif result == 1:
- await c.answer("Ты не на всё подписался", show_alert=True)
- @dp.message_handler(commands=['help'])
- async def help_message(m: types.Message):
- text = f'Чтобы добавить канал или группу в список обязательных для подписки нужно:\n' \
- f'1. Добавить бота как админа в этот канал или группу.\n' \
- f'2. Переслать сообщение из канала или группы в группу где этот канал или ' \
- f'группа должны быть обязательными к подписке\n' \
- f'{hitalic("При пересылке сообщения из группы есть ньюанс.")} Надо пересылать сообщение от имени группы.\n' \
- f'Такие сообщения может отправлять админ с включенной анонимностью.\n' \
- f'3. Ответить на пересланное сообщение командой <code>/channel_add</code> \n' \
- f'4. Готово! Канал/Группа теперь обязательна для подписки в этом чате.\n' \
- f'\tP.S. По такой е схеме группа или канал удаляется из списка обязательных, ' \
- f'а именно, ответом на пересланное сообщение из нужного канала или чата, только ' \
- f'с коммандой <code>/channel_del</code>\n' \
- f'\tP.P.S. Кликнув по коммандам выше, они скопируются.\n\n' \
- f'/channel_list - покажет все обязательные каналы для этого чата' \
- f'{hbold("Работа с стоп словами:")}\n' \
- f'В любом чате где есть бот или в личном диалоге с ним напишите <code>/stop_add </code>[нужное вам слово]\n' \
- f'Например так: /stop_add бинанс\n' \
- f'Теперь все сообщения со словом "бинанс" будут удаляться.\n' \
- f'Удаляются слова командой <code>/stop_del </code>\n' \
- f'/stop_list - Покажет список всех стоп слов\n'
- await m.answer(text)
- @dp.message_handler(AdminFilter(), commands=['channel_add'])
- async def channel_add(m: types.Message):
- global chats
- try:
- t = chats[m.chat.id]
- except KeyError:
- chats[m.chat.id] = []
- try:
- if m.reply_to_message.forward_from_chat.type in ['channel', 'supergroup']:
- if m.reply_to_message.forward_from_chat.id in chats[m.chat.id]:
- exist = await m.reply('Этот канал уже есть в списке обязательных\n'
- 'Через 10 секунд это сообщение удалится')
- await asyncio.sleep(10)
- await exist.delete()
- await m.delete()
- return 0
- try:
- cha = await dp.bot.get_chat(m.reply_to_message.forward_from_chat.id)
- db.add_channel(m.chat.id, m.reply_to_message.forward_from_chat.id)
- chats = await update_chat_list()
- logger.success(f'Добавил в список обязательных каналов {m.reply_to_message.forward_from_chat.title} '
- f'в чате{m.chat.title}')
- mess = await m.reply('Добавил в список обязательных каналов\n'
- 'Через 10 секунд это сообщение удалится')
- await asyncio.sleep(10)
- await m.delete()
- await mess.delete()
- except Unauthorized:
- logger.error(f"[{m.chat.title}] Бот не является админом в канале: "
- f"{m.reply_to_message.forward_from_chat.title} "
- f"id:{m.reply_to_message.forward_from_chat.id}")
- reply = await m.answer('Бот не является админом в этом канале!')
- await asyncio.sleep(10)
- await reply.delete()
- await m.delete()
- return 0
- except MessageToDeleteNotFound:
- pass
- except MessageCantBeDeleted as e:
- logger.error(f'[{m.chat.title}] {e}')
- except AttributeError:
- pass
- @dp.message_handler(AdminFilter(), commands=['channel_del'])
- async def channel_del(m: types.Message):
- global chats
- try:
- if m.reply_to_message.forward_from_chat.type in ['channel', 'supergroup']:
- db.delete_channel(m.chat.id, m.reply_to_message.forward_from_chat.id)
- chats = await update_chat_list()
- logger.success(
- f'Канал {m.reply_to_message.forward_from_chat.title} успешно удалён из обязательных в чате {m.chat.title}')
- text = 'Канал успешно удалён'
- text += '\n\nСообщение удалится через 20 секунд'
- mess = await m.reply(text)
- await asyncio.sleep(20)
- await m.delete()
- await mess.delete()
- except MessageCantBeDeleted as e:
- logger.error(f'{e}\n{json.loads(m)}')
- except AttributeError:
- pass
- except OperationalError as e:
- logger.error(f"[channel_del] {e}")
- await asyncio.sleep(10)
- await channel_del(m)
- @dp.message_handler(AdminFilter(), commands=['channel_list'])
- async def channel_list(m: types.Message):
- channels_id = [c[1] for c in db.select_channels(m.chat.id)]
- channels_entity = []
- for c in channels_id:
- try:
- entity = await dp.bot.get_chat(c)
- channels_entity.append(entity)
- except ChatNotFound:
- continue
- except Unauthorized:
- text = f"chat_id:{c}\n Похоже, что бот не является админом канала"
- channels_entity.append(Chat(id=c, title=text, invite_link='not_found'))
- chats_formated = "\n".join([f"🔸\t{hlink(chat.title, chat.invite_link)}" for chat in channels_entity])
- text = f'Список обязательных каналов для этого чата: \n' \
- f'{chats_formated}' if len(channels_entity) > 0 else 'Здесь нет обязательных каналов для подписки'
- text += '\n\nСообщение удалится через 20 секунд'
- mess = await m.reply(text)
- await asyncio.sleep(20)
- await m.delete()
- await mess.delete()
- @dp.message_handler(IsAdmin(), commands=['channel_add', 'channel_del', 'channel_list'])
- async def err_message(m: types.Message):
- logger.error(f'Этот {m.from_user.full_name}, опять мне личку пишет.....')
- await m.answer('Эту команду используй в групповом чате.')
- async def collect_words(m: types.Message):
- lines = m.text.split('\n')
- words = []
- # если одна строка и после команды что-то написанно
- if len(lines) == 1 and len(lines[0].split()) > 1:
- # склеиваем слова после команды и добавляем в общий список
- word = ' '.join([i.strip() for i in lines[0].split()[1::]])
- words.append(word)
- # Если строк больше
- elif len(lines) > 1:
- # В первой строчке не только команда
- if len(lines[0].split()) > 1:
- word = ' '.join([i.strip() for i in lines[0].split()[1::]])
- words.append(word)
- for line in lines[1::]:
- words.append(line.strip())
- return words
- @logger.catch
- @dp.message_handler(IsAdmin(), commands=['stop_add'])
- @dp.message_handler(AdminFilter(), commands=['stop_add'])
- async def add_world(m: types.Message):
- words = await collect_words(m)
- processed_words = []
- for w in words:
- try:
- db.add_word(w)
- processed_words.append(f"✅ {w} - добавленно в список стоп слов")
- logger.success(f'{w} - добавленно в список стоп слов')
- await asyncio.sleep(1)
- except sqlite3.IntegrityError as e:
- if 'UNIQUE constraint failed' in str(e):
- processed_words.append(f'❌ {w} - Уже есть в списке')
- continue
- words_success = "\n".join(processed_words)
- text = f'{hpre(words_success)}'
- await m.reply(text)
- global filter_worlds
- filter_worlds = [word[1] for word in db.select_all_word()]
- @logger.catch
- @dp.message_handler(IsAdmin(), commands=['stop_del'])
- @dp.message_handler(AdminFilter(), commands=['stop_del'])
- async def stop_delete(m: types.Message):
- words = await collect_words(m)
- processed_words = []
- for w in words:
- try:
- db.delete_word(w)
- processed_words.append(f"✅ {w} - удалено из списка стоп слов")
- logger.success(f'{w} - удалено из списка стоп слов')
- await asyncio.sleep(1)
- except Exception as e:
- print(e)
- processed_words.append(f'❌ {w} - Ошибка')
- # logger.error(f'❌ {w} - Ошибка')
- continue
- words_success = "\n".join(processed_words)
- text = f'{hpre(words_success)}'
- await m.reply(text)
- global filter_worlds
- filter_worlds = [word[1] for word in db.select_all_word()]
- @logger.catch
- @dp.message_handler(IsAdmin(), Command('stop_list'))
- @dp.message_handler(AdminFilter(), Command('stop_list'))
- async def list_stop_worlds(m: types.Message):
- messages = []
- temp_text = ''
- for i in filter_worlds:
- temp_text += f"{i}\n"
- if len(temp_text) > 4000:
- messages.append(temp_text)
- temp_text = ''
- if i is filter_worlds[-1]:
- messages.append(temp_text)
- for list_words in messages:
- no_words = 'Сюда пока что ничего не добавили((\nНапишите <code>/stop_add </code>' \
- '[слово] - чтобы добавить слово в фильтр\n'
- text = f'Список всех стоп слов: \n{hpre(list_words)}' if len(filter_worlds) > 0 else no_words
- if len(messages) > 1 and list_words not in messages[0]:
- text = hpre(list_words)
- await m.answer(text)
- @dp.message_handler(AdminFilter())
- async def any_admin_message(m: types.Message):
- # await m.answer('u are admin in chat')
- pass
- @dp.message_handler()
- # @logger.catch
- async def any_message(m: types.Message):
- global chats
- chat_id = m.chat.id
- user_id = m.from_user.id
- if chat_id in chats:
- result = await check_subs(m)
- if result == 1:
- keyboard = await subs_keys(chat_id, user_id)
- text = f'{hlink(m.from_user.full_name, f"tg://user?id={m.from_user.id}")},\n' \
- f'❗️ Вы не подписались на каналы, поэтому Ваше сообщение удаляется.\n' \
- f'✅ Подпишитесь на каналы, указанные ниже и сможете писать в чат.\n'
- warn = await m.answer(text, reply_markup=keyboard)
- try:
- await m.delete()
- except MessageToDeleteNotFound:
- pass
- await asyncio.sleep(60)
- await warn.delete()
- # try:
- # except MessageToDeleteNotFound:
- # logger.error(f'Cant delete {warn}\n{traceback.extract_tb()}')
- # return
- answers = f"Добрый день, {hlink(m.from_user.full_name, f'tg://user?id={m.from_user.id}')}\n\n" \
- f"⚠️ Если Ваше объявление удаляется в чате, то Вы можете разместить его " \
- f"только на {hunderline('ПЛАТНОЙ ОСНОВЕ.')}\n" \
- f"С тарифами на размещение можно ознакомиться в боте: @Vacansy_resume_bot\n\n" \
- f"Администрация чата не несёт ответственности за размещаемую пользователями информацию и не исправляет её."
- for w in filter_worlds:
- if w.lower() in m.text.lower():
- mess = await m.answer(answers)
- try:
- await m.delete()
- except MessageToDeleteNotFound:
- pass
- await asyncio.sleep(10)
- try:
- logger.info(f'[delete] {m.chat.title[:20]} | {m.from_user.full_name[:20]}: {w[:20]}')
- await mess.delete()
- except MessageToDeleteNotFound:
- pass
- break
- async def default_command(dp):
- for admin_id in config.ADMIN_ID:
- await dp.bot.set_my_commands([
- BotCommand('help', 'показать все комманды'),
- BotCommand('stop_add', 'Напиши после команды слово которое ловить и удалять'),
- BotCommand('stop_del', 'Напиши после команды слово которое хочешь удалить из базы'),
- BotCommand('stop_list', 'Показать все стоп-слова')
- ], BotCommandScopeChat(chat_id=admin_id))
- await dp.bot.set_my_commands([
- BotCommand('help', 'показать все комманды'),
- BotCommand('channel_add', 'Добавить канал в обязательный к подписке'),
- BotCommand('channel_del', 'Удалить канал из обязательных к подписке'),
- BotCommand('channel_list', 'Показать каналы этого чата'),
- BotCommand('stop_add', 'Напиши после команды слово которое ловить и удалять'),
- BotCommand('stop_del', 'Напиши после команды слово которое хочешь удалить из базы'),
- BotCommand('stop_list', 'Показать все стоп-слова'),
- ], BotCommandScopeAllChatAdministrators())
- async def startup(dp):
- global chats
- chats = await update_chat_list()
- me = await dp.bot.get_me()
- await default_command(dp)
- logger.debug(f"bot started | {me.first_name} @{me.username}")
- async def shut(dp):
- logger.debug("bot stopped")
- if __name__ == '__main__':
- dp.middleware.setup(JoinUsers())
- # dp.middleware.setup(CheckSubscribe())
- dp.filters_factory.bind(IsAdmin)
- executor.start_polling(dp, on_startup=startup, on_shutdown=shut)
|