Spaces:
iq7se2
/
7
Paused

iq7se2 commited on
Commit
9db3a84
ยท
verified ยท
1 Parent(s): 8a18fba

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +60 -367
app.py CHANGED
@@ -1,412 +1,105 @@
1
- """
2
- ๐Ÿš€ Manga Engine Pro - ุจูˆุช ุณุญุจ ุงู„ู…ุงู†ุฌุง ุงู„ุฅุญุชุฑุงููŠ
3
- โœ… ู†ุธุงู… ุฃุฑุดูŠู ุฐูƒูŠ ุจู‚ู†ุงุฉ ุชูŠู„ูŠุฌุฑุงู…
4
- โœ… ุชุฎุฒูŠู† ู„ู…ุฑุฉ ูˆุงุญุฏุฉ - ู…ุดุงุฑูƒุฉ ู„ู„ุฃุจุฏ
5
- โœ… ู…ุญุฑูƒ Playwright ู„ุชุฌุงูˆุฒ ุงู„ุญู…ุงูŠุงุช
6
- """
7
-
8
- import os, time, threading, zipfile, io, re, json, telebot, requests
9
- from flask import Flask
10
- from playwright.sync_api import sync_playwright
11
- from PIL import Image
12
  from telebot import types
13
 
14
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
15
- # โš™๏ธ ุงู„ุฅุนุฏุงุฏุงุช
16
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
17
- BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN") # ุชูˆูƒู† ุงู„ุจูˆุช
18
- ARCHIVE_CHANNEL = os.getenv("ARCHIVE_CHANNEL_ID") # ู…ุนุฑู‘ู ู‚ู†ุงุฉ ุงู„ุฃุฑุดูŠู ู…ุซุงู„: -1001234567890
19
- ADMIN_HANDLE = os.getenv("ADMIN_HANDLE", "@svipfast") # ุญุณุงุจ ุงู„ู…ุทูˆุฑ
20
 
21
- bot = telebot.TeleBot(BOT_TOKEN, threaded=False)
 
 
 
22
  app = Flask(__name__)
23
 
24
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
25
- # ๐Ÿ—„๏ธ ู†ุธุงู… ู‚ุงุนุฏุฉ ุจูŠุงู†ุงุช ุงู„ุฃุฑุดูŠู
26
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
27
 
