commit bc69e456dafa972c2c6105e5e03d6e94fb391778 Author: greenhazz Date: Tue Feb 10 22:47:23 2026 +0300 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a0fc907 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.12-trixie + +WORKDIR /app + +COPY . . +RUN pip3 install -r req.txt + +RUN mkdir -p /app/data + +CMD ["python", "main.py"] diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..e0319bc --- /dev/null +++ b/compose.yaml @@ -0,0 +1,9 @@ +services: + reg: + build: . + volumes: + - db-data:/storage + env_file: ".env" + +volumes: + db-data: diff --git a/main.py b/main.py new file mode 100644 index 0000000..f9820c6 --- /dev/null +++ b/main.py @@ -0,0 +1,156 @@ +import asyncio +import hashlib +import hmac +from tomllib import load + +import requests +from aiogram import Bot, Dispatcher +from aiogram.enums import ChatMemberStatus +from aiogram.filters import Command +from aiogram.types import Message +from tinydb import Query, TinyDB +from dotenv import load_dotenv + +load_dotenv() +import os +# ================= CONFIG ================= + +db = TinyDB("/storage/users.json") +Users = Query() + +BOT_TOKEN = os.getenv("BOT_TOKEN") + +MATRIX_SERVER = os.getenv("MATRIX_SERVER") +SHARED_SECRET = os.getenv("SHARED_SECRET") +MAX_MATRIX_ACCOUNTS = int(os.getenv("MAX_MATRIX_ACCOUNTS")) +# я хотел еще сделать лимит на подписчиков гринвещает и моих котов но стало лень +REQUIRED_CHANNELS = [ + -1002308592767, # channel 1 ID + -1002032804724, # channel 2 ID +] + +# ========================================= + + +def can_create_matrix_account(telegram_id: int) -> bool: + user = db.get(Users.telegram_id == telegram_id) + if not user: + return True + return len(user.get("matrix_accounts", [])) < MAX_MATRIX_ACCOUNTS + + +def add_matrix_account(telegram_id: int, username: str): + user = db.get(Users.telegram_id == telegram_id) + + if user: + accounts = user.get("matrix_accounts", []) + accounts.append(username) + db.update({"matrix_accounts": accounts}, Users.telegram_id == telegram_id) + else: + db.insert({"telegram_id": telegram_id, "matrix_accounts": [username]}) + + +def generate_mac(nonce: str, username: str, password: str, admin: bool = False) -> str: + msg = f"{nonce}\0{username}\0{password}\0{'admin' if admin else 'notadmin'}" + return hmac.new(SHARED_SECRET.encode(), msg.encode(), hashlib.sha1).hexdigest() + + +async def is_subscribed(bot: Bot, user_id: int) -> bool: + """ + Returns True if user is member of AT LEAST ONE required channel + """ + for channel_id in REQUIRED_CHANNELS: + try: + member = await bot.get_chat_member(channel_id, user_id) + if member.status in ( + ChatMemberStatus.MEMBER, + ChatMemberStatus.ADMINISTRATOR, + ChatMemberStatus.OWNER, + ): + return True + except Exception: + continue + return False + + +async def create_user(message: Message, bot: Bot): + telegram_id = message.from_user.id + + # 🔒 limit check + if not can_create_matrix_account(telegram_id): + await message.answer( + "🚫 Limit reached!\n\n" + "You can create **up to 3 Matrix accounts** per Telegram account." + ) + return + + # 2️⃣ parse args + args = message.text.split(maxsplit=2) + if len(args) != 3: + await message.answer("Usage:\n" "/create username password") + return + + username, password = args[1], args[2] + + try: + # 3️⃣ get nonce + nonce_resp = requests.get( + f"{MATRIX_SERVER}/_synapse/admin/v1/register", timeout=10 + ) + nonce_resp.raise_for_status() + nonce = nonce_resp.json()["nonce"] + + # 4️⃣ generate MAC + mac = generate_mac(nonce, username, password) + + # 5️⃣ register user + payload = { + "nonce": nonce, + "username": username, + "password": password, + "mac": mac, + "admin": False, + } + + r = requests.post( + f"{MATRIX_SERVER}/_synapse/admin/v1/register", json=payload, timeout=10 + ) + + if r.status_code == 200: + add_matrix_account(message.from_user.id, username) + domain = MATRIX_SERVER.replace("https://", "").replace("http://", "") + await message.answer( + "✅ Matrix account created!\n\n" f"👤 User: @{username}:{domain}" + ) + await bot.send_message( + -5087229310, + f"""matrix [user](tg://user?id={message.from_user.id}) {message.from_user.username} is created""", + parse_mode="markdown", + ) + else: + await message.answer("❌ Failed to create account\n\n" f"{r.text}") + + except Exception as e: + await message.answer(f"⚠️ Error:\n{e}") + + +async def start(message: Message): + await message.answer( + "👋 Welcome!\n\n" + "To create a Matrix account:\n" + "`/create username password`\n\n" + ) + + +async def main(): + bot = Bot(BOT_TOKEN) + dp = Dispatcher() + + dp.message.register(start, Command("start")) + dp.message.register(create_user, Command("create")) + + await dp.start_polling(bot) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/req.txt b/req.txt new file mode 100644 index 0000000..ead7501 --- /dev/null +++ b/req.txt @@ -0,0 +1,3 @@ +aiogram +tinydb +requests