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

2
.gitignore vendored
View File

@ -8,3 +8,5 @@ update.json
.idea
venv
*.rdb
.venv
poetry.lock

View File

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

View File

@ -1,6 +1,14 @@
FROM python:3.11-slim
FROM python:slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "bot/main.py"]
COPY . /app
RUN apt-get update && \
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 json
from config import BOT_TOKEN, WEBHOOK
from bot.config import BOT_TOKEN, WEBHOOK
import logging
# 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.callback_vouch import update_button
from utils.mention import userdata_extract
from storage import Profile
import logging
logger = logging.getLogger(__name__)
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
import logging
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)

View File

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

View File

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

View File

@ -1,5 +1,5 @@
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
import json
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 storage import Profile, storage
from handlers.send_button import show_request_msg

View File

@ -1,13 +1,15 @@
import json
from api import (
from bot.api import (
send_message,
forward_message,
get_chat_administrators,
)
from storage import storage
from config import FEEDBACK_CHAT_ID
from bot.config import FEEDBACK_CHAT_ID
import logging
logger = logging.getLogger(__name__)
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 storage import Profile, storage

View File

@ -1,7 +1,7 @@
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 config import FEEDBACK_CHAT_ID
from bot.config import FEEDBACK_CHAT_ID
import logging
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 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 utils.mention import userdata_extract
import logging
logger = logging.getLogger(__name__)
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_graph import handle_command_graph
from handlers.command_ask import handle_command_ask
from config import FEEDBACK_CHAT_ID
from bot.config import FEEDBACK_CHAT_ID
import logging
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 storage import storage
import logging
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)

View File

@ -1,38 +1,30 @@
import asyncio
import logging
import signal
import sys
import signal # Import the signal module
from aiogram import Bot, Dispatcher, Router
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 aiohttp import web
from bot.api import send_message
from handlers.routing import handle_routing
from handlers.callback_unlink import handle_unlink
from handlers.callback_vouch import handle_button
from handlers.handle_join_request import handle_join_request
from handlers.handle_startup import handle_startup
from handlers.handle_members_change import handle_join, handle_left
from state import State
from storage import Profile
from storage.profile import Profile
from bot.state import State
from bot.config import FEEDBACK_CHAT_ID
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
router = Router()
bot = Bot(BOT_TOKEN, parse_mode=ParseMode.HTML)
dp = Dispatcher()
state = State()
@router.message(CommandStart())
async def command_start_handler(message: Message) -> None:
async def command_start_handler(message):
caption = "Напишите своё сообщение для нас" if message.from_user.llanguage_code == 'ru' else "Write your message for us"
await message.answer(caption)
@router.callback_query()
async def process_callback(callback_query: CallbackQuery):
async def process_callback(callback_query):
cbq = vars(callback_query)
try:
cbq["from"] = vars(callback_query.from_user)
@ -47,11 +39,10 @@ async def process_callback(callback_query: CallbackQuery):
logger.debug(cbq)
import traceback
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: ChatJoinRequest) -> None:
async def join_request_handler(update):
print("chat join request")
join_request = vars(update)
try:
@ -63,11 +54,10 @@ async def join_request_handler(update: ChatJoinRequest) -> None:
logger.debug(join_request)
import traceback
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: Message) -> None:
async def all_handler(message):
msg = vars(message)
try:
msg["from"] = vars(message.from_user)
@ -81,11 +71,10 @@ async def all_handler(message: Message) -> None:
logger.debug(msg)
import traceback
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: ChatMemberUpdated):
async def chat_members_change(update):
member_updated = vars(update)
try:
member_updated["chat"] = vars(update.chat)
@ -106,27 +95,32 @@ async def chat_members_change(update: ChatMemberUpdated):
logger.debug(member_updated)
import traceback
text = traceback.format_exc()
await bot.send_message(FEEDBACK_CHAT_ID, text)
await send_message(FEEDBACK_CHAT_ID, text)
async def main() -> None:
# connect router
dp.include_router(router)
async def main(request):
# storage revalidation
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__":
logging.basicConfig(level=logging.INFO, stream=sys.stdout)
# Define a function to handle SIGTERM
def handle_sigterm(signum, frame):
logger.info("Received SIGTERM. Shutting down gracefully...")
asyncio.get_event_loop().stop()
# Register the SIGTERM signal handler
signal.signal(signal.SIGTERM, handle_sigterm)
asyncio.get_event_loop().run_until_complete(main())
app = web.Application()
app.router.add_post('/your_bot_token', main) # Замените 'your_bot_token' на реальный токен вашего бота
web.run_app(app, host='0.0.0.0', port=3000) # Выберите подходящий порт

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 storage.profile import Profile as ProfileObj
from config import REDIS_URL
from bot.config import REDIS_URL
import json

View File

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