28
- # ุงู„ูู‡ุฑุณ ุงู„ุฏุงุฎู„ูŠ: { "base_url|chapter_N" : message_id_in_channel }
29
- archive_index: dict = {}
30
- INDEX_MSG_ID: int | None = None # ู…ุนุฑู‘ู ุฑุณุงู„ุฉ ุงู„ูู‡ุฑุณ ุงู„ู…ุซุจู‘ุชุฉ
31
-
32
- def _index_key(base_url: str, chapter: int) -> str:
33
- return f"{base_url.rstrip('/')}|{chapter}"
34
-
35
- # โ”€โ”€ ุชุญู…ูŠู„ ุงู„ูู‡ุฑุณ ุนู†ุฏ ุงู„ุจุฏุก โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
36
- def load_archive_index():
37
- """ูŠุญู…ู‘ู„ ูู‡ุฑุณ ุงู„ุฃุฑุดูŠู ู…ู† ุงู„ุฑุณุงู„ุฉ ุงู„ู…ุซุจู‘ุชุฉ ููŠ ู‚ู†ุงุฉ ุงู„ุฃุฑุดูŠู."""
38
- global archive_index, INDEX_MSG_ID
39
- if not ARCHIVE_CHANNEL:
40
- print("โš ๏ธ ARCHIVE_CHANNEL_ID ุบูŠุฑ ู…ุถุจูˆุทุŒ ุงู„ุฃุฑุดูŠู ู…ุนุทู‘ู„.")
41
- return
42
- try:
43
- chat = bot.get_chat(ARCHIVE_CHANNEL)
44
- if chat.pinned_message:
45
- INDEX_MSG_ID = chat.pinned_message.message_id
46
- # ุงู„ูู‡ุฑุณ ู…ุฎุฒู‘ู† ูƒู†ุต JSON ููŠ ุงู„ุฑุณุงู„ุฉ ุงู„ู…ุซุจู‘ุชุฉ
47
- archive_index = json.loads(chat.pinned_message.text)
48
- print(f"โœ… ุชู… ุชุญู…ูŠู„ ุงู„ูู‡ุฑุณ: {len(archive_index)} ูุตู„ ู…ุคุฑุดู.")
49
- else:
50
- print("โ„น๏ธ ู„ุง ูŠูˆุฌุฏ ูู‡ุฑุณ ู…ุซุจู‘ุช ุจุนุฏุŒ ุณูŠูู†ุดุฃ ุนู†ุฏ ุฃูˆู„ ุฃุฑุดูุฉ.")
51
- except Exception as e:
52
- print(f"โŒ ุฎุทุฃ ููŠ ุชุญู…ูŠู„ ุงู„ูู‡ุฑุณ: {e}")
53
-
54
- # โ”€โ”€ ุญูุธ ุงู„ูู‡ุฑุณ ุจุนุฏ ูƒู„ ุฅุถุงูุฉ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
55
- def save_archive_index():
56
- """ูŠุญูุธ ุงู„ูู‡ุฑุณ ุงู„ุญุงู„ูŠ ููŠ ุฑุณุงู„ุฉ ู…ุซุจู‘ุชุฉ ุจู‚ู†ุงุฉ ุงู„ุฃุฑุดูŠู."""
57
- global INDEX_MSG_ID
58
- if not ARCHIVE_CHANNEL:
59
- return
60
- try:
61
- text = json.dumps(archive_index, ensure_ascii=False)
62
- # ุญุฏ ุชูŠู„ูŠุฌุฑุงู… ู„ู„ู†ุต 4096 ุญุฑู - ู†ู‚ุณู‘ู… ุฅุฐุง ุชุฌุงูˆุฒ ุงู„ุญุฏ
63
- if len(text) > 4000:
64
- # ุงุญุชูŠุงุท: ู†ูุฑุณู„ ูƒู…ู„ู JSON ุจุฏู„ุงู‹ ู…ู† ู†ุต
65
- file_bytes = io.BytesIO(text.encode())
66
- file_bytes.name = "index.json"
67
- msg = bot.send_document(ARCHIVE_CHANNEL, file_bytes, caption="#MANGA_INDEX")
68
- # ู†ุฎุฒู‘ู† ูู‚ุท message_id ุงู„ุฃุฎูŠุฑ ู„ู„ู…ู„ู ููŠ ุงู„ุฑุณุงู„ุฉ ุงู„ู…ุซุจู‘ุชุฉ
69
- if INDEX_MSG_ID:
70
- bot.unpin_chat_message(ARCHIVE_CHANNEL, INDEX_MSG_ID)
71
- bot.pin_chat_message(ARCHIVE_CHANNEL, msg.message_id)
72
- INDEX_MSG_ID = msg.message_id
73
- else:
74
- if INDEX_MSG_ID:
75
- bot.edit_message_text(text, ARCHIVE_CHANNEL, INDEX_MSG_ID)
76
- else:
77
- msg = bot.send_message(ARCHIVE_CHANNEL, text)
78
- bot.pin_chat_message(ARCHIVE_CHANNEL, msg.message_id)
79
- INDEX_MSG_ID = msg.message_id
80
- except Exception as e:
81
- print(f"โŒ ุฎุทุฃ ููŠ ุญูุธ ุงู„ูู‡ุฑุณ: {e}")
82
-
83
- # โ”€โ”€ ุงู„ุจุญุซ ููŠ ุงู„ุฃุฑุดูŠู โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
84
- def search_archive(base_url: str, chapter: int) -> int | None:
85
- """ูŠูุนูŠุฏ message_id ุงู„ูุตู„ ุฅุฐุง ูƒุงู† ู…ุคุฑุดูุงู‹ุŒ ูˆุฅู„ุง None."""
86
- return archive_index.get(_index_key(base_url, chapter))
87
-
88
- # โ”€โ”€ ุฅุถุงูุฉ ูุตู„ ู„ู„ุฃุฑุดูŠู โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
89
- def add_to_archive(base_url: str, chapter: int, zip_bytes: bytes, filename: str) -> int | None:
90
- """ูŠุฑูุน ุงู„ู…ู„ู ู„ู‚ู†ุงุฉ ุงู„ุฃุฑุดูŠู ูˆูŠุญุฏู‘ุซ ุงู„ูู‡ุฑุณ."""
91
- if not ARCHIVE_CHANNEL:
92
- return None
93
- try:
94
- caption = (
95
- f"๐Ÿ“ฆ <b>{filename}</b>\n"
96
- f"๐Ÿ”‘ <code>{_index_key(base_url, chapter)}</code>\n"
97
- f"๐Ÿ“… {time.strftime('%Y-%m-%d %H:%M')}"
98
- )
99
- file_io = io.BytesIO(zip_bytes)
100
- file_io.name = filename
101
- msg = bot.send_document(
102
- ARCHIVE_CHANNEL, file_io,
103
- caption=caption, parse_mode="HTML"
104
- )
105
- archive_index[_index_key(base_url, chapter)] = msg.message_id
106
- save_archive_index()
107
- print(f"โœ… ุฃูุฑุดู ุงู„ูุตู„ {chapter} ุจู€ message_id={msg.message_id}")
108
- return msg.message_id
109
- except Exception as e:
110
- print(f"โŒ ุฎุทุฃ ููŠ ุงู„ุฃุฑุดูุฉ: {e}")
111
- return None
112
-
113
- # โ”€โ”€ ุฅุฑุณุงู„ ู…ู† ุงู„ุฃุฑุดูŠู ู…ุจุงุดุฑุฉู‹ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
114
- def forward_from_archive(chat_id: int, message_id: int, chapter: int):
115
- """ูŠูุนูŠุฏ ุชูˆุฌูŠู‡ ุงู„ู…ู„ู ู…ู† ุงู„ุฃุฑุดูŠู ู„ู„ู…ุณุชุฎุฏู…."""
116
- bot.forward_message(chat_id, ARCHIVE_CHANNEL, message_id)
117
 
