version-0.2.0

This commit is contained in:
Untone 2024-01-06 14:25:35 +03:00
parent bc05b93c47
commit 908529b5bb
26 changed files with 185 additions and 153 deletions

4
.gitignore vendored
View File

@ -7,4 +7,6 @@ update.json
.vscode .vscode
.idea .idea
venv venv
*.rdb *.rdb
.venv
poetry.lock

View File

@ -1,3 +1,11 @@
## [0.2.0]
- реорганизация кодовой базы
- удаление зависимости от aiogram
- удаление остатков зависимостей от vercel
- переход на poetry
## [0.0.12] ## [0.0.12]
- множество исправлений в роутинге сообщений - множество исправлений в роутинге сообщений

View File

@ -1,6 +1,14 @@
FROM python:3.11-slim FROM python:slim
WORKDIR /app WORKDIR /app
COPY requirements.txt . COPY . /app
RUN pip install -r requirements.txt
COPY . . RUN apt-get update && \
CMD ["python", "bot/main.py"] apt-get install -y git curl && \
curl -sSL https://install.python-poetry.org | python - && \
echo "export PATH=$PATH:/root/.local/bin" >> ~/.bashrc && \
. ~/.bashrc && \
poetry config virtualenvs.create false && \
poetry install --no-dev
CMD python main.py

View File

@ -2,14 +2,22 @@
Для переиспользования этого кода в требуется Для переиспользования этого кода в требуется
1. Cоздать аккаунт бота с помощью @BotFather 1. Cоздать аккаунт бота с помощью `@BotFather`
2. Аккаунт и деплой кода на vercel.com 2. Назначить бота администратором группы в его профиле, имея права назначать администраторов в группе
3. Назначить бота администратором группы в его профиле, имея права назначать администраторов в группе 3. Настроить переменные среды:
4. Настроить переменные среды: - `BOT_TOKEN` - токен бота созданный с помощью @BotFather
- `FEEDBACK_CHAT_ID` - айди чата для обратной связи
- `REDIS_URL`
- BOT_TOKEN - токен бота созданный с помощью @BotFather ### Локальная разработка
- FEEDBACK_CHAT_ID - айди чата для обратной связи
- REDIS_URL ```sh
mkdir .venv
python3.12 -m venv .venv
poetry env use .venv/bin/python3.12
poetry update
poetry run python main.py
```

View File

@ -1,62 +0,0 @@
from bot.handlers.routing import handle_routing
from bot.handlers.callback_vouch import handle_button
from bot.handlers.callback_unlink import handle_unlink
from bot.handlers.handle_startup import handle_startup
from bot.handlers.handle_join_request import handle_join_request
from bot.api import register_webhook, send_message
from bot.config import FEEDBACK_CHAT_ID, WEBHOOK
from bot.state import State
from sanic.app import Sanic
from sanic.response import text
import logging
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
app = Sanic(name="welcomecenter")
app.config.LOGGING = True
app.config.REGISTERED = False
state = State()
@app.get("/")
async def register(req):
if not app.config.REGISTERED:
logger.info(register_webhook())
app.config.REGISTERED = True
await handle_startup()
return text("ok")
@app.post("/")
async def handle(req):
logger.debug(req)
try:
update = req.json
logger.debug(update)
# видимые сообщения
msg = update.get("message", update.get("edited_message"))
if msg:
msg["edit"] = "edited_message" in update
await handle_routing(msg)
# кнопки
elif "callback_query" in update:
data = update["callback_query"]["data"]
if data.startswith("vouch"):
await handle_button(update["callback_query"])
elif data.startswith("unlink"):
await handle_unlink(update["callback_query"])
# заявки
elif "chat_join_request" in update:
logger.info("chat join request")
await handle_join_request(update["chat_join_request"])
except Exception:
import traceback
await send_message(FEEDBACK_CHAT_ID, f"<pre>\n{traceback.format_exc()}\n</pre>")
traceback.print_exc()
logger.error(traceback.format_exc())
return text("ok")

View File

