Initial commit
This commit is contained in:
parent
7a1a025471
commit
a7bfd5ed4a
13
Dockerfile
Normal file
13
Dockerfile
Normal file
@ -0,0 +1,13 @@
|
||||
FROM python:3.11
|
||||
|
||||
WORKDIR /moderaotrbot
|
||||
COPY ./ ./
|
||||
|
||||
RUN rm -rf /etc/localtime
|
||||
RUN ln -s /usr/share/zoneinfo/Europe/Moscow /etc/localtime
|
||||
RUN echo "Europe/Moscow" > /etc/timezone
|
||||
|
||||
RUN pip install --upgrade pip
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
CMD ["python","-u", "main.py"]
|
36
README.md
36
README.md
@ -1,3 +1,35 @@
|
||||
# chat_moderator_bot
|
||||
## Бот транскрибатор
|
||||
|
||||
Удаляет сообщения из группы с бан-вордами. После, скидывает изменяемое в админ-панели сообщение
|
||||
### Конфигурация .env
|
||||
|
||||
```
|
||||
BOT_TOKEN - Токен бота
|
||||
|
||||
ADMINS - Админы, для которых работает /start. Записывать через запятую: user_id1,user_id2,user_id3 и т.д.
|
||||
|
||||
POSTGRES_NAME - Имя postgres бд
|
||||
POSTGRES_HOST - Хост postgres бд
|
||||
POSTGRES_PORT - Порт postgres бд
|
||||
POSTGRES_PASSWORD - Пароль от postgres бд
|
||||
POSTGRES_USER - Пользовтаель postgres бд
|
||||
|
||||
REDIS_NAME - Имя redis бд
|
||||
REDIS_HOST - Хост redis бд
|
||||
REDIS_PORT - Порт redis бд
|
||||
REDIS_PASSWORD - Пароль redis бд
|
||||
```
|
||||
|
||||
## Шаг 1: Получение Токена бота
|
||||
|
||||
1. Перейдите в [BotFather](https://t.me/BotFather)
|
||||
2. Скопируйте Токен вашего телеграмм бота
|
||||
|
||||
## Шаг 2: Загрузка переменных в окружение
|
||||
|
||||
1. Создайте файл `.env` в корневой папке проекта и заполните его значениями выше.
|
||||
|
||||
2. Скачайте необходимые проекту библиотеки командой:
|
||||
|
||||
```
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
27
config.py
Normal file
27
config.py
Normal file
@ -0,0 +1,27 @@
|
||||
from os import getenv
|
||||
from aiogram.types import BotCommand
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
BOT_TOKEN: str = getenv('BOT_TOKEN')
|
||||
|
||||
ADMINS: list = [int(admin) for admin in getenv('ADMINS').split(',')]
|
||||
|
||||
POSTGRES_NAME: str = getenv('POSTGRES_NAME')
|
||||
POSTGRES_HOST: str = getenv('POSTGRES_HOST')
|
||||
POSTGRES_PORT: int = int(getenv('POSTGRES_PORT'))
|
||||
POSTGRES_PASSWORD: str = getenv('POSTGRES_PASSWORD')
|
||||
POSTGRES_USER: str = getenv('POSTGRES_USER')
|
||||
|
||||
REDIS_NAME: int = int(getenv('REDIS_NAME'))
|
||||
REDIS_HOST: str = getenv('REDIS_HOST')
|
||||
REDIS_PORT: int =int(getenv('REDIS_PORT'))
|
||||
REDIS_PASSWORD: str = getenv('REDIS_PASSWORD')
|
||||
|
||||
commands = [
|
||||
BotCommand(
|
||||
command='start',
|
||||
description='Меню'
|
||||
)
|
||||
]
|
8
core.py
Normal file
8
core.py
Normal file
@ -0,0 +1,8 @@
|
||||
from aiogram import Bot
|
||||
|
||||
from config import BOT_TOKEN
|
||||
|
||||
bot = Bot(
|
||||
token=BOT_TOKEN,
|
||||
parse_mode="HTML"
|
||||
)
|
BIN
data/ban_words.xlsx
Normal file
BIN
data/ban_words.xlsx
Normal file
Binary file not shown.
33
docker-compose.yml
Normal file
33
docker-compose.yml
Normal file
@ -0,0 +1,33 @@
|
||||
services:
|
||||
bot:
|
||||
build:
|
||||
context: .
|
||||
restart: always
|
||||
depends_on:
|
||||
- db
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
db:
|
||||
image: postgres
|
||||
restart: always
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||
- POSTGRES_DB=${POSTGRES_NAME}
|
||||
expose:
|
||||
- '5432'
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
|
||||
redis:
|
||||
image: redis:latest
|
||||
restart: always
|
||||
command: [ "redis-server", "--requirepass", "4CEqaD0JL8gTM4XWVt8K" ]
|
||||
expose:
|
||||
- '6379'
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
redis_data:
|
8
handlers/__init__.py
Normal file
8
handlers/__init__.py
Normal file
@ -0,0 +1,8 @@
|
||||
from handlers.ban_words import router as rban_words
|
||||
from handlers.commands import router as rcommands
|
||||
from handlers.come_back import router as rcome_back
|
||||
from handlers.message import router as rmessage
|
||||
from handlers.group import router as rgroup
|
||||
from handlers.ban_media import router as rban_media
|
||||
|
||||
routers = [rcommands, rban_words, rcome_back, rmessage, rgroup, rban_media]
|
BIN
handlers/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
handlers/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
handlers/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
handlers/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
handlers/__pycache__/ban_media.cpython-311.pyc
Normal file
BIN
handlers/__pycache__/ban_media.cpython-311.pyc
Normal file
Binary file not shown.
BIN
handlers/__pycache__/ban_words.cpython-311.pyc
Normal file
BIN
handlers/__pycache__/ban_words.cpython-311.pyc
Normal file
Binary file not shown.
BIN
handlers/__pycache__/come_back.cpython-311.pyc
Normal file
BIN
handlers/__pycache__/come_back.cpython-311.pyc
Normal file
Binary file not shown.
BIN
handlers/__pycache__/commands.cpython-310.pyc
Normal file
BIN
handlers/__pycache__/commands.cpython-310.pyc
Normal file
Binary file not shown.
BIN
handlers/__pycache__/commands.cpython-311.pyc
Normal file
BIN
handlers/__pycache__/commands.cpython-311.pyc
Normal file
Binary file not shown.
BIN
handlers/__pycache__/group.cpython-311.pyc
Normal file
BIN
handlers/__pycache__/group.cpython-311.pyc
Normal file
Binary file not shown.
BIN
handlers/__pycache__/message.cpython-311.pyc
Normal file
BIN
handlers/__pycache__/message.cpython-311.pyc
Normal file
Binary file not shown.
66
handlers/ban_media.py
Normal file
66
handlers/ban_media.py
Normal file
@ -0,0 +1,66 @@
|
||||
from aiogram import Router, F
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.types import CallbackQuery
|
||||
|
||||
from templates import commands as tcommands
|
||||
from utils.db import Postgres, Redis
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith('enable_ban_media_'))
|
||||
async def enable_ban_media_btn(call: CallbackQuery, state: FSMContext):
|
||||
"""
|
||||
Ловит кнопку Включить
|
||||
:param call: CallbackQuery
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
state_data = await state.get_data()
|
||||
p = Postgres()
|
||||
r = Redis()
|
||||
|
||||
await p.update_data(
|
||||
table_name='ban_media',
|
||||
new_data={call.data[17:]: True},
|
||||
query_filter={}
|
||||
)
|
||||
await r.update_dict(
|
||||
key='ban_media',
|
||||
value={call.data[17:]: 'yes'}
|
||||
)
|
||||
print('new redis data', {call.data[17:]: 'yes'})
|
||||
|
||||
await state_data['last_msg'].edit_text(
|
||||
text=tcommands.start_text,
|
||||
reply_markup=await tcommands.start_ikb()
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith('disable_ban_media_'))
|
||||
async def disable_ban_media_btn(call: CallbackQuery, state: FSMContext):
|
||||
"""
|
||||
Ловит кнопку Выключить
|
||||
:param call: CallbackQuery
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
state_data = await state.get_data()
|
||||
p = Postgres()
|
||||
r = Redis()
|
||||
|
||||
await p.update_data(
|
||||
table_name='ban_media',
|
||||
new_data={call.data[18:]: False},
|
||||
query_filter={}
|
||||
)
|
||||
await r.update_dict(
|
||||
key='ban_media',
|
||||
value={call.data[18:]: ''}
|
||||
)
|
||||
print('new redis data', {call.data[18:]: ''})
|
||||
|
||||
await state_data['last_msg'].edit_text(
|
||||
text=tcommands.start_text,
|
||||
reply_markup=await tcommands.start_ikb()
|
||||
)
|
134
handlers/ban_words.py
Normal file
134
handlers/ban_words.py
Normal file
@ -0,0 +1,134 @@
|
||||
import logging
|
||||
|
||||
from aiogram import Router, F
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.types import CallbackQuery, FSInputFile, Message
|
||||
|
||||
from templates import ban_words as tban_words
|
||||
from templates import commands as tcommands
|
||||
from utils.defs import delete_msg, create_xlsx
|
||||
from utils.db import Redis, Postgres
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
@router.callback_query(F.data == 'ban_words')
|
||||
async def ban_words_btn(call: CallbackQuery, state: FSMContext):
|
||||
"""
|
||||
Ловит кнопку 🚫 Стоп слова
|
||||
:param call: CallbackQuery
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
state_data = await state.get_data()
|
||||
|
||||
document_path = await create_xlsx()
|
||||
|
||||
last_msg = await call.message.answer_document(
|
||||
document=FSInputFile(document_path),
|
||||
reply_markup=tban_words.actions_ikb()
|
||||
)
|
||||
|
||||
await delete_msg(
|
||||
msg=state_data.get('last_msg')
|
||||
)
|
||||
await state.update_data(
|
||||
last_msg=last_msg
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith('ban_words_action_'))
|
||||
async def ban_words_action_btn(call: CallbackQuery, state: FSMContext):
|
||||
"""
|
||||
Ловит кнопки ➕ Добавить ➖ Удалить
|
||||
:param call: CallbackQuery
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
state_data = await state.get_data()
|
||||
|
||||
last_msg = await call.message.answer(
|
||||
text=tban_words.send_words_text,
|
||||
reply_markup=tban_words.send_words_ikb()
|
||||
)
|
||||
|
||||
await state.update_data(
|
||||
last_msg=last_msg,
|
||||
ban_words_action=call.data[17:]
|
||||
)
|
||||
|
||||
await state.set_state(
|
||||
state=tban_words.SendState.words
|
||||
)
|
||||
|
||||
await delete_msg(
|
||||
msg=state_data.get('last_msg')
|
||||
)
|
||||
|
||||
|
||||
@router.message(tban_words.SendState.words, F.text)
|
||||
async def get_words(msg: Message, state: FSMContext):
|
||||
"""
|
||||
Ловит новое сообщение
|
||||
:param msg: Message
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
state_data = await state.get_data()
|
||||
words = msg.text.split('\n')
|
||||
|
||||
r = Redis()
|
||||
p = Postgres()
|
||||
|
||||
redis_ban_words = await r.get_list(
|
||||
key='ban_words'
|
||||
)
|
||||
|
||||
if state_data['ban_words_action'] == 'add':
|
||||
msg_text = tban_words.success_text.format(
|
||||
action='добавлены'
|
||||
)
|
||||
for word in words:
|
||||
await p.create_row(
|
||||
table_name='ban_words',
|
||||
data_to_insert={'word': word.lower()}
|
||||
)
|
||||
redis_ban_words.append(word.lower())
|
||||
|
||||
else:
|
||||
msg_text = tban_words.success_text.format(
|
||||
action='удалены'
|
||||
)
|
||||
not_deleted = []
|
||||
for word in words:
|
||||
try:
|
||||
await p.query(
|
||||
query_text=f"DELETE FROM ban_words WHERE LOWER(word)='{word.lower()}'"
|
||||
)
|
||||
redis_ban_words.remove(word.lower())
|
||||
except Exception as e:
|
||||
logging.error(f'get_words {e}')
|
||||
not_deleted.append(word)
|
||||
|
||||
if not_deleted:
|
||||
msg_text = tban_words.not_success_remove_text.format(
|
||||
words='\n'.join(not_deleted)
|
||||
)
|
||||
|
||||
await state_data['last_msg'].edit_text(
|
||||
text=msg_text,
|
||||
reply_markup=await tcommands.start_ikb()
|
||||
)
|
||||
await msg.delete()
|
||||
|
||||
await r.delete_key(
|
||||
'ban_words'
|
||||
)
|
||||
await r.update_list(
|
||||
'ban_words',
|
||||
*redis_ban_words
|
||||
)
|
||||
|
||||
await state.set_state(
|
||||
state=None
|
||||
)
|
115
handlers/come_back.py
Normal file
115
handlers/come_back.py
Normal file
@ -0,0 +1,115 @@
|
||||
from aiogram import Router, F
|
||||
from aiogram.types import CallbackQuery
|
||||
from aiogram.fsm.context import FSMContext
|
||||
|
||||
from handlers.ban_words import ban_words_btn
|
||||
from templates import message as tmessage
|
||||
from templates import commands as tcommands
|
||||
from handlers.message import message_btn
|
||||
from utils.defs import delete_msg
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
@router.callback_query(F.data == 'come_back_buttons')
|
||||
async def come_back_buttons_btn(call: CallbackQuery, state: FSMContext):
|
||||
"""
|
||||
Ловит кнопку ⬅️ Назад
|
||||
:param call: CallbackQuery
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
state_data = await state.get_data()
|
||||
|
||||
await state_data['last_msg'].edit_text(
|
||||
text=tmessage.send_buttons_text,
|
||||
reply_markup=tmessage.send_buttons_ikb()
|
||||
)
|
||||
|
||||
await delete_msg(
|
||||
msg=state_data.get('preview_msg')
|
||||
)
|
||||
|
||||
state_data['edited_message_data']['buttons'] = []
|
||||
await state.update_data(
|
||||
edited_message_data=state_data['edited_message_data']
|
||||
)
|
||||
|
||||
await state.set_state(
|
||||
state=tmessage.SendState.buttons
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data == 'come_back_message')
|
||||
async def come_back_message_btn(call: CallbackQuery, state: FSMContext):
|
||||
"""
|
||||
Ловит кнопку ⬅️ Назад
|
||||
:param call: CallbackQuery
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
state_data = await state.get_data()
|
||||
|
||||
await state_data['last_msg'].edit_text(
|
||||
text=tmessage.send_message_text,
|
||||
reply_markup=tmessage.send_message_ikb()
|
||||
)
|
||||
|
||||
await state.set_state(
|
||||
state=tmessage.SendState.message
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data == 'come_back_preview')
|
||||
async def come_back_preview_btn(call: CallbackQuery, state: FSMContext):
|
||||
"""
|
||||
Ловит кнопку ⬅️ Назад
|
||||
:param call: CallbackQuery
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
await message_btn(
|
||||
call=call,
|
||||
state=state
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data == 'come_back_menu')
|
||||
async def come_back_preview_btn(call: CallbackQuery, state: FSMContext):
|
||||
"""
|
||||
Ловит кнопку ⬅️ Назад
|
||||
:param call: CallbackQuery
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
state_data = await state.get_data()
|
||||
|
||||
last_msg = await call.message.answer(
|
||||
text=tcommands.start_text,
|
||||
reply_markup=await tcommands.start_ikb()
|
||||
)
|
||||
|
||||
await state.update_data(
|
||||
last_msg=last_msg
|
||||
)
|
||||
|
||||
await delete_msg(
|
||||
msg=state_data.get('preview_msg')
|
||||
)
|
||||
await delete_msg(
|
||||
msg=state_data.get('last_msg')
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data == 'come_back_ban_words')
|
||||
async def come_back_ban_words_btn(call: CallbackQuery, state: FSMContext):
|
||||
"""
|
||||
Ловит кнопку ⬅️ Назад
|
||||
:param call: CallbackQuery
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
await ban_words_btn(
|
||||
call=call,
|
||||
state=state
|
||||
)
|
39
handlers/commands.py
Normal file
39
handlers/commands.py
Normal file
@ -0,0 +1,39 @@
|
||||
from aiogram import Router
|
||||
from aiogram.types import Message, BotCommandScopeChat
|
||||
from aiogram.filters import Command
|
||||
from aiogram.fsm.context import FSMContext
|
||||
|
||||
from core import bot
|
||||
from templates import commands as tcommands
|
||||
from config import ADMINS, commands
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
@router.message(Command("start"))
|
||||
async def start_command(msg: Message, state: FSMContext):
|
||||
"""
|
||||
Ловит команду /start
|
||||
:param msg: Message
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
await msg.delete()
|
||||
if not msg.from_user.id in ADMINS:
|
||||
return
|
||||
|
||||
last_msg = await msg.answer(
|
||||
text=tcommands.start_text,
|
||||
reply_markup=await tcommands.start_ikb()
|
||||
)
|
||||
|
||||
await state.update_data(
|
||||
last_msg=last_msg
|
||||
)
|
||||
|
||||
await bot.set_my_commands(
|
||||
commands=commands,
|
||||
scope=BotCommandScopeChat(
|
||||
chat_id=msg.from_user.id
|
||||
)
|
||||
)
|
92
handlers/group.py
Normal file
92
handlers/group.py
Normal file
@ -0,0 +1,92 @@
|
||||
|
||||
from aiogram import Router, F
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.types import Message
|
||||
|
||||
from templates.message import send_preview
|
||||
from utils.db import Redis, Postgres
|
||||
from utils.defs import delete_msg
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
@router.message(F.chat.func(lambda chat: chat.type in ('group', 'supergroup')))
|
||||
async def get_all_messages(msg: Message, state: FSMContext):
|
||||
"""
|
||||
Ловит все сообщения в чате
|
||||
:param msg: Message
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
ban = False
|
||||
|
||||
msg_text = msg.text
|
||||
if not msg_text:
|
||||
msg_text = msg.caption
|
||||
|
||||
r = Redis()
|
||||
p = Postgres()
|
||||
|
||||
if msg_text:
|
||||
ban_words = await r.get_list(
|
||||
key='ban_words'
|
||||
)
|
||||
if not ban_words:
|
||||
ban_words = await p.get_data(
|
||||
table_name='ban_words'
|
||||
)
|
||||
ban_words = [word['word'] for word in ban_words]
|
||||
await r.delete_key(
|
||||
'ban_words'
|
||||
)
|
||||
await r.update_list(
|
||||
'ban_words',
|
||||
*ban_words
|
||||
)
|
||||
|
||||
for ban_word in ban_words:
|
||||
if ban_word in msg_text.lower():
|
||||
ban = True
|
||||
|
||||
else:
|
||||
ban_media = await r.get_dict(
|
||||
key='ban_media'
|
||||
)
|
||||
print(ban_media)
|
||||
if not ban_media.get('video') or ban_media.get('photo'):
|
||||
postgres_ban_media = await p.get_data(
|
||||
table_name='ban_media'
|
||||
)
|
||||
ban_media = {}
|
||||
for key, value in postgres_ban_media[0].items():
|
||||
ban_media[key] = 'yes' if value else ''
|
||||
|
||||
await r.update_dict(
|
||||
'ban_media',
|
||||
value=ban_media
|
||||
)
|
||||
|
||||
if ban_media.get('video') and msg.video:
|
||||
ban = True
|
||||
if ban_media.get('photo') and msg.photo:
|
||||
ban = True
|
||||
|
||||
if ban:
|
||||
await delete_msg(
|
||||
msg=msg
|
||||
)
|
||||
message_data = await p.get_data(
|
||||
table_name='message'
|
||||
)
|
||||
|
||||
if msg.from_user.username:
|
||||
username = f'@{msg.from_user.username}'
|
||||
else:
|
||||
username = msg.from_user.full_name
|
||||
|
||||
if message_data[0]['included']:
|
||||
await send_preview(
|
||||
chat_id=msg.chat.id,
|
||||
message_data=message_data[0],
|
||||
username=username
|
||||
)
|
252
handlers/message.py
Normal file
252
handlers/message.py
Normal file
@ -0,0 +1,252 @@
|
||||
import json
|
||||
|
||||
from aiogram import Router, F
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.types import CallbackQuery, Message
|
||||
|
||||
from templates import commands as tcommands
|
||||
from templates import message as tmessage
|
||||
from utils.db import Postgres
|
||||
from utils.defs import delete_msg
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
@router.callback_query(F.data == 'message')
|
||||
async def message_btn(call: CallbackQuery, state: FSMContext):
|
||||
"""
|
||||
Ловит кнопку 💬 Стоп сообщение
|
||||
:param call: CallbackQuery
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
state_data = await state.get_data()
|
||||
p = Postgres()
|
||||
message_data = await p.get_data(
|
||||
table_name='message'
|
||||
)
|
||||
|
||||
preview_msg = await tmessage.send_preview(
|
||||
message_data=message_data[0],
|
||||
chat_id=call.from_user.id
|
||||
)
|
||||
|
||||
last_msg = await call.message.answer(
|
||||
text=tmessage.actions_text.format(
|
||||
include='Да' if message_data[0]['included'] else 'Нет'
|
||||
),
|
||||
reply_markup=tmessage.actions_ikb(
|
||||
included=message_data[0]['included']
|
||||
)
|
||||
)
|
||||
await delete_msg(
|
||||
msg=state_data.get('last_msg')
|
||||
)
|
||||
await state.update_data(
|
||||
preview_msg=preview_msg,
|
||||
last_msg=last_msg
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data == 'edit_message')
|
||||
async def edit_message_btn(call: CallbackQuery, state: FSMContext):
|
||||
"""
|
||||
Ловит кнопку 📝 Редактировать
|
||||
:param call: CallbackQuery
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
state_data = await state.get_data()
|
||||
|
||||
await state_data['last_msg'].edit_text(
|
||||
text=tmessage.send_message_text,
|
||||
reply_markup=tmessage.send_message_ikb()
|
||||
)
|
||||
await state.set_state(
|
||||
state=tmessage.SendState.message
|
||||
)
|
||||
await delete_msg(
|
||||
msg=state_data.get('preview_msg')
|
||||
)
|
||||
|
||||
await state.update_data(
|
||||
edited_message_data={
|
||||
'text': '',
|
||||
'buttons': [],
|
||||
'media': ''
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@router.message(tmessage.SendState.message, F.content_type.in_({'text', 'photo'}))
|
||||
async def get_edit_message(msg: Message, state: FSMContext):
|
||||
"""
|
||||
Ловит новое сообщение
|
||||
:param msg: Message
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
state_data = await state.get_data()
|
||||
|
||||
await state_data['last_msg'].edit_text(
|
||||
text=tmessage.send_buttons_text,
|
||||
reply_markup=tmessage.send_buttons_ikb()
|
||||
)
|
||||
await msg.delete()
|
||||
|
||||
await state.set_state(
|
||||
state=tmessage.SendState.buttons
|
||||
)
|
||||
|
||||
if msg.photo:
|
||||
state_data['edited_message_data']['text'] = msg.caption
|
||||
state_data['edited_message_data']['media'] = msg.photo[-1].file_id
|
||||
else:
|
||||
state_data['edited_message_data']['text'] = msg.text
|
||||
state_data['edited_message_data']['media'] = None
|
||||
|
||||
await state.update_data(
|
||||
edited_message_data=state_data['edited_message_data']
|
||||
)
|
||||
|
||||
|
||||
@router.message(tmessage.SendState.buttons, F.text)
|
||||
async def get_edit_buttons(msg: Message, state: FSMContext):
|
||||
"""
|
||||
Ловит новое url кнопки к сообщению
|
||||
:param msg: Message
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
state_data = await state.get_data()
|
||||
buttons = tmessage.build_url_ikb(msg.text)
|
||||
|
||||
await msg.delete()
|
||||
|
||||
if not buttons:
|
||||
await state_data['last_msg'].edit_text(
|
||||
text=tmessage.incorrect_data_text + '\n\n' + tmessage.send_buttons_text,
|
||||
reply_markup=tmessage.send_buttons_ikb()
|
||||
)
|
||||
return
|
||||
|
||||
await delete_msg(
|
||||
msg=state_data.get('last_msg')
|
||||
)
|
||||
|
||||
state_data['edited_message_data']['buttons'] = buttons
|
||||
|
||||
preview_msg = await tmessage.send_preview(
|
||||
message_data=state_data['edited_message_data'],
|
||||
chat_id=msg.from_user.id
|
||||
)
|
||||
last_msg = await msg.answer(
|
||||
text=tmessage.check_data_text,
|
||||
reply_markup=tmessage.check_data_ikb()
|
||||
)
|
||||
|
||||
await state.update_data(
|
||||
edited_message_data=state_data['edited_message_data'],
|
||||
preview_msg=preview_msg,
|
||||
last_msg=last_msg
|
||||
)
|
||||
await state.set_state(
|
||||
state=None
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data == 'pass_buttons')
|
||||
async def pass_buttons_btn(call: CallbackQuery, state: FSMContext):
|
||||
"""
|
||||
Ловит кнопку ➡️ Пропустить
|
||||
:param call: CallbackQuery
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
state_data = await state.get_data()
|
||||
|
||||
await delete_msg(
|
||||
msg=state_data.get('last_msg')
|
||||
)
|
||||
|
||||
preview_msg = await tmessage.send_preview(
|
||||
message_data=state_data['edited_message_data'],
|
||||
chat_id=call.from_user.id
|
||||
)
|
||||
|
||||
last_msg = await call.message.answer(
|
||||
text=tmessage.check_data_text,
|
||||
reply_markup=tmessage.check_data_ikb()
|
||||
)
|
||||
|
||||
await state.update_data(
|
||||
preview_msg=preview_msg,
|
||||
last_msg=last_msg
|
||||
)
|
||||
|
||||
await state.set_state(
|
||||
state=None
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data == 'publish_message')
|
||||
async def publish_message_btn(call: CallbackQuery, state: FSMContext):
|
||||
"""
|
||||
Ловит кнопку ✅ Опубликовать
|
||||
:param call: CallbackQuery
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
state_data = await state.get_data()
|
||||
|
||||
state_data['edited_message_data']['buttons'] = json.dumps(
|
||||
state_data['edited_message_data']['buttons']
|
||||
)
|
||||
|
||||
p = Postgres()
|
||||
await p.update_data(
|
||||
table_name='message',
|
||||
new_data=state_data['edited_message_data'],
|
||||
query_filter={}
|
||||
)
|
||||
|
||||
await state_data['last_msg'].edit_text(
|
||||
text=tmessage.publish_message_text,
|
||||
reply_markup=await tcommands.start_ikb()
|
||||
)
|
||||
|
||||
await delete_msg(
|
||||
msg=state_data.get('preview_msg')
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data.startswith('included_message_'))
|
||||
async def included_message_btn(call: CallbackQuery, state: FSMContext):
|
||||
"""
|
||||
Ловит кнопку 📝 Редактировать
|
||||
:param call: CallbackQuery
|
||||
:param state: FSMContext
|
||||
:return:
|
||||
"""
|
||||
state_data = await state.get_data()
|
||||
|
||||
if call.data[17:] == 'true':
|
||||
included = True
|
||||
else:
|
||||
included = False
|
||||
|
||||
await state_data['last_msg'].edit_text(
|
||||
text=tmessage.actions_text.format(
|
||||
include='Да' if included else 'Нет'
|
||||
),
|
||||
reply_markup=tmessage.actions_ikb(
|
||||
included=included
|
||||
)
|
||||
)
|
||||
|
||||
p = Postgres()
|
||||
await p.update_data(
|
||||
table_name='message',
|
||||
new_data={'included': included},
|
||||
query_filter={}
|
||||
)
|
36
main.py
Normal file
36
main.py
Normal file
@ -0,0 +1,36 @@
|
||||
import asyncio
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from aiogram import Dispatcher
|
||||
|
||||
from core import bot
|
||||
from handlers import routers
|
||||
from utils.db import Postgres
|
||||
# from utils.middleware import DeleteMessage
|
||||
|
||||
dp = Dispatcher()
|
||||
dp.include_routers(*routers)
|
||||
# dp.update.middleware.register(
|
||||
# middleware=DeleteMessage()
|
||||
# )
|
||||
|
||||
|
||||
async def start():
|
||||
"""
|
||||
Запускает бота
|
||||
:return:
|
||||
"""
|
||||
p = Postgres()
|
||||
await p.create_tables()
|
||||
|
||||
await bot.delete_webhook()
|
||||
await dp.start_polling(bot)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
stream=sys.stdout
|
||||
)
|
||||
asyncio.run(start())
|
7
requirements.txt
Normal file
7
requirements.txt
Normal file
@ -0,0 +1,7 @@
|
||||
aiogram==3.6.0
|
||||
aiohttp==3.9.5
|
||||
python-dotenv==1.0.1
|
||||
openpyxl~=3.1.5
|
||||
pandas~=2.2.2
|
||||
redis~=5.0.7
|
||||
asyncpg~=0.29.0
|
0
templates/__init__.py
Normal file
0
templates/__init__.py
Normal file
BIN
templates/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
templates/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
templates/__pycache__/ban_words.cpython-311.pyc
Normal file
BIN
templates/__pycache__/ban_words.cpython-311.pyc
Normal file
Binary file not shown.
BIN
templates/__pycache__/commands.cpython-311.pyc
Normal file
BIN
templates/__pycache__/commands.cpython-311.pyc
Normal file
Binary file not shown.
BIN
templates/__pycache__/message.cpython-311.pyc
Normal file
BIN
templates/__pycache__/message.cpython-311.pyc
Normal file
Binary file not shown.
64
templates/ban_words.py
Normal file
64
templates/ban_words.py
Normal file
@ -0,0 +1,64 @@
|
||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
from aiogram.fsm.state import StatesGroup, State
|
||||
|
||||
|
||||
class SendState(StatesGroup):
|
||||
words = State()
|
||||
|
||||
|
||||
send_words_text = """
|
||||
Отправь список слов по одному или столбцом
|
||||
"""
|
||||
|
||||
success_text = """
|
||||
Слова {action}!
|
||||
"""
|
||||
|
||||
not_success_remove_text = """
|
||||
Что-то пошло не так. Не удалил слова:
|
||||
|
||||
{words}
|
||||
"""
|
||||
|
||||
|
||||
def send_words_ikb() -> InlineKeyboardMarkup:
|
||||
"""
|
||||
-⬅️ Назад
|
||||
:return: объект клавиатуры для параметра reply_markup
|
||||
"""
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.add(
|
||||
InlineKeyboardButton(
|
||||
text='⬅️ Назад',
|
||||
callback_data='come_back_ban_words'
|
||||
)
|
||||
)
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def actions_ikb() -> InlineKeyboardMarkup:
|
||||
"""
|
||||
-➕ Добавить
|
||||
-➖ Удалить
|
||||
-⬅️ Назад
|
||||
:return: объект клавиатуры для параметра reply_markup
|
||||
"""
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.add(
|
||||
InlineKeyboardButton(
|
||||
text='➕ Добавить',
|
||||
callback_data='ban_words_action_add'
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text='➖ Удалить',
|
||||
callback_data='ban_words_action_remove'
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text='⬅️ Назад',
|
||||
callback_data='come_back_menu'
|
||||
)
|
||||
)
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
62
templates/commands.py
Normal file
62
templates/commands.py
Normal file
@ -0,0 +1,62 @@
|
||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
|
||||
from utils.db import Postgres
|
||||
|
||||
start_text = """
|
||||
Привет, Админ!
|
||||
"""
|
||||
|
||||
|
||||
async def start_ikb() -> InlineKeyboardMarkup:
|
||||
"""
|
||||
-🚫 Стоп слова
|
||||
-💬 Стоп сообщение
|
||||
-Запретить / Разрешить видео без опис.
|
||||
-Запретить / Разрешить фото без опис.
|
||||
:return: объект клавиатуры для параметра reply_markup
|
||||
"""
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
ban_media_photo = {'text': '', 'callback_data': ''}
|
||||
ban_media_video = {'text': '', 'callback_data': ''}
|
||||
|
||||
ban_media = await Postgres().get_data(
|
||||
table_name='ban_media'
|
||||
)
|
||||
|
||||
if not ban_media[0]['photo']:
|
||||
ban_media_photo['text'] = 'Запретить фото без описания'
|
||||
ban_media_photo['callback_data'] = 'enable_ban_media_photo'
|
||||
else:
|
||||
ban_media_photo['text'] = 'Разрешить фото без описания'
|
||||
ban_media_photo['callback_data'] = 'disable_ban_media_photo'
|
||||
|
||||
if not ban_media[0]['video']:
|
||||
ban_media_video['text'] = 'Запретить видео без описания'
|
||||
ban_media_video['callback_data'] = 'enable_ban_media_video'
|
||||
else:
|
||||
ban_media_video['text'] = 'Разрешить видео без описания'
|
||||
ban_media_video['callback_data'] = 'disable_ban_media_video'
|
||||
|
||||
|
||||
builder.add(
|
||||
InlineKeyboardButton(
|
||||
text='🚫 Стоп слова',
|
||||
callback_data='ban_words'
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text='💬 Стоп сообщение',
|
||||
callback_data='message'
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text=ban_media_video['text'],
|
||||
callback_data=ban_media_video['callback_data']
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text=ban_media_photo['text'],
|
||||
callback_data=ban_media_photo['callback_data']
|
||||
)
|
||||
)
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
226
templates/message.py
Normal file
226
templates/message.py
Normal file
@ -0,0 +1,226 @@
|
||||
import json
|
||||
|
||||
from aiogram.fsm.state import StatesGroup, State
|
||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||
from aiogram.types import Message
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
|
||||
from core import bot
|
||||
|
||||
|
||||
class SendState(StatesGroup):
|
||||
message = State()
|
||||
buttons = State()
|
||||
|
||||
|
||||
actions_text = """
|
||||
Сообщение включено: <b>{include}</b>
|
||||
|
||||
Выберите действие:
|
||||
"""
|
||||
|
||||
send_message_text = """
|
||||
Отправьте сообщение с прикреплением до одного фото:
|
||||
"""
|
||||
|
||||
send_buttons_text = """
|
||||
Отправьте кнопки в таком формате:
|
||||
|
||||
<b>Кнопка в первом ряду - http://example.com</b>
|
||||
<b>Кнопка во втором ряду - http://example.com</b>
|
||||
|
||||
Используйте разделитель " | ", чтобы добавить до 8 кнопок в один ряд (допустимо 6 рядов):
|
||||
<b>Кнопка в ряду - http://example.com | Другая кнопка в ряду - http://example.com</b>
|
||||
"""
|
||||
|
||||
incorrect_data_text = """
|
||||
<b>Не верный формат данных</b>
|
||||
"""
|
||||
|
||||
check_data_text = """
|
||||
Проверьте введённые данные
|
||||
"""
|
||||
|
||||
publish_message_text = """
|
||||
Обуликовано успешно
|
||||
"""
|
||||
|
||||
|
||||
def check_data_ikb() -> InlineKeyboardMarkup:
|
||||
"""
|
||||
-✅ Опубликовать
|
||||
-⬅️ Назад
|
||||
:return: объект клавиатуры для параметра reply_markup
|
||||
"""
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.add(
|
||||
InlineKeyboardButton(
|
||||
text='✅ Опубликовать',
|
||||
callback_data='publish_message'
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text='⬅️ Назад',
|
||||
callback_data='come_back_buttons'
|
||||
)
|
||||
)
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
async def send_preview(message_data: dict, chat_id: int, username='') -> Message | None:
|
||||
"""
|
||||
Присылает превью сообщения
|
||||
:param message_data: Данные сообщения из бд
|
||||
:param chat_id: ID телеграм чата куда надо прислать превью
|
||||
:param username: Username пользователя
|
||||
:return:
|
||||
"""
|
||||
msg_text = message_data['text']
|
||||
if username:
|
||||
msg_text = f'{username}\n\n' + message_data['text']
|
||||
|
||||
if message_data['media']:
|
||||
preview_msg = await bot.send_photo(
|
||||
chat_id=chat_id,
|
||||
caption=msg_text,
|
||||
photo=message_data['media'],
|
||||
reply_markup=url_ikb(
|
||||
row_buttons=message_data['buttons']
|
||||
)
|
||||
)
|
||||
else:
|
||||
preview_msg = await bot.send_message(
|
||||
chat_id=chat_id,
|
||||
text=msg_text,
|
||||
reply_markup=url_ikb(
|
||||
row_buttons=message_data['buttons']
|
||||
)
|
||||
)
|
||||
|
||||
return preview_msg
|
||||
|
||||
|
||||
def send_buttons_ikb() -> InlineKeyboardMarkup:
|
||||
"""
|
||||
-➡️ Пропустить
|
||||
-⬅️ Назад
|
||||
:return: объект клавиатуры для параметра reply_markup
|
||||
"""
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.add(
|
||||
InlineKeyboardButton(
|
||||
text='➡️ Пропустить',
|
||||
callback_data='pass_buttons'
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text='⬅️ Назад',
|
||||
callback_data='come_back_message'
|
||||
)
|
||||
)
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def send_message_ikb() -> InlineKeyboardMarkup:
|
||||
"""
|
||||
-⬅️ Назад
|
||||
:return: объект клавиатуры для параметра reply_markup
|
||||
"""
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.add(
|
||||
InlineKeyboardButton(
|
||||
text='⬅️ Назад',
|
||||
callback_data='come_back_preview'
|
||||
)
|
||||
)
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def actions_ikb(included: bool) -> InlineKeyboardMarkup:
|
||||
"""
|
||||
-Вкл✅ \ Выкл ❌
|
||||
-📝 Редактировать
|
||||
-⬅️ Назад
|
||||
:param included: bool включено ли сообщение или нет
|
||||
:return: объект клавиатуры для параметра reply_markup
|
||||
"""
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
if included:
|
||||
included_btn = InlineKeyboardButton(
|
||||
text='Выкл ❌',
|
||||
callback_data='included_message_false'
|
||||
)
|
||||
else:
|
||||
included_btn = InlineKeyboardButton(
|
||||
text='Вкл ✅',
|
||||
callback_data='included_message_true'
|
||||
)
|
||||
|
||||
builder.add(
|
||||
included_btn,
|
||||
InlineKeyboardButton(
|
||||
text='📝 Редактировать',
|
||||
callback_data='edit_message'
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text='⬅️ Назад',
|
||||
callback_data='come_back_menu'
|
||||
)
|
||||
)
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def build_url_ikb(msg_text: str) -> list:
|
||||
"""
|
||||
Создаёт клавиатуру с url кнопками пользователя
|
||||
:param msg_text: Текст сообщения с заданными параметры клавиатуру
|
||||
:return: list с кнопками клавиатуры
|
||||
"""
|
||||
try:
|
||||
paragraphs = msg_text.split('\n')
|
||||
row_buttons = []
|
||||
for paragraph in paragraphs:
|
||||
row = []
|
||||
for button_text_data in paragraph.split(' | '):
|
||||
row.append(button_text_data.strip().split(' - '))
|
||||
row_buttons.append(row)
|
||||
|
||||
return row_buttons
|
||||
except:
|
||||
return []
|
||||
|
||||
|
||||
def url_ikb(row_buttons: list | str) -> InlineKeyboardMarkup | None:
|
||||
"""
|
||||
Создаёт клавиатуру с url кнопками пользователя
|
||||
:param row_buttons: List с кнопками клавиатуры
|
||||
:return: В случае ошибки False, если норм то объект клавиатуры для параметра reply_markup
|
||||
"""
|
||||
try:
|
||||
if isinstance(row_buttons, str):
|
||||
row_buttons = json.loads(row_buttons)
|
||||
|
||||
builder = InlineKeyboardBuilder()
|
||||
rows_len = [0, 0, 0, 0, 0, 0]
|
||||
i = 0
|
||||
|
||||
for row in row_buttons:
|
||||
rows_len[i] = len(row)
|
||||
for button in row:
|
||||
builder.add(
|
||||
InlineKeyboardButton(
|
||||
text=button[0],
|
||||
url=button[1]
|
||||
)
|
||||
)
|
||||
i += 1
|
||||
|
||||
builder.adjust(*rows_len)
|
||||
if row_buttons:
|
||||
return builder.as_markup()
|
||||
return None
|
||||
except:
|
||||
return None
|
458
templates/moderation.py
Normal file
458
templates/moderation.py
Normal file
@ -0,0 +1,458 @@
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
from aiogram.fsm.state import StatesGroup, State
|
||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, FSInputFile
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
|
||||
from config import FILES_PATH, ROLE_MODERATOR_ID, ROLE_CURATOR_ID
|
||||
from core import bot
|
||||
from utils.bitrix import notify
|
||||
from utils.db import get_data, query
|
||||
|
||||
|
||||
class ModerationState(StatesGroup):
|
||||
confirm = State()
|
||||
confirm_confirm = State()
|
||||
revision = State()
|
||||
grade = State()
|
||||
reject = State()
|
||||
check_revision_comment = State()
|
||||
check_reject_comment = State()
|
||||
date = State()
|
||||
|
||||
|
||||
new_idea_text = """
|
||||
{name}, пожалуйста, ознакомьтесь с новой НеоИдеей.
|
||||
"""
|
||||
|
||||
new_idea_data_text = """
|
||||
<b>Автор:</b> {author_name}
|
||||
<b>Дата:</b> {creation_date}
|
||||
<b>Подразделение:</b> {department}
|
||||
<b>Категория:</b> {category}
|
||||
<b>Город:</b> {city}
|
||||
<b>Название:</b> «{idea_name}»
|
||||
<b>Содержание:</b> {idea_content}
|
||||
"""
|
||||
|
||||
choose_action_text = """
|
||||
Пожалуйста, выберите одно из следующих действий:
|
||||
"""
|
||||
|
||||
confirm_action_text = """
|
||||
<b>Вы согласовываете НеоИдею:</b>
|
||||
«{idea_name}».
|
||||
|
||||
<b>Автор:</b> {author_name}
|
||||
"""
|
||||
|
||||
choose_responsible_text = """
|
||||
Пожалуйста, выберите <b>Ответственного за категорию</b> для передачи НеоИдеи в работу.
|
||||
"""
|
||||
|
||||
send_date_text = """
|
||||
В календаре укажите крайние сроки выполнения:
|
||||
"""
|
||||
|
||||
confirm_responsible_date_text = """
|
||||
Вы передаёте НеоИдею в работу.
|
||||
<b>Название:</b> «{idea_name}».
|
||||
|
||||
<b>Автор:</b> {author_name}
|
||||
<b>Ответственный за категорию:</b> {responsible_name}
|
||||
<b>Крайние сроки выполнения:</b> {date}
|
||||
"""
|
||||
|
||||
confirm_action_send_text = """
|
||||
Спасибо! Вы передали НеоИдею в работу.
|
||||
"""
|
||||
|
||||
revision_action_text = """
|
||||
<b>Вы возвращаете на доработку НеоИдею:</b>
|
||||
«{idea_name}».
|
||||
<b>Автор:</b> {author_name}
|
||||
|
||||
Оставьте комментарий для того, чтобы отправить НеоИдею на доработку.
|
||||
"""
|
||||
|
||||
send_revision_comment_text = """
|
||||
Оставьте комментарий для того, чтобы отправить НеоИдею на доработку
|
||||
"""
|
||||
|
||||
send_comment_text = """
|
||||
Пожалуйста, оставьте свой комментарий:
|
||||
"""
|
||||
|
||||
check_comment = """
|
||||
Проверьте правильность введенных данных:
|
||||
|
||||
{comment}
|
||||
"""
|
||||
|
||||
revision_action_send_text = """
|
||||
Вы отправили НеоИдею на доработку.
|
||||
"""
|
||||
|
||||
grade_action_text = """
|
||||
<b>Вы направляете НеоИдею на оценку:</b>
|
||||
«{idea_name}».
|
||||
<b>Автор:</b> {author_name}
|
||||
|
||||
НеоИдея будет передана Куратору.
|
||||
"""
|
||||
|
||||
grade_action_send_text = """
|
||||
Спасибо! НеоИдея передана Куратору.
|
||||
"""
|
||||
|
||||
reject_action_text = """
|
||||
<b>Вы отклоняете НеоИдею:</b>
|
||||
«{idea_name}».
|
||||
<b>Автор:</b> {author_name}
|
||||
|
||||
Пожалуйста, оставьте свой комментарий.
|
||||
"""
|
||||
|
||||
reject_action_send_text = """
|
||||
Спасибо! НеоИдея отклонена.
|
||||
"""
|
||||
|
||||
you_rejected = """
|
||||
Здравствуйте, {name}.
|
||||
|
||||
Ваша НеоИдея "{idea_name}" была отклонена с комментарием:
|
||||
"{comment}"
|
||||
"""
|
||||
|
||||
you_revision = """
|
||||
Ваша НеоИдея возвращена на доработку, пожалуйста проработайте комментарии от генерального директора и отправьте заявку заново.
|
||||
|
||||
НеоИдея: "{idea_name}"
|
||||
Комментарий: "{comment}"
|
||||
"""
|
||||
|
||||
accept_notify_author_text = """
|
||||
Поздравляем! Ваша НеоИдея передана к внедрению
|
||||
"""
|
||||
|
||||
accept_notify_curator_text = """
|
||||
Быстрая НеоИдея автора {author_name} передана к внедрению {responsible_name}
|
||||
"""
|
||||
|
||||
grade_curator_text = """
|
||||
Вам на оценку передана НеоИдея "{idea_name}".
|
||||
"""
|
||||
|
||||
confirm_notify_curator_text = """
|
||||
Быстрая НеоИдея автора {author_name} передана к ответственному за категорию {responsible_name}
|
||||
"""
|
||||
|
||||
confirm_notify_author_text = """
|
||||
Ваша НеоИдея передана ответственному за категорию
|
||||
"""
|
||||
|
||||
|
||||
def confirm_action_ikb() -> InlineKeyboardMarkup:
|
||||
"""
|
||||
-Да, продолжить ✅
|
||||
-Нет, вернуться назад ❌
|
||||
:return: объект клавиатуры для параметра reply_markup
|
||||
"""
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.add(
|
||||
InlineKeyboardButton(
|
||||
text='Да, продолжить ✅',
|
||||
callback_data='confirm_action_confirm'
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text='Нет, вернуться назад ❌',
|
||||
callback_data='confirm_action_reject'
|
||||
)
|
||||
)
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
def choose_action_ikb(idea_id: str | int, msg_id: str | int) -> InlineKeyboardMarkup:
|
||||
"""
|
||||
-Согласовать и передать в работу
|
||||
-Вернуть Автору на доработку
|
||||
-Направить на оценку
|
||||
-Отклонить
|
||||
:param idea_id: ID идеи
|
||||
:param msg_id: ID сообщения с клавиатурой
|
||||
:return: объект клавиатуры для параметра reply_markup
|
||||
"""
|
||||
builder = InlineKeyboardBuilder()
|
||||
builder.add(
|
||||
InlineKeyboardButton(
|
||||
text='Согласовать и передать в работу',
|
||||
callback_data=f'moderation_confirm_{idea_id}_{msg_id}'
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text='Вернуть Автору на доработку',
|
||||
callback_data=f'moderation_revision_{idea_id}_{msg_id}'
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text='Направить на оценку',
|
||||
callback_data=f'moderation_grade_{idea_id}_{msg_id}'
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text='Отклонить',
|
||||
callback_data=f'moderation_reject_{idea_id}_{msg_id}'
|
||||
)
|
||||
)
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
async def choose_responsible_ikb(category_id: int) -> InlineKeyboardMarkup:
|
||||
"""
|
||||
-Ответственный 1
|
||||
-Ответственный 2
|
||||
:param category_id: ID категории идеи
|
||||
:return: объект клавиатуры для параметра reply_markup
|
||||
"""
|
||||
builder = InlineKeyboardBuilder()
|
||||
responsible_list = await get_data(
|
||||
table_name='user_to_categories',
|
||||
query_filter={'category_id': category_id}
|
||||
)
|
||||
for responsible in responsible_list:
|
||||
responsible_data = await get_data(
|
||||
table_name='users',
|
||||
query_filter={'id': responsible['user_id']}
|
||||
)
|
||||
builder.add(
|
||||
InlineKeyboardButton(
|
||||
text=responsible_data[0]['full_name'],
|
||||
callback_data=f'choose_responsible_{responsible_data[0]["id"]}',
|
||||
)
|
||||
)
|
||||
builder.adjust(1)
|
||||
return builder.as_markup()
|
||||
|
||||
|
||||
async def send_new_idea_on_moderation(
|
||||
idea_id: int, user_name: str, department: str, idea_title: str,
|
||||
idea_content: str, relation_id: int, city_id: int, category_id: int, creation_date: datetime.datetime):
|
||||
"""
|
||||
Присылает новую идею модераторам из списка
|
||||
:param idea_id: Данные идеи
|
||||
:param user_name: Имя пользователя
|
||||
:param department: Отдел пользователя
|
||||
:param idea_title: Название идеи
|
||||
:param idea_content: Описание идеи
|
||||
:param relation_id: id отношения к идее
|
||||
:param city_id: id города, где работает пользователь
|
||||
:param category_id: id категории
|
||||
:param creation_date: Дата создания идеи
|
||||
:return:
|
||||
"""
|
||||
moderators = await get_data(
|
||||
table_name='user_to_roles',
|
||||
query_filter={'role_id': ROLE_MODERATOR_ID}
|
||||
)
|
||||
city = await get_data(
|
||||
table_name='cities',
|
||||
query_filter={'id': city_id}
|
||||
)
|
||||
files = await get_data(
|
||||
table_name='idea_to_files',
|
||||
query_filter={'idea_id': idea_id}
|
||||
)
|
||||
category = await get_data(
|
||||
table_name='categories',
|
||||
query_filter={'id': category_id}
|
||||
)
|
||||
notify_text = new_idea_data_text.format(
|
||||
author_name=user_name,
|
||||
creation_date=creation_date,
|
||||
department=department,
|
||||
idea_name=idea_title,
|
||||
idea_content=idea_content,
|
||||
city=city[0]['name'],
|
||||
category=category[0]['name']
|
||||
)
|
||||
|
||||
for moderator_id in moderators:
|
||||
try:
|
||||
moderator = await get_data(
|
||||
table_name='users',
|
||||
query_filter={'id': moderator_id['user_id']}
|
||||
)
|
||||
formatted_new_idea_text = new_idea_text.format(
|
||||
name=moderator[0]['full_name'],
|
||||
)
|
||||
await bot.send_message(
|
||||
chat_id=moderator[0]['telegram_id'],
|
||||
text=formatted_new_idea_text
|
||||
)
|
||||
await bot.send_message(
|
||||
chat_id=moderator[0]['telegram_id'],
|
||||
text=notify_text
|
||||
)
|
||||
for file in files:
|
||||
file_data = await get_data(
|
||||
table_name='files',
|
||||
query_filter={'id': file['file_id']}
|
||||
)
|
||||
await bot.send_document(
|
||||
chat_id=moderator[0]['telegram_id'],
|
||||
document=FSInputFile(FILES_PATH + file_data[0]['file'])
|
||||
)
|
||||
last_msg = await bot.send_message(
|
||||
chat_id=moderator[0]['telegram_id'],
|
||||
text=choose_action_text,
|
||||
reply_markup=choose_action_ikb(
|
||||
idea_id=idea_id,
|
||||
msg_id=0
|
||||
)
|
||||
)
|
||||
await bot.edit_message_reply_markup(
|
||||
message_id=last_msg.message_id,
|
||||
chat_id=moderator[0]['telegram_id'],
|
||||
reply_markup=choose_action_ikb(
|
||||
idea_id=idea_id,
|
||||
msg_id=last_msg.message_id
|
||||
)
|
||||
)
|
||||
moderator_user_data = await get_data(
|
||||
table_name='users',
|
||||
query_filter={'id': moderator_id['user_id']}
|
||||
)
|
||||
await notify(
|
||||
user_id=moderator_user_data[0]['external_id'],
|
||||
message='Новая быстрая НеоИдея!'
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
|
||||
curators = await get_data(
|
||||
table_name='user_to_roles',
|
||||
query_filter={'role_id': ROLE_CURATOR_ID}
|
||||
)
|
||||
for curator in curators:
|
||||
try:
|
||||
user_data = await get_data(
|
||||
table_name='users',
|
||||
query_filter={'id': curator['user_id']}
|
||||
)
|
||||
await notify(
|
||||
user_id=user_data[0]['external_id'],
|
||||
message='Новая быстрая НеоИдея!'
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error(f'не могу отправить уведомление куратору о новой идее {e}')
|
||||
|
||||
|
||||
async def accept_notify(author_name: str, responsible_id: str, author_id: int) -> None:
|
||||
"""
|
||||
Уведомляет куратора и автора о принятии идеи
|
||||
:param responsible_id: ID кому передают идею
|
||||
:param author_name: Имя автора идеи
|
||||
:param author_id: ID автора
|
||||
:return:
|
||||
"""
|
||||
responsible_data = await get_data(
|
||||
table_name='users',
|
||||
query_filter={'id': responsible_id}
|
||||
)
|
||||
notify_text = notify_curator_text.format(
|
||||
author_name=author_name,
|
||||
responsible_name=responsible_data[0]['full_name']
|
||||
)
|
||||
curators = await get_data(
|
||||
table_name='user_to_roles',
|
||||
query_filter={'role_id': ROLE_CURATOR_ID}
|
||||
)
|
||||
|
||||
for curator in curators:
|
||||
try:
|
||||
user_data = await get_data(
|
||||
table_name='users',
|
||||
query_filter={'id': curator['user_id']}
|
||||
)
|
||||
await notify(
|
||||
user_id=user_data[0]['external_id'],
|
||||
message=notify_text
|
||||
)
|
||||
await bot.send_message(
|
||||
chat_id=user_data[0]['telegram_id'],
|
||||
text=notify_text
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error(f'не могу отправить уведомление куратору о принятии идеи {e}')
|
||||
|
||||
try:
|
||||
author_data = await get_data(
|
||||
table_name='users',
|
||||
query_filter={'id': author_id}
|
||||
)
|
||||
await notify(
|
||||
user_id=author_data[0]['external_id'],
|
||||
message=notify_author_text
|
||||
)
|
||||
await bot.send_message(
|
||||
chat_id=author_data[0]['telegram_id'],
|
||||
text=notify_author_text
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error(f'Не смог отправить уведомление автору идеи {e}')
|
||||
|
||||
|
||||
async def confirm_notify(author_name: str, responsible_id: str, author_id: int) -> None:
|
||||
"""
|
||||
Уведомляет куратора и автора о согласовании идеи
|
||||
:param responsible_id: ID кому передают идею
|
||||
:param author_name: Имя автора идеи
|
||||
:param author_id: ID автора
|
||||
:return:
|
||||
"""
|
||||
responsible_data = await get_data(
|
||||
table_name='users',
|
||||
query_filter={'id': responsible_id}
|
||||
)
|
||||
notify_text = confirm_notify_curator_text.format(
|
||||
author_name=author_name,
|
||||
responsible_name=responsible_data[0]['full_name']
|
||||
)
|
||||
curators = await get_data(
|
||||
table_name='user_to_roles',
|
||||
query_filter={'role_id': ROLE_CURATOR_ID}
|
||||
)
|
||||
|
||||
for curator in curators:
|
||||
try:
|
||||
user_data = await get_data(
|
||||
table_name='users',
|
||||
query_filter={'id': curator['user_id']}
|
||||
)
|
||||
await notify(
|
||||
user_id=user_data[0]['external_id'],
|
||||
message=notify_text
|
||||
)
|
||||
await bot.send_message(
|
||||
chat_id=user_data[0]['telegram_id'],
|
||||
text=notify_text
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error(f'не могу отправить уведомление куратору о принятии идеи {e}')
|
||||
|
||||
try:
|
||||
author_data = await get_data(
|
||||
table_name='users',
|
||||
query_filter={'id': author_id}
|
||||
)
|
||||
await notify(
|
||||
user_id=author_data[0]['external_id'],
|
||||
message=notify_author_text
|
||||
)
|
||||
await bot.send_message(
|
||||
chat_id=author_data[0]['telegram_id'],
|
||||
text=notify_author_text
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error(f'Не смог отправить уведомление автору идеи {e}')
|
0
utils/__init__.py
Normal file
0
utils/__init__.py
Normal file
BIN
utils/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
utils/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
utils/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/db.cpython-311.pyc
Normal file
BIN
utils/__pycache__/db.cpython-311.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/defs.cpython-311.pyc
Normal file
BIN
utils/__pycache__/defs.cpython-311.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/middleware.cpython-311.pyc
Normal file
BIN
utils/__pycache__/middleware.cpython-311.pyc
Normal file
Binary file not shown.
250
utils/db.py
Normal file
250
utils/db.py
Normal file
@ -0,0 +1,250 @@
|
||||
import logging
|
||||
|
||||
import asyncpg
|
||||
import redis.asyncio as redis
|
||||
|
||||
from config import POSTGRES_NAME, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_HOST, POSTGRES_PORT, REDIS_NAME, \
|
||||
REDIS_HOST, REDIS_PORT, REDIS_PASSWORD
|
||||
|
||||
create_tables_query = """
|
||||
CREATE TABLE IF NOT EXISTS ban_words
|
||||
(id SERIAL PRIMARY KEY,
|
||||
word TEXT NOT NULL);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS message
|
||||
(id SERIAL PRIMARY KEY,
|
||||
text TEXT NOT NULL,
|
||||
media TEXT,
|
||||
buttons TEXT NOT NULL,
|
||||
included BOOL NOT NULL);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS ban_media
|
||||
(id SERIAL PRIMARY KEY,
|
||||
video BOOL NOT NULL,
|
||||
photo BOOL NOT NULL);
|
||||
"""
|
||||
|
||||
exist_query = """
|
||||
SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $1)
|
||||
"""
|
||||
|
||||
|
||||
class Postgres:
|
||||
def __init__(self):
|
||||
self.conn = None
|
||||
|
||||
async def connect(self):
|
||||
self.conn = await asyncpg.connect(
|
||||
database=POSTGRES_NAME,
|
||||
user=POSTGRES_USER,
|
||||
password=POSTGRES_PASSWORD,
|
||||
host=POSTGRES_HOST,
|
||||
port=POSTGRES_PORT,
|
||||
)
|
||||
|
||||
async def create_tables(self) -> None:
|
||||
"""
|
||||
Создаёт таблицы если их нет
|
||||
:return:
|
||||
"""
|
||||
await self.connect()
|
||||
try:
|
||||
async with self.conn.transaction():
|
||||
message_exist = await self.conn.fetchval(
|
||||
exist_query, 'message'
|
||||
)
|
||||
ban_media_exist = await self.conn.fetchval(
|
||||
exist_query, 'ban_media'
|
||||
)
|
||||
await self.conn.execute(
|
||||
create_tables_query
|
||||
)
|
||||
|
||||
if not message_exist:
|
||||
await self.create_row(
|
||||
table_name='message',
|
||||
data_to_insert={
|
||||
'text': 'Сообщение',
|
||||
'media': '',
|
||||
'buttons': '',
|
||||
'included': False
|
||||
}
|
||||
)
|
||||
|
||||
if not ban_media_exist:
|
||||
await self.create_row(
|
||||
table_name='ban_media',
|
||||
data_to_insert={
|
||||
'photo': True,
|
||||
'video': True,
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f'Ошибка в create_tables {e}')
|
||||
finally:
|
||||
await self.conn.close()
|
||||
|
||||
async def get_data(self, table_name: str, columns='*', query_filter=None) -> list:
|
||||
"""
|
||||
Получить данные нужной таблицы по указанным фильтрам
|
||||
:param columns: Название колонн с нужными данными
|
||||
:param query_filter: Фильтры запроса в формате {колонка: её значение}
|
||||
:param table_name: Название таблицы для запроса
|
||||
:return: False в случае ошибки, словарь с данными в случае успеха
|
||||
"""
|
||||
if query_filter is None:
|
||||
query_filter = {}
|
||||
await self.connect()
|
||||
try:
|
||||
if isinstance(columns, str):
|
||||
columns = [columns]
|
||||
full_query = f"SELECT {','.join(columns)} FROM {table_name}"
|
||||
|
||||
if query_filter:
|
||||
query_filter = ' AND '.join(
|
||||
[f"{key} = '{value}'" for key, value in query_filter.items()]
|
||||
)
|
||||
full_query += f' WHERE {query_filter}'
|
||||
|
||||
async with self.conn.transaction():
|
||||
result = await self.conn.fetch(full_query)
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f'Ошибка в get_data {e}')
|
||||
finally:
|
||||
await self.conn.close()
|
||||
return []
|
||||
|
||||
async def update_data(self, new_data: dict, query_filter: dict, table_name: str) -> bool:
|
||||
"""
|
||||
Обновляет данные по заданным фильтрам
|
||||
:param new_data: Новые данные в формате {Колонка: новое значение}
|
||||
:param query_filter: Фильтры запроса в формате {колонка: её значение}
|
||||
:param table_name: Название таблицы для запроса
|
||||
:return: True в случае успеха, False в случае ошибки
|
||||
"""
|
||||
await self.connect()
|
||||
try:
|
||||
dollar_data = {key: f"${i + 1}" for i, key in enumerate(new_data)}
|
||||
values = ', '.join(f'{key} = {value}' for key, value in dollar_data.items())
|
||||
full_query = f"UPDATE {table_name} SET {values}"
|
||||
|
||||
if query_filter:
|
||||
query_filter = ' AND '.join([f"{key} = '{value}'" for key, value in query_filter.items()])
|
||||
full_query += f' WHERE {query_filter}'
|
||||
|
||||
async with self.conn.transaction():
|
||||
await self.conn.execute(full_query, *new_data.values())
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f'Ошибка в update_data {e}')
|
||||
return False
|
||||
finally:
|
||||
await self.conn.close()
|
||||
|
||||
async def create_row(self, data_to_insert: dict, table_name: str) -> bool:
|
||||
"""
|
||||
Создаёт новую строку с данными
|
||||
:param data_to_insert: Список, где ключ - название столбика, значение - значение столбика в новой строчке
|
||||
:param table_name: Название таблицы, куда вставляем данные
|
||||
:return: id последней вставленной строки
|
||||
"""
|
||||
await self.connect()
|
||||
try:
|
||||
dollars = [f"${i + 1}" for i in range(len(data_to_insert))]
|
||||
full_query = f"INSERT INTO {table_name} ({', '.join(data_to_insert.keys())}) VALUES ({', '.join(dollars)})"
|
||||
async with self.conn.transaction():
|
||||
await self.conn.execute(full_query, *data_to_insert.values())
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f'Ошибка в create_row {e}')
|
||||
return False
|
||||
finally:
|
||||
await self.conn.close()
|
||||
|
||||
async def query(self, query_text: str):
|
||||
"""
|
||||
Прямой запрос к бд
|
||||
:param query_text: sql запрос
|
||||
:return: Результат sql запроса
|
||||
"""
|
||||
await self.connect()
|
||||
try:
|
||||
async with self.conn.transaction():
|
||||
await self.conn.execute(query_text)
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f'Ошибка в query {e}')
|
||||
finally:
|
||||
await self.conn.close()
|
||||
|
||||
|
||||
class Redis:
|
||||
def __init__(self):
|
||||
self.conn = None
|
||||
|
||||
async def connect(self):
|
||||
try:
|
||||
self.conn = await redis.Redis(
|
||||
host=REDIS_HOST,
|
||||
port=REDIS_PORT,
|
||||
db=REDIS_NAME,
|
||||
password=REDIS_PASSWORD,
|
||||
decode_responses=True,
|
||||
encoding='utf-8'
|
||||
)
|
||||
except Exception as e:
|
||||
logging.error('redis connect', e)
|
||||
|
||||
async def delete_key(self, *keys: str | int) -> str | int:
|
||||
await self.connect()
|
||||
try:
|
||||
return await self.conn.delete(*keys)
|
||||
except Exception as e:
|
||||
logging.error('redis delete_key', e)
|
||||
finally:
|
||||
await self.conn.close()
|
||||
|
||||
async def update_list(self, key: str | int, *values) -> str | int:
|
||||
await self.connect()
|
||||
try:
|
||||
return await self.conn.rpush(key, *values)
|
||||
except Exception as e:
|
||||
logging.error('redis update_data', e)
|
||||
finally:
|
||||
await self.conn.close()
|
||||
|
||||
async def get_list(self, key: str | int) -> list:
|
||||
await self.connect()
|
||||
try:
|
||||
data = await self.conn.lrange(name=str(key), start=0, end=-1)
|
||||
return data
|
||||
except Exception as e:
|
||||
logging.error('redis get_data', e)
|
||||
return []
|
||||
finally:
|
||||
await self.conn.close()
|
||||
|
||||
async def update_dict(self, key: str | int, value: dict) -> str | int:
|
||||
await self.connect()
|
||||
try:
|
||||
return await self.conn.hset(name=str(key), mapping=value)
|
||||
except Exception as e:
|
||||
logging.error('redis update', e)
|
||||
finally:
|
||||
await self.conn.close()
|
||||
|
||||
async def get_dict(self, key: str | int) -> dict:
|
||||
await self.connect()
|
||||
try:
|
||||
data = await self.conn.hgetall(name=str(key))
|
||||
return data
|
||||
except Exception as e:
|
||||
logging.error('redis get', e)
|
||||
return []
|
||||
finally:
|
||||
await self.conn.close()
|
44
utils/defs.py
Normal file
44
utils/defs.py
Normal file
@ -0,0 +1,44 @@
|
||||
import logging
|
||||
|
||||
from aiogram.types import Message
|
||||
import pandas
|
||||
|
||||
from utils.db import Postgres
|
||||
|
||||
|
||||
async def delete_msg(msg: Message) -> None:
|
||||
"""
|
||||
Безопасно удаляет сообщение
|
||||
:param msg: Message
|
||||
:return: True, если текст является ссылкой, иначе False.
|
||||
"""
|
||||
try:
|
||||
await msg.delete()
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
async def create_xlsx() -> str:
|
||||
"""
|
||||
Составляет xlsx файл с данными бан слова
|
||||
:return: Путь к готовому xlsx файлу
|
||||
"""
|
||||
try:
|
||||
p = Postgres()
|
||||
ban_words = await p.get_data(
|
||||
table_name='ban_words'
|
||||
)
|
||||
table_dict = {'ID': [], 'Слово': []}
|
||||
|
||||
for ban_word in ban_words:
|
||||
table_dict['ID'].append(ban_word['id'])
|
||||
table_dict['Слово'].append(ban_word['word'])
|
||||
|
||||
df = pandas.DataFrame(table_dict)
|
||||
file_path = 'data/ban_words.xlsx'
|
||||
df.to_excel(file_path, index=False)
|
||||
|
||||
return file_path
|
||||
|
||||
except Exception as e:
|
||||
logging.error('Ошибка в create_xlsx', e)
|
73
utils/middleware.py
Normal file
73
utils/middleware.py
Normal file
@ -0,0 +1,73 @@
|
||||
# from aiogram import BaseMiddleware
|
||||
# from aiogram.types import TelegramObject, Update
|
||||
#
|
||||
# from typing import Callable, Dict, Any, Awaitable
|
||||
# from utils.defs import delete_msg
|
||||
# from utils.db import Postgres, Redis
|
||||
# from templates.message import send_preview
|
||||
#
|
||||
#
|
||||
# class DeleteMessage(BaseMiddleware):
|
||||
# """
|
||||
# Мидлвари удаляющая сообщения с бан вордами
|
||||
# """
|
||||
#
|
||||
# async def __call__(
|
||||
# self,
|
||||
# handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
|
||||
# event: Update,
|
||||
# data: Dict[str, Any]
|
||||
# ) -> Any:
|
||||
# if not event.message:
|
||||
# return await handler(event, data)
|
||||
# if not (event.message.chat.type == 'group' or
|
||||
# event.message.chat.type == 'supergroup'):
|
||||
# return await handler(event, data)
|
||||
#
|
||||
# ban = False
|
||||
# r = Redis()
|
||||
# p = Postgres()
|
||||
#
|
||||
# ban_words = await r.get_list(
|
||||
# key='ban_words'
|
||||
# )
|
||||
# if not ban_words:
|
||||
# ban_words = await p.get_data(
|
||||
# table_name='ban_words'
|
||||
# )
|
||||
# ban_words = [word['word'] for word in ban_words]
|
||||
# await r.delete_key(
|
||||
# 'ban_words'
|
||||
# )
|
||||
# await r.update_list(
|
||||
# 'ban_words',
|
||||
# *ban_words
|
||||
# )
|
||||
#
|
||||
# for ban_word in ban_words:
|
||||
# print(ban_word)
|
||||
# if ban_word in event.message.text.lower():
|
||||
# print(event.message.text.lower(), 'нашёл')
|
||||
# ban = True
|
||||
#
|
||||
# if ban:
|
||||
# await delete_msg(
|
||||
# msg=event.message
|
||||
# )
|
||||
# message_data = await p.get_data(
|
||||
# table_name='message'
|
||||
# )
|
||||
#
|
||||
# if event.message.from_user.username:
|
||||
# username = f'@{event.message.from_user.username}'
|
||||
# else:
|
||||
# username = event.message.from_user.full_name
|
||||
#
|
||||
# if message_data[0]['included']:
|
||||
# await send_preview(
|
||||
# chat_id=event.message.chat.id,
|
||||
# message_data=message_data[0],
|
||||
# username=username
|
||||
# )
|
||||
#
|
||||
# return await handler(event, data)
|
Loading…
Reference in New Issue
Block a user