118
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
119
- # ๐ŸŒ ู…ุญุฑูƒ ุงู„ุณุญุจ (Playwright)
120
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
121
 
122
- def fetch_images_engine(url: str) -> list:
123
- """ูŠุณุญุจ ุตูˆุฑ ุงู„ู…ุงู†ุฌุง ู…ู† ุงู„ุตูุญุฉ ุจุงุณุชุฎุฏุงู… Playwright."""
124
- with sync_playwright() as p:
125
- browser = p.chromium.launch(headless=True)
126
- context = browser.new_context(
127
- user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
128
- "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
129
- )
130
- page = context.new_page()
131
- try:
132
- page.goto(url, wait_until="networkidle", timeout=75000)
133
- # ุงู„ุชู…ุฑูŠุฑ ุงู„ุชุฏุฑูŠุฌูŠ ู„ุชุญู…ูŠู„ ุงู„ุตูˆุฑ ุงู„ูƒุณูˆู„ุฉ
134
- for _ in range(6):
135
- page.mouse.wheel(0, 1500)
136
- time.sleep(0.9)
137
- page.wait_for_timeout(2000)
138
-
139
- images = page.query_selector_all("img")
140
- img_list = []
141
- seen_srcs = set()
142
-
143
- for img in images:
144
- src = (
145
- img.get_attribute("src")
146
- or img.get_attribute("data-src")
147
- or img.get_attribute("data-lazy-src")
148
- or img.get_attribute("data-original")
149
- )
150
- if not src or src in seen_srcs:
151
- continue
152
- # ุชุตููŠุฉ ุงู„ุตูˆุฑ ุงู„ุตุบูŠุฑุฉ ูˆุงู„ุดุนุงุฑุงุช
153
- if not any(ext in src.lower() for ext in ['.jpg', '.jpeg', '.png', '.webp', '.avif']):
154
- continue
155
- if any(kw in src.lower() for kw in ['logo', 'banner', 'avatar', 'icon', 'thumb']):
156
- continue
157
- seen_srcs.add(src)
158
- try:
159
- res = page.request.get(src, timeout=20000)
160
- if res.status == 200 and len(res.body()) > 20000:
161
- img_list.append(Image.open(io.BytesIO(res.body())).convert('RGB'))
162
- except:
163
- continue
164
-
165
- browser.close()
166
- return img_list
167
- except Exception as e:
168
- print(f"โŒ ุฎุทุฃ ููŠ ุงู„ุฌู„ุจ: {e}")
169
- browser.close()
170
- return []
171
 