@ -1,6 +1,6 @@
import aiohttp import aiohttp
import json import json
from config import BOT_TOKEN, WEBHOOK from bot.config import BOT_TOKEN, WEBHOOK
import logging import logging
# Create a logger instance # Create a logger instance

View File

@ -1,9 +1,10 @@
from api import send_message, delete_message, kick_member from bot.api import send_message, delete_message, kick_member
from handlers.command_my import handle_command_my from handlers.command_my import handle_command_my
from handlers.callback_vouch import update_button from handlers.callback_vouch import update_button
from utils.mention import userdata_extract from utils.mention import userdata_extract
from storage import Profile from storage import Profile
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)

View File

@ -1,6 +1,7 @@
from api import approve_chat_join_request, edit_replymarkup from bot.api import approve_chat_join_request, edit_replymarkup
from storage import Profile, storage from storage import Profile, storage
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)

View File

@ -1,6 +1,6 @@
from storage import Profile from storage import Profile
from handlers.send_button import show_request_msg from handlers.send_button import show_request_msg
from api import get_member from bot.api import get_member
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)

View File

@ -1,5 +1,5 @@
from utils.graph import generate_chart from utils.graph import generate_chart
from api import send_document from bot.api import send_document
from storage import storage, scan from storage import storage, scan
import json import json

View File

@ -1,5 +1,5 @@
from storage import Profile, scan from storage import Profile, scan
from api import get_member, send_message from bot.api import get_member, send_message
from utils.mention import userdata_extract from utils.mention import userdata_extract
import json import json
import logging import logging

View File

@ -1,4 +1,4 @@
from api import send_message, delete_message, get_chat_administrators from bot.api import send_message, delete_message, get_chat_administrators
from handlers.command_my import handle_command_my from handlers.command_my import handle_command_my
from storage import Profile, storage from storage import Profile, storage
from handlers.send_button import show_request_msg from handlers.send_button import show_request_msg

View File

@ -1,13 +1,15 @@
import json import json
from api import ( from bot.api import (
send_message, send_message,
forward_message, forward_message,
get_chat_administrators, get_chat_administrators,
) )
from storage import storage from storage import storage
from config import FEEDBACK_CHAT_ID from bot.config import FEEDBACK_CHAT_ID
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)

View File

@ -1,4 +1,4 @@
from api import approve_chat_join_request, delete_message from bot.api import approve_chat_join_request, delete_message
from handlers.send_button import show_request_msg from handlers.send_button import show_request_msg
from storage import Profile, storage from storage import Profile, storage

View File

@ -1,7 +1,7 @@
from handlers.send_button import show_request_msg from handlers.send_button import show_request_msg
from api import delete_message from bot.api import delete_message
from storage import Profile, storage from storage import Profile, storage
from config import FEEDBACK_CHAT_ID from bot.config import FEEDBACK_CHAT_ID
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -1,10 +1,11 @@
from config import FEEDBACK_CHAT_ID from bot.config import FEEDBACK_CHAT_ID
from storage import scan, Profile from storage import scan, Profile
from api import approve_chat_join_request, kick_member, send_message from bot.api import approve_chat_join_request, kick_member, send_message
from handlers.callback_vouch import update_button from handlers.callback_vouch import update_button
from utils.mention import userdata_extract from utils.mention import userdata_extract
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)

View File

@ -3,7 +3,7 @@ from handlers.handle_default import handle_default
from handlers.command_my import handle_command_my from handlers.command_my import handle_command_my
from handlers.command_graph import handle_command_graph from handlers.command_graph import handle_command_graph
from handlers.command_ask import handle_command_ask from handlers.command_ask import handle_command_ask
from config import FEEDBACK_CHAT_ID from bot.config import FEEDBACK_CHAT_ID
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -1,7 +1,10 @@
from api import send_message, send_photo, get_userphotos, delete_message from bot.api import send_message, send_photo, get_userphotos, delete_message
from utils.mention import mention, userdata_extract from utils.mention import mention, userdata_extract
from storage import storage from storage import storage
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)

View File

