|
1 | | -import re |
2 | | -import json |
3 | | -import asyncio |
4 | | -import logging |
5 | | -import os |
6 | | -from pathlib import Path |
7 | | -from aiogram import Bot, Dispatcher, F |
8 | | -from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton |
9 | | -from aiogram.enums import ParseMode |
10 | | -from aiogram.filters import Command |
11 | | -from aiogram.client.default import DefaultBotProperties |
12 | | -from dotenv import load_dotenv |
13 | | -from keep_alive import keep_alive |
| 1 | +import re import json import asyncio import logging import os from pathlib import Path from aiogram import Bot, Dispatcher, F from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton from aiogram.enums import ParseMode from aiogram.filters import Command from aiogram.client.default import DefaultBotProperties from dotenv import load_dotenv from keep_alive import keep_alive |
14 | 2 |
|
15 | 3 | Load environment variables |
16 | 4 |
|
@@ -50,11 +38,13 @@ def is_admin(user_id: int) -> bool: return int(user_id) in ADMIN_IDS |
50 | 38 |
|
51 | 39 | === COMMAND HANDLERS === |
52 | 40 |
|
53 | | -@dp.message(Command("start")) async def cmd_start(msg: Message): try: me = await bot.get_me() status = f"\n🤖 <b>{me.first_name}</b> is online.\nUsername: @{me.username}\nID: <code>{me.id}</code>\n" except Exception as e: logger.error(f"Status check failed: {e}") status = "⚠️ Bot is online, but couldn't fetch status." |
| 41 | +@dp.message(Command("start")) async def cmd_start(msg: Message): try: me = await bot.get_me() status = f"\n🤖 <b>{me.first_name}</b> is online.\nUsername: @{me.username}\nID: <code>{me.id}</code>" except Exception as e: logger.error(f"Status check failed: {e}") status = "\n⚠️ Bot is online, but couldn't fetch status." |
54 | 42 |
|
55 | 43 | welcome_text = f""" |
56 | 44 |
|
57 | | -{status} <b>Admin Commands:</b> /addurl [Label] [URL] — Add one referral link |
| 45 | +{status} |
| 46 | +
|
| 47 | +<b>Admin Commands:</b> /addurl [Label] [URL] — Add one referral link |
58 | 48 | /addurls — Add multiple links (one per line) |
59 | 49 | /delurl [URL] — Remove a referral link |
60 | 50 | /delurls — Remove multiple referral links |
@@ -154,4 +144,82 @@ def is_admin(user_id: int) -> bool: return int(user_id) in ADMIN_IDS |
154 | 144 | not_found.append(escape_html(url)) |
155 | 145 |
|
156 | 146 | if removed: |
| 147 | + save_links(links_db) |
| 148 | + |
| 149 | + reply = "" |
| 150 | + if removed: |
| 151 | + reply += "❌ Removed:\n" + "\n".join(removed) |
| 152 | + if not_found: |
| 153 | + reply += "\n\n⚠️ Not found:\n" + "\n".join(not_found) |
| 154 | + |
| 155 | + await msg.reply(reply or "No URLs to remove.") |
| 156 | +except Exception as e: |
| 157 | + logger.error(f"delurls error: {e}") |
| 158 | + await msg.reply("❌ Error deleting URLs.") |
| 159 | + |
| 160 | +@dp.message(Command("listurls")) async def cmd_listurls(msg: Message): if not is_admin(msg.from_user.id): await msg.reply("❌ Only admins can use this command.") return |
| 161 | + |
| 162 | +try: |
| 163 | + if not links_db: |
| 164 | + await msg.reply("No links saved.") |
| 165 | + return |
| 166 | + |
| 167 | + lines = [f"{i}. {escape_html(v['label'])} → {escape_html(k)}" for i, (k, v) in enumerate(links_db.items(), 1)] |
| 168 | + text = f"<b>Saved Links ({len(links_db)}):</b>\n\n" + "\n".join(lines) |
| 169 | + |
| 170 | + for i in range(0, len(text), 4000): |
| 171 | + await msg.reply(text[i:i + 4000]) |
| 172 | +except Exception as e: |
| 173 | + logger.error(f"listurls error: {e}") |
| 174 | + await msg.reply("❌ Error listing links.") |
| 175 | + |
| 176 | +@dp.message(Command("setbutton")) async def cmd_setbutton(msg: Message): if not is_admin(msg.from_user.id): await msg.reply("❌ Only admins can use this command.") return |
| 177 | + |
| 178 | +try: |
| 179 | + parts = msg.text.split(maxsplit=2) |
| 180 | + if len(parts) < 3: |
| 181 | + await msg.reply("Usage: /setbutton [URL] [Text]") |
| 182 | + return |
| 183 | + |
| 184 | + url, label = parts[1], parts[2] |
| 185 | + if url not in links_db: |
| 186 | + await msg.reply("URL not found.") |
| 187 | + return |
| 188 | + |
| 189 | + links_db[url]["label"] = label |
| 190 | + save_links(links_db) |
| 191 | + await msg.reply(f"🔁 Updated label for {escape_html(url)} to: <b>{escape_html(label)}</b>") |
| 192 | +except Exception as e: |
| 193 | + logger.error(f"setbutton error: {e}") |
| 194 | + await msg.reply("❌ Error updating label.") |
| 195 | + |
| 196 | +=== AUTO FORMAT === |
| 197 | + |
| 198 | +@dp.message() async def auto_edit(msg: Message): try: text = msg.text or "" if not text or not links_db: return |
| 199 | + |
| 200 | +found_urls = [url for url in links_db if url in text] |
| 201 | + if not found_urls: |
| 202 | + return |
| 203 | + |
| 204 | + code_match = re.search(r'(code[:\s]*)([A-Za-z0-9@_-]+)', text, re.IGNORECASE) |
| 205 | + code_text = f"<b>Code:</b> {escape_html(code_match.group(2))}\n\n" if code_match else "" |
| 206 | + title = text.splitlines()[0] if text.splitlines() else "Referral Links" |
| 207 | + new_text = f"<b>{escape_html(title)}</b>\n\n{code_text}<b>Links below:</b>" |
| 208 | + |
| 209 | + keyboard = build_keyboard(found_urls) |
| 210 | + await bot.edit_message_text(chat_id=msg.chat.id, message_id=msg.message_id, text=new_text, reply_markup=keyboard) |
| 211 | +except Exception as e: |
| 212 | + logger.warning(f"Auto-format failed: {e}") |
| 213 | + |
| 214 | +=== LIFECYCLE === |
| 215 | + |
| 216 | +async def on_startup(): logger.info("Bot is starting...") try: await bot.delete_webhook(drop_pending_updates=True) logger.info("Webhook deleted.") except Exception as e: logger.warning(f"Webhook delete failed: {e}") |
| 217 | + |
| 218 | +async def on_shutdown(): logger.info("Bot shutting down...") await bot.session.close() |
| 219 | + |
| 220 | +=== MAIN LOOP === |
| 221 | + |
| 222 | +async def main(): retry = 0 while retry < 5: try: await on_startup() await dp.start_polling(bot, skip_updates=True) except KeyboardInterrupt: break except Exception as e: retry += 1 logger.error(f"Crash: {e}, retrying in {2 ** retry}s...") await asyncio.sleep(min(60, 2 ** retry)) finally: await on_shutdown() |
| 223 | + |
| 224 | +if name == "main": keep_alive() try: asyncio.run(main()) except Exception as e: logger.error(f"Fatal: {e}") |
157 | 225 |
|
0 commit comments