172
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
173
- # ๐Ÿ—๏ธ ุจู†ุงุก ุงู„ุฑูˆุงุจุท ูˆุชุฌู…ูŠุน ู…ู„ู ZIP
174
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
175
 
176
- def build_chapter_url(sample_url: str, chapter: int) -> str:
177
- """ูŠูู†ุดุฆ ุฑุงุจุท ุงู„ูุตู„ ุจู†ุงุกู‹ ุนู„ู‰ ู†ู…ุท ุฑุงุจุท ุงู„ุนูŠู‘ู†ุฉ."""
178
- base = re.sub(r'/(?:chapter-)?[\d]+$', '', sample_url.strip().rstrip('/'))
179
- if "azoramoon" in sample_url or "chapter-" in sample_url:
180
- return f"{base}/chapter-{chapter}"
181
- return f"{base}/{chapter}"
182
-
183
- def images_to_zip_bytes(imgs: list, chapter: int) -> bytes | None:
184
- """ูŠุญูˆู‘ู„ ู‚ุงุฆู…ุฉ ุงู„ุตูˆุฑ ุฅู„ู‰ ู…ู„ู ZIP ูŠุญุชูˆูŠ PDF."""
185
- if not imgs:
186
- return None
187
- pdf_buf = io.BytesIO()
188
- imgs[0].save(pdf_buf, format='PDF', save_all=True, append_images=imgs[1:])
189
- zip_buf = io.BytesIO()
190
- with zipfile.ZipFile(zip_buf, 'w', zipfile.ZIP_DEFLATED) as zf:
191
- zf.writestr(f"Chapter_{chapter}.pdf", pdf_buf.getvalue())
192
- return zip_buf.getvalue()
193
 
194
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
195
- # ๐Ÿค– ู…ู†ุทู‚ ุงู„ุจูˆุช - ุงู„ู…ุนุงู„ุฌุงุช ูˆุงู„ุฃูˆุงู…ุฑ
196
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
197
 
198
- user_states: dict = {}
199
-
200
- @app.route('/')
201
- def home():
202
- return "<h1>๐Ÿš€ Manga Engine Pro is Running</h1><p>Archive-powered manga bot.</p>"
203
-
204
- # โ”€โ”€ /start โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
205
  @bot.message_handler(commands=['start'])
206
- def welcome(message):
207
- user_states[message.chat.id] = {}
208
- markup = types.InlineKeyboardMarkup(row_width=1)
209
- markup.add(
210
- types.InlineKeyboardButton("๐Ÿ“ฅ ุฅุฑุณุงู„ ุฑุงุจุท ุฌุฏูŠุฏ", callback_data="new_order"),
211
- types.InlineKeyboardButton("๐Ÿ“Š ุฅุญุตุงุฆูŠุงุช ุงู„ุฃุฑุดูŠู", callback_data="stats"),
212
- types.InlineKeyboardButton("๐Ÿ’ก ูƒูŠู ุฃุญุตู„ ุนู„ู‰ ุงู„ุฑุงุจุทุŸ", callback_data="help_link"),
213
- types.InlineKeyboardButton("๐Ÿ‘จโ€๐Ÿ’ป ุงู„ู…ุทูˆุฑ", url=f"https://t.me/{ADMIN_HANDLE.replace('@','')}"),
214
- )
215
  bot.send_message(
216
  message.chat.id,
217
- f"<b>๐Ÿš€ ู…ุฑุญุจุงู‹ ููŠ ุจูˆุช ุณุญุจ ุงู„ู…ุงู†ุฌุง ุงู„ุฅุญุชุฑุงููŠ</b>\n\n"
218
- f"โœจ <b>ุงู„ุฌุฏูŠุฏ:</b> ู†ุธุงู… ุฃุฑุดูŠู ุฐูƒูŠ!\n"
219
- f"ุฅุฐุง ุทู„ุจุช ูุตู„ุงู‹ ุณุจู‚ ุณุญุจู‡ ู…ู† ู‚ุจู„ุŒ ุณูŠูุฑุณูŽู„ ู„ูƒ ููˆุฑุงู‹ โšก\n\n"
220
- f"ุงู„ู…ุทูˆุฑ: {ADMIN_HANDLE}",
221
- parse_mode="HTML", reply_markup=markup
222
  )