@ -1,38 +1,30 @@
import asyncio import asyncio
import logging import logging
import signal
import sys import sys
import signal # Import the signal module from aiohttp import web
from aiogram import Bot, Dispatcher, Router from bot.api import send_message
from aiogram.enums import ParseMode
from aiogram.filters import CommandStart
from aiogram.types import Message, ChatJoinRequest, CallbackQuery, ChatMemberUpdated
from aiogram.enums import ChatMemberStatus
from config import BOT_TOKEN, FEEDBACK_CHAT_ID
from handlers.routing import handle_routing from handlers.routing import handle_routing
from handlers.callback_unlink import handle_unlink from handlers.callback_unlink import handle_unlink
from handlers.callback_vouch import handle_button from handlers.callback_vouch import handle_button
from handlers.handle_join_request import handle_join_request from handlers.handle_join_request import handle_join_request
from handlers.handle_startup import handle_startup from handlers.handle_startup import handle_startup
from handlers.handle_members_change import handle_join, handle_left from handlers.handle_members_change import handle_join, handle_left
from state import State from storage.profile import Profile
from storage import Profile from bot.state import State
from bot.config import FEEDBACK_CHAT_ID
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
router = Router()
bot = Bot(BOT_TOKEN, parse_mode=ParseMode.HTML)
dp = Dispatcher()
state = State() state = State()
@router.message(CommandStart()) async def command_start_handler(message):
async def command_start_handler(message: Message) -> None:
caption = "Напишите своё сообщение для нас" if message.from_user.llanguage_code == 'ru' else "Write your message for us" caption = "Напишите своё сообщение для нас" if message.from_user.llanguage_code == 'ru' else "Write your message for us"
await message.answer(caption) await message.answer(caption)
@router.callback_query() async def process_callback(callback_query):
async def process_callback(callback_query: CallbackQuery):
cbq = vars(callback_query) cbq = vars(callback_query)
try: try:
cbq["from"] = vars(callback_query.from_user) cbq["from"] = vars(callback_query.from_user)
@ -47,11 +39,10 @@ async def process_callback(callback_query: CallbackQuery):
logger.debug(cbq) logger.debug(cbq)
import traceback import traceback
text = traceback.format_exc() text = traceback.format_exc()
await bot.send_message(FEEDBACK_CHAT_ID, text) await send_message(FEEDBACK_CHAT_ID, text)
@router.chat_join_request() async def join_request_handler(update):
async def join_request_handler(update: ChatJoinRequest) -> None:
print("chat join request") print("chat join request")
join_request = vars(update) join_request = vars(update)
try: try:
@ -63,11 +54,10 @@ async def join_request_handler(update: ChatJoinRequest) -> None:
logger.debug(join_request) logger.debug(join_request)
import traceback import traceback
text = traceback.format_exc() text = traceback.format_exc()
await bot.send_message(FEEDBACK_CHAT_ID, text) await send_message(FEEDBACK_CHAT_ID, text)
@router.message() async def all_handler(message):
async def all_handler(message: Message) -> None:
msg = vars(message) msg = vars(message)
try: try:
msg["from"] = vars(message.from_user) msg["from"] = vars(message.from_user)
@ -81,11 +71,10 @@ async def all_handler(message: Message) -> None:
logger.debug(msg) logger.debug(msg)
import traceback import traceback
text = traceback.format_exc() text = traceback.format_exc()
await bot.send_message(FEEDBACK_CHAT_ID, text) await send_message(FEEDBACK_CHAT_ID, text)
@router.my_chat_member() async def chat_members_change(update):
async def chat_members_change(update: ChatMemberUpdated):
member_updated = vars(update) member_updated = vars(update)
try: try:
member_updated["chat"] = vars(update.chat) member_updated["chat"] = vars(update.chat)
@ -100,33 +89,38 @@ async def chat_members_change(update: ChatMemberUpdated):
await handle_join(member_updated) await handle_join(member_updated)
else: else:
logger.info("unhandled members update") logger.info("unhandled members update")
except Exception as e: except Exception as e:
logger.error(f"[main.my_chat_member] ERROR {e}") logger.error(f"[main.my_chat_member] ERROR {e}")
logger.debug(member_updated) logger.debug(member_updated)
import traceback import traceback
text = traceback.format_exc() text = traceback.format_exc()
await bot.send_message(FEEDBACK_CHAT_ID, text) await send_message(FEEDBACK_CHAT_ID, text)
async def main() -> None: async def main(request):
# connect router
dp.include_router(router)
# storage revalidation # storage revalidation
await handle_startup() await handle_startup()
# Start event dispatching
await dp.start_polling(bot) # Получение обновлений
updates = await request.json()
for update in updates["result"]:
if "message" in update:
await all_handler(update["message"])
elif "callback_query" in update:
await process_callback(update["callback_query"])
elif "join_chat_request" in update:
await join_request_handler(update["join_chat_request"])
elif "my_chat_member" in update:
await chat_members_change(update["my_chat_member"])
return web.Response()
if __name__ == "__main__": if __name__ == "__main__":
logging.basicConfig(level=logging.INFO, stream=sys.stdout) logging.basicConfig(level=logging.INFO, stream=sys.stdout)
# Define a function to handle SIGTERM app = web.Application()
def handle_sigterm(signum, frame): app.router.add_post('/your_bot_token', main) # Замените 'your_bot_token' на реальный токен вашего бота
logger.info("Received SIGTERM. Shutting down gracefully...") web.run_app(app, host='0.0.0.0', port=3000) # Выберите подходящий порт
asyncio.get_event_loop().stop()
# Register the SIGTERM signal handler
signal.signal(signal.SIGTERM, handle_sigterm)
asyncio.get_event_loop().run_until_complete(main())