223
 
224
- # โ”€โ”€ ู…ุนุงู„ุฌุฉ ุงู„ุฃุฒุฑุงุฑ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
225
- @bot.callback_query_handler(func=lambda call: True)
226
- def handle_query(call):
227
- chat_id = call.message.chat.id
228
-
229
- if call.data == "new_order":
230
- msg = bot.send_message(chat_id, "๐Ÿ”— <b>ุฃุฑุณู„ ุฑุงุจุท ุงู„ูุตู„:</b>", parse_mode="HTML")
231
- bot.register_next_step_handler(msg, process_url_step)
232
-
233
- elif call.data == "stats":
234
- total = len(archive_index)
235
- bot.send_message(
236
- chat_id,
237
- f"๐Ÿ“Š <b>ุฅุญุตุงุฆูŠุงุช ุงู„ุฃุฑุดูŠู</b>\n\n"
238
- f"๐Ÿ“ฆ ุงู„ูุตูˆู„ ุงู„ู…ุคุฑุดูุฉ: <b>{total}</b>\n"
239
- f"โšก ุฌุงู‡ุฒุฉ ู„ู„ุฅุฑุณุงู„ ุงู„ููˆุฑูŠ",
240
- parse_mode="HTML"
241
- )
242
-
243
- elif call.data == "help_link":
244
- bot.send_message(
245
- chat_id,
246
- "<b>๐Ÿ’ก ุทุฑูŠู‚ุฉ ุฅุฑุณุงู„ ุงู„ุฑุงุจุท:</b>\n\n"
247
- "ุงู†ุณุฎ ุฑุงุจุท ุฃูŠ ูุตู„ ู…ู† ุงู„ู…ูˆู‚ุน ู…ุจุงุดุฑุฉ:\n"
248
- "<code>https://olympustaff.com/series/manga-name/1</code>\n\n"
249
- "ุงู„ุจูˆุช ุณูŠูƒุชุดู ุชู„ู‚ุงุฆูŠุงู‹ ู†ู…ุท ุงู„ุฑูˆุงุจุท ูˆูŠุณุญุจ ุจู‚ูŠุฉ ุงู„ูุตูˆู„ ๐ŸŽฏ",
250
- parse_mode="HTML"
251
- )
252
 
253
- elif call.data.startswith("mode_"):
254
- mode = call.data.split("_")[1]
255
- user_states[chat_id]['mode'] = mode
256
- prompts = {
257
- "single": "๐Ÿ”ข ุฃุฑุณู„ <b>ุฑู‚ู… ุงู„ูุตู„</b>:",
258
- "range": "๐Ÿ”ข ุฃุฑุณู„ <b>ุงู„ู…ุฏู‰</b> (ู…ุซุงู„: <code>1-10</code>):",
259
- "auto10": "๐Ÿ”ข ุฃุฑุณู„ <b>ุฑู‚ู… ุงู„ูุตู„ ุงู„ุฃูˆู„</b> ูˆุณู†ุญู…ู‘ู„ 10 ูุตูˆู„ ุชู„ู‚ุงุฆูŠุงู‹:",
260
- }
261
- msg = bot.send_message(chat_id, prompts.get(mode, "ุฃุฑุณู„ ุงู„ุฑู‚ู…:"), parse_mode="HTML")
262
- bot.register_next_step_handler(msg, final_execution_step)
263
 
264
- bot.answer_callback_query(call.id)
 
 
265
 
266
- # โ”€โ”€ ุงู„ุฎุทูˆุฉ 1: ุงุณุชู„ุงู… ุงู„ุฑุงุจุท โ”€โ”€โ”€๏ฟฝ๏ฟฝโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
267
- def process_url_step(message):
268
  url = message.text.strip()
269
- if not url.startswith("http"):
270
- bot.send_message(message.chat.id, "โŒ ุงู„ุฑุงุจุท ุบูŠุฑ ุตุญูŠุญ.")
271
- return
272
- user_states[message.chat.id] = {'url': url}
273
- markup = types.InlineKeyboardMarkup(row_width=2)
274
- markup.add(
275
- types.InlineKeyboardButton("๐ŸŽฏ ูุตู„ ูˆุงุญุฏ", callback_data="mode_single"),
276
- types.InlineKeyboardButton("๐Ÿ“ฆ ุญุฒู…ุฉ ูุตูˆู„", callback_data="mode_range"),
277
- types.InlineKeyboardButton("โฉ 10 ูุตูˆู„ ุชู„ู‚ุงุฆูŠ", callback_data="mode_auto10"),
278
- )
279
- bot.send_message(
280
- message.chat.id,
281
- "โœ… <b>ุงู„ุฑุงุจุท ู…ู‚ุจูˆู„! ุงุฎุชุฑ ุทุฑูŠู‚ุฉ ุงู„ุชุญู…ูŠู„:</b>",
282
- parse_mode="HTML", reply_markup=markup
283
- )
284
 
285
- # โ”€โ”€ ุงู„ุฎุทูˆุฉ 2: ุงู„ุชู†ููŠุฐ ู…ุน ุงู„ุฃุฑุดูŠู ุงู„ุฐูƒูŠ โ”€โ”€โ”€โ”€โ”€โ”€
286
- def final_execution_step(message):
287
- chat_id = message.chat.id
288
- state = user_states.get(chat_id, {})
289
-
290
- if 'url' not in state:
291
- bot.send_message(chat_id, "โš ๏ธ ุงู†ุชู‡ุช ุงู„ุฌู„ุณุฉุŒ ุงุจุฏุฃ ู…ู† /start")
292
- return
293
-
294
- text = message.text.strip()
295
- mode = state['mode']
296
- url = state['url']
297
- base_url = re.sub(r'/(?:chapter-)?[\d]+$', '', url.strip().rstrip('/'))
298
-
299
- try:
300
- if mode == "single":
301
- start = end = int(text)
302
- elif mode == "range":
303
- start, end = map(int, text.split('-'))
304
- elif mode == "auto10":
305
- start = int(text)
306
- end = start + 9
307
- else:
308
- raise ValueError("mode unknown")
309
- except:
310
- bot.send_message(chat_id, "โš ๏ธ ุชู†ุณูŠู‚ ุฎุงุทุฆ (ู…ุซุงู„: 5 ุฃูˆ 1-10)")
311
  return
312
 
313
- count = end - start + 1
314
- status_msg = bot.send_message(
315
- chat_id,
316
- f"๐Ÿ” <b>ูŠุจุญุซ ููŠ ุงู„ุฃุฑุดูŠู...</b>\n๐Ÿ“‹ ุงู„ูุตูˆู„: {start} โ† {end} ({count} ูุตู„)",
317
- parse_mode="HTML"
318
- )
319
-
320
- # โ”€โ”€ ุงู„ุจุญุซ ููŠ ุงู„ุฃุฑุดูŠู ุฃูˆู„ุงู‹ โ”€โ”€
321
- cached, missing = [], []
322
- for ch in range(start, end + 1):
323
- mid = search_archive(base_url, ch)
324
- if mid:
325
- cached.append((ch, mid))
326
- else:
327
- missing.append(ch)
328
-
329
- # ุฅุฑุณุงู„ ุงู„ูุตูˆู„ ุงู„ู…ุคุฑุดูุฉ ููˆุฑุงู‹
330
- if cached:
331
- bot.edit_message_text(
332
- f"โšก <b>ูˆูุฌุฏ ููŠ ุงู„ุฃุฑุดูŠู {len(cached)} ูุตู„!</b>\nูŠูุฑุณูŽู„ ููˆุฑุงู‹...",
333
- chat_id, status_msg.message_id, parse_mode="HTML"
334
- )
335
- for ch, mid in cached:
336
- try:
337
- forward_from_archive(chat_id, mid, ch)
338
- time.sleep(0.4) # ุชูุงุฏูŠ flood
339
- except Exception as e:
340
- print(f"Forward error ch{ch}: {e}")
341
-
342
- # ุณุญุจ ุงู„ูุตูˆู„ ุงู„ู†ุงู‚ุตุฉ
343
- if missing:
344
- bot.send_message(
345
- chat_id,
346
- f"๐Ÿ”„ <b>ูŠุณุญุจ {len(missing)} ูุตู„ ุฌุฏูŠุฏ...</b>\n"
347
- f"ุงู„ูุตูˆู„: {', '.join(map(str, missing))}",
348
- parse_mode="HTML"
349
- )
350
- for ch in missing:
351
- ch_url = build_chapter_url(url, ch)
352
- bot.send_chat_action(chat_id, 'upload_document')
353
- imgs = fetch_images_engine(ch_url)
354
- if not imgs:
355
- bot.send_message(chat_id, f"โŒ ุงู„ูุตู„ {ch} ู„ู… ูŠูุณุญุจ (ุชุญู‚ู‚ ู…ู† ุงู„ุฑุงุจุท)")
356
- continue
357
 