86
pyproject.toml Normal file
View File

@ -0,0 +1,86 @@
[tool.poetry]
name = "welcomecenterbot"
version = "0.2.0"
description = "telegram group helper"
authors = ["rainbowdev circle"]
license = "Open Source"
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.12"
aiohttp = "^3.9.1"
redis = "^5.0.1"
[tool.poetry.group.dev.dependencies]
setuptools = "^69.0.2"
mypy = "^1.6.1"
black = "^23.10.1"
ruff = "^0.1.2"
isort = "^5.12.0"
[tool.black]
line-length = 120
target-version = ['py312']
include = '\.pyi?$'
exclude = '''
(
/(
\.eggs
| \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
)/
| foo.py
)
'''
[tool.isort]
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
use_parentheses = true
ensure_newline_before_comments = true
line_length = 120
[tool.ruff]
select = ["E4", "E7", "E9", "F"]
ignore = []
line-length = 120
target-version = "py312"
[tool.pyright]
venvPath = "."
venv = ".venv"
include = ["bot/."]
useLibraryCodeForTypes = false
disableLanguageServices = false
disableOrganizeImports = false
reportMissingImports = true
reportMissingModuleSource = "warning"
reportImportCycles = "warning"
maxMemoryForLargeFile = 4096
pythonVersion = "3.12"
autoImportCompletions = true
useVirtualEnv = true
typeCheckingMode = "basic"
disableJediCompletion = true
disableCompletion = false
disableSnippetCompletion = false
disableGoToDefinition = false
disableRenaming = false
disableSignatureHelp = false
diagnostics = true
logLevel = "debug"
pluginSearchPaths = []
typings = {}
mergeTypeStubPackages = false
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

View File

@ -1,5 +0,0 @@
# sanic==19.6.0
redis
asyncio
aiohttp
aiogram

View File

@ -1,6 +1,6 @@
from redis import Redis from redis import Redis
from storage.profile import Profile as ProfileObj from storage.profile import Profile as ProfileObj
from config import REDIS_URL from bot.config import REDIS_URL
import json import json

View File

@ -1,15 +0,0 @@
{
"version": 2,
"functions": {
"api/webhook.py": {
"memory": 1024,
"maxDuration": 10
}
},
"routes": [
{
"src": "/",
"dest": "/api/webhook.py"
}
]
}