358
- zip_bytes = images_to_zip_bytes(imgs, ch)
359
- if not zip_bytes:
360
- continue
361
 
362
- filename = f"Chapter_{ch}.zip"
363
 
364
- # โ‘  ุฃุฑุดูุฉ ุฃูˆู„ุงู‹
365
- archived_mid = add_to_archive(base_url, ch, zip_bytes, filename)
366
-
367
- # โ‘ก ุงู„ุฅุฑุณุงู„ ู„ู„ู…ุณุชุฎุฏู… ู…ู† ุงู„ุฃุฑุดูŠู ุฃูˆ ู…ุจุงุดุฑุฉ
368
- if archived_mid:
369
- forward_from_archive(chat_id, archived_mid, ch)
370
- else:
371
- # fallback: ุฅุฑุณุงู„ ู…ุจุงุดุฑ
372
- f_io = io.BytesIO(zip_bytes)
373
- f_io.name = filename
374
- bot.send_document(
375
- chat_id, f_io,
376
- caption=f"๐Ÿ“ฆ ุงู„ูุตู„ {ch}\n๐Ÿ‘จโ€๐Ÿ’ป {ADMIN_HANDLE}",
377
- parse_mode="HTML"
378
- )
379
- time.sleep(0.5)
380
-
381
- # ุฑุณุงู„ุฉ ุงู„ุงู†ุชู‡ุงุก
382
- summary = (
383
- f"โœ… <b>ุงูƒุชู…ู„ ุงู„ุชุญู…ูŠู„!</b>\n\n"
384
- f"โšก ู…ู† ุงู„ุฃุฑุดูŠู: {len(cached)}\n"
385
- f"๐ŸŒ ู…ุณุญูˆุจ ุฌุฏูŠุฏ: {len(missing)}\n"
386
- f"๐Ÿ‘จโ€๐Ÿ’ป {ADMIN_HANDLE}"
387
- )
388
- bot.send_message(chat_id, summary, parse_mode="HTML")
389
- try:
390
- bot.delete_message(chat_id, status_msg.message_id)
391
- except:
392
- pass
393
 
394
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
395
- # ๐Ÿš€ ุงู„ุชุดุบูŠู„
396
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
397
 
398
  if __name__ == "__main__":
399
- print("๐Ÿ”ง ุชุซุจูŠุช Chromium...")
400
- os.system("playwright install chromium")
401
 
402
- print("๐Ÿ“‚ ุชุญู…ูŠู„ ูู‡ุฑุณ ุงู„ุฃุฑุดูŠู...")
403
- load_archive_index()
404
 
405
- print("๐Ÿค– ุชุดุบูŠู„ ุงู„ุจูˆุช...")
406
- threading.Thread(
407
- target=lambda: bot.infinity_polling(timeout=30, long_polling_timeout=25),
408
- daemon=True
409
- ).start()
 
 
 
410
 
411
- print("๐ŸŒ ุชุดุบูŠู„ Flask...")
412
- app.run(host="0.0.0.0", port=7860)
 
1
+ import os, time, threading, zipfile, io, re, json
2
+ from flask import Flask, request
3
+ import telebot
 
 
 
 
 
 
 
 
4
  from telebot import types
5
 
6
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
7
+ # โš™๏ธ ุงู„ุฅุนุฏุงุฏุงุช
8
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
 
 
 
9
 
10
+ BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
11
+ ADMIN_HANDLE = os.getenv("ADMIN_HANDLE", "@svipfast")
12
+
13
+ bot = telebot.TeleBot(BOT_TOKEN)
14
  app = Flask(__name__)
15
 
16
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
17
+ # ๐Ÿง  ุญุงู„ุฉ ุงู„ู…ุณุชุฎุฏู…
18
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
19
 
20
+ user_states = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
23
+ # ๐Ÿ  ุงู„ุตูุญุฉ ุงู„ุฑุฆูŠุณูŠุฉ
24
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
25
 
26
+ @app.route('/')
27
+ def home():
28
+ return "โœ… Bot is running with Webhook"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
31
+ # ๐Ÿ”— Webhook endpoint
32
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
33
 
34
+ @app.route(f"/{BOT_TOKEN}", methods=["POST"])
35
+ def webhook():
36
+ json_str = request.stream.read().decode("utf-8")
37
+ update = telebot.types.Update.de_json(json_str)
38
+ bot.process_new_updates([update])
39
+ return "OK", 200
 
 
 
 
 
 
 
 
 
 
 
40
 
41
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
42
+ # ๐Ÿš€ /start
43
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
44
 
 
 
 
 
 
 
 
45
  @bot.message_handler(commands=['start'])
46
+ def start(message):
47
+ markup = types.InlineKeyboardMarkup()
48
+ markup.add(types.InlineKeyboardButton("๐Ÿ“ฅ ุฅุฑุณุงู„ ุฑุงุจุท", callback_data="send_link"))
49
+
 
 
 
 
 
50
  bot.send_message(
51
  message.chat.id,
52
+ f"๐Ÿš€ ุฃู‡ู„ุงู‹ ุจูƒ!\n\nุฃุฑุณู„ ุฑุงุจุท ุงู„ู…ุงู†ุฌุง ูˆุณูŠุชู… ุณุญุจู‡.\n\n๐Ÿ‘จโ€๐Ÿ’ป {ADMIN_HANDLE}",
53
+ reply_markup=markup
 
 
 
54
  )
55
 
56
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
57
+ # ๐Ÿ”˜ ุงู„ุฃุฒุฑุงุฑ
58
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
+ @bot.callback_query_handler(func=lambda call: True)
61
+ def handle_buttons(call):
62
+ if call.data == "send_link":
63
+ msg = bot.send_message(call.message.chat.id, "๐Ÿ”— ุฃุฑุณู„ ุงู„ุฑุงุจุท ุงู„ุขู†:")
64
+ bot.register_next_step_handler(msg, process_link)
 
 
 
 
 
65
 
66
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
67
+ # ๐Ÿ”— ู…ุนุงู„ุฌุฉ ุงู„ุฑุงุจุท
68
+ # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
69
 
70
+ def process_link(message):
 
71
  url = message.text.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
+ if not url.startswith("http"):
74
+ bot.send_message(message.chat.id, "โŒ ุงู„ุฑุงุจุท ุบูŠุฑ ุตุงู„ุญ")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  return
76
 
77
+ bot.send_message(message.chat.id, f"โœ… ุชู… ุงุณุชู„ุงู… ุงู„ุฑุงุจุท:\n{url}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
+ # (ู‡ู†ุง ุชุถูŠู ู…ุญุฑูƒ ุงู„ุณุญุจ ุงู„ุฎุงุต ุจูƒ)
80
+ bot.send_message(message.chat.id, "โš™๏ธ ุฌุงุฑูŠ ุงู„ู…ุนุงู„ุฌุฉ...")
 
81
 
82
+ time.sleep(2)
83
 
84
+ bot.send_message(message.chat.id, "โœ… ุชู… (ุชุฌุฑุจุฉ ูู‚ุท)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
87
+ # ๐Ÿš€ ุงู„ุชุดุบูŠู„
88
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
89
 
90
  if __name__ == "__main__":
91
+ print("๐Ÿš€ Starting bot...")
 
92
 
93
+ # ุฑุงุจุท Space (ุบูŠุฑู‡!)
94
+ SPACE_URL = os.getenv("SPACE_URL")
95
 
96
+ if SPACE_URL:
97
+ webhook_url = f"{SPACE_URL}/{BOT_TOKEN}"
98
+ bot.remove_webhook()
99
+ time.sleep(1)
100
+ bot.set_webhook(url=webhook_url)
101
+ print(f"โœ… Webhook set: {webhook_url}")
102
+ else:
103
+ print("โš ๏ธ ุถุน SPACE_URL ููŠ Secrets")
104
 
105
+ app.run(host="0.0.0.0", port=7860)