understanding commited on
Commit
03b3ec5
Β·
verified Β·
1 Parent(s): 3130906

Update bot/handlers.py

Browse files
Files changed (1) hide show
  1. bot/handlers.py +223 -21
bot/handlers.py CHANGED
@@ -1,31 +1,233 @@
1
  # PATH: bot/handlers.py
 
2
  from hydrogram import Client, filters
3
- from hydrogram.types import Message
 
4
  from bot.config import Telegram
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
  def setup_handlers(app: Client) -> None:
7
- @app.on_message(filters.command(["start"]))
 
8
  async def start_handler(_: Client, m: Message):
9
- await m.reply_text(
10
- "βœ… Online.\n"
11
- "Commands:\n"
12
- "/ping\n"
13
- "/me\n"
14
- )
15
-
16
- @app.on_message(filters.command(["ping"]))
17
  async def ping_handler(_: Client, m: Message):
18
- await m.reply_text("πŸ“ Pong!")
19
 
20
- @app.on_message(filters.command(["me"]))
21
  async def me_handler(_: Client, m: Message):
22
  uid = m.from_user.id if m.from_user else None
23
- await m.reply_text(f"πŸ‘€ Your ID: `{uid}`", quote=True)
24
-
25
- @app.on_message(filters.private & filters.text & ~filters.command(["start", "ping", "me"]))
26
- async def echo_handler(_: Client, m: Message):
27
- # Normal simple reply bot
28
- txt = (m.text or "").strip()
29
- if not txt:
30
- return
31
- await m.reply_text(f"πŸ—£ You said: {txt}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # PATH: bot/handlers.py
2
+ import asyncio
3
  from hydrogram import Client, filters
4
+ from hydrogram.types import Message, CallbackQuery
5
+
6
  from bot.config import Telegram
7
+ from bot.ui import texts
8
+ from bot.ui.keyboards import profiles_keyboard
9
+ from bot.ui.callbacks import parse_cb
10
+
11
+ from bot.core.auth import is_owner_id, require_allowed
12
+ from bot.core.progress import SpeedETA, human_bytes, human_eta
13
+ from bot.core.tasks import create_task, set_task
14
+
15
+ from bot.integrations.cf_worker1 import profile_add, profile_list, profile_set_default
16
+ from bot.integrations.cf_worker2 import allow_user, disallow_user, pick_profile, access_token, record_upload, stats_today
17
+
18
+ from bot.telegram.media import download_to_temp
19
+ from bot.telegram.parse import extract_media_message, extract_title_description
20
+ from bot.telegram.replies import safe_reply, safe_edit
21
+ from bot.temp.files import cleanup_file
22
+
23
+ from bot.youtube.uploader import upload_video
24
 
25
  def setup_handlers(app: Client) -> None:
26
+
27
+ @app.on_message(filters.command(["start"]) & filters.private)
28
  async def start_handler(_: Client, m: Message):
29
+ await safe_reply(m, texts.START_TEXT)
30
+
31
+ @app.on_message(filters.command(["ping"]) & filters.private)
 
 
 
 
 
32
  async def ping_handler(_: Client, m: Message):
33
+ await safe_reply(m, "πŸ“ Pong!")
34
 
35
+ @app.on_message(filters.command(["me"]) & filters.private)
36
  async def me_handler(_: Client, m: Message):
37
  uid = m.from_user.id if m.from_user else None
38
+ await safe_reply(m, f"πŸ‘€ Your ID: `{uid}`")
39
+
40
+ # -------- OWNER allow/disallow via forward OR id --------
41
+ @app.on_message(filters.command(["allow"]) & filters.private)
42
+ async def allow_cmd(_: Client, m: Message):
43
+ if not is_owner_id(m.from_user.id if m.from_user else None):
44
+ return await safe_reply(m, texts.OWNER_ONLY)
45
+
46
+ # If forward present -> use forwarded user id
47
+ target = None
48
+ if m.reply_to_message and m.reply_to_message.from_user:
49
+ target = m.reply_to_message.from_user.id
50
+ elif m.forward_from:
51
+ target = m.forward_from.id
52
+ else:
53
+ parts = (m.text or "").split()
54
+ if len(parts) >= 2 and parts[1].isdigit():
55
+ target = int(parts[1])
56
+
57
+ if not target:
58
+ return await safe_reply(m, "Usage:\nβ€’ Reply/forward user msg then /allow\nβ€’ or /allow <user_id>")
59
+
60
+ j = await allow_user(target)
61
+ await safe_reply(m, f"βœ… allowed: `{target}`\n{j}")
62
+
63
+ @app.on_message(filters.command(["disallow"]) & filters.private)
64
+ async def disallow_cmd(_: Client, m: Message):
65
+ if not is_owner_id(m.from_user.id if m.from_user else None):
66
+ return await safe_reply(m, texts.OWNER_ONLY)
67
+
68
+ parts = (m.text or "").split()
69
+ if len(parts) < 2 or not parts[1].isdigit():
70
+ return await safe_reply(m, "Usage: /disallow <user_id>")
71
+
72
+ target = int(parts[1])
73
+ j = await disallow_user(target)
74
+ await safe_reply(m, f"βœ… disallowed: `{target}`\n{j}")
75
+
76
+ # -------- Owner stats --------
77
+ @app.on_message(filters.command(["stats"]) & filters.private)
78
+ async def stats_cmd(_: Client, m: Message):
79
+ if not is_owner_id(m.from_user.id if m.from_user else None):
80
+ return await safe_reply(m, texts.OWNER_ONLY)
81
+ j = await stats_today()
82
+ await safe_reply(m, f"πŸ“Š Stats:\n`{j}`")
83
+
84
+ # -------- Auth flow (Worker1) --------
85
+ @app.on_message(filters.command(["auth"]) & filters.private)
86
+ async def auth_cmd(_: Client, m: Message):
87
+ uid = m.from_user.id if m.from_user else 0
88
+ ok = await require_allowed(uid)
89
+ if not ok:
90
+ return await safe_reply(m, texts.NOT_ALLOWED)
91
+
92
+ # expects: /auth <client_id> <client_secret>
93
+ parts = (m.text or "").split(maxsplit=2)
94
+ if len(parts) < 3:
95
+ return await safe_reply(m, "Usage:\n/auth <CLIENT_ID> <CLIENT_SECRET>\n(baad me JSON method add kar denge)")
96
+
97
+ client_id = parts[1].strip()
98
+ client_secret = parts[2].strip()
99
+
100
+ j = await profile_add(uid, client_id, client_secret, label="main", ttl_sec=600)
101
+ if not j.get("ok"):
102
+ return await safe_reply(m, f"❌ profile_add failed: `{j}`")
103
+
104
+ await safe_reply(m, texts.SENT_AUTH_LINK + j["login_url"])
105
+
106
+ @app.on_message(filters.command(["profiles"]) & filters.private)
107
+ async def profiles_cmd(_: Client, m: Message):
108
+ uid = m.from_user.id if m.from_user else 0
109
+ ok = await require_allowed(uid)
110
+ if not ok:
111
+ return await safe_reply(m, texts.NOT_ALLOWED)
112
+
113
+ j = await profile_list(uid)
114
+ if not j.get("ok"):
115
+ return await safe_reply(m, f"❌ profile_list failed: `{j}`")
116
+
117
+ profiles = j.get("profiles") or []
118
+ txt = f"Default: `{j.get('default_profile_id')}`\n\n"
119
+ for p in profiles:
120
+ txt += f"- `{p['profile_id']}` | {p.get('label')} | refresh={p.get('has_refresh')} | ch={p.get('channel_title')}\n"
121
+
122
+ kb = profiles_keyboard(profiles)
123
+ await safe_reply(m, txt, reply_markup=kb)
124
+
125
+ @app.on_callback_query()
126
+ async def cb(_: Client, q: CallbackQuery):
127
+ uid = q.from_user.id if q.from_user else 0
128
+ if not await require_allowed(uid):
129
+ return await q.answer("Not allowed", show_alert=True)
130
+
131
+ action, value = parse_cb(q.data or "")
132
+ if action == "setdef" and value:
133
+ j = await profile_set_default(uid, value)
134
+ if not j.get("ok"):
135
+ return await q.answer("Failed", show_alert=True)
136
+ await q.answer("Default set βœ…", show_alert=False)
137
+ try:
138
+ await q.message.edit_text(f"βœ… Default set: `{value}`")
139
+ except Exception:
140
+ pass
141
+ else:
142
+ await q.answer("OK")
143
+
144
+ # -------- Upload handler (send video/document) --------
145
+ @app.on_message(filters.private & (filters.video | filters.document))
146
+ async def upload_handler(app_: Client, m: Message):
147
+ uid = m.from_user.id if m.from_user else 0
148
+ ok = await require_allowed(uid)
149
+ if not ok:
150
+ return await safe_reply(m, texts.NOT_ALLOWED)
151
+
152
+ media, kind = extract_media_message(m)
153
+ if not media:
154
+ return await safe_reply(m, "Send a video/document.")
155
+
156
+ # find default profile + channel_id from worker1 list
157
+ pl = await profile_list(uid)
158
+ if not pl.get("ok"):
159
+ return await safe_reply(m, f"❌ profile_list failed: `{pl}`")
160
+
161
+ default_id = pl.get("default_profile_id")
162
+ profiles = pl.get("profiles") or []
163
+ dp = next((p for p in profiles if p.get("profile_id") == default_id), None)
164
+ if not dp or not dp.get("has_refresh"):
165
+ return await safe_reply(m, texts.NEED_AUTH)
166
+
167
+ channel_id = dp.get("channel_id")
168
+ if not channel_id:
169
+ return await safe_reply(m, "❌ channel_id missing. Re-auth profile.")
170
+
171
+ # pick profile (rotation same channel only)
172
+ pick = await pick_profile(uid, channel_id)
173
+ if not pick.get("ok"):
174
+ return await safe_reply(m, f"❌ pick_profile failed: `{pick}`")
175
+ use_profile_id = pick["profile_id"]
176
+
177
+ tok = await access_token(uid, use_profile_id)
178
+ if not tok.get("ok"):
179
+ return await safe_reply(m, f"❌ access_token failed: `{tok}`")
180
+ access_tok = tok["access_token"]
181
+
182
+ # status message
183
+ status = await safe_reply(m, texts.UPLOAD_START)
184
+
185
+ task_id = f"{uid}:{m.id}"
186
+ create_task(task_id, uid)
187
+ set_task(task_id, "downloading", "")
188
+
189
+ file_path = ""
190
+ try:
191
+ # download
192
+ file_path, file_size, file_name = await download_to_temp(app_, m)
193
+
194
+ # metadata rules
195
+ title, desc = extract_title_description(m, file_name)
196
+
197
+ # upload progress updates
198
+ se = SpeedETA()
199
+ last_edit = 0.0
200
+
201
+ async def prog(done: int, total: int):
202
+ nonlocal last_edit
203
+ snap = se.update(done, total)
204
+ now = snap["done"]
205
+ if not status:
206
+ return
207
+ # throttle edit
208
+ import time
209
+ if time.time() - last_edit < 2.0 and done < total:
210
+ return
211
+ last_edit = time.time()
212
+ txt = (
213
+ f"{texts.UPLOAD_TO_YT}\n"
214
+ f"{human_bytes(now)}/{human_bytes(total)}\n"
215
+ f"speed: {human_bytes(snap['speed_bps'])}/s | eta: {human_eta(snap['eta_sec'])}"
216
+ )
217
+ await safe_edit(status, txt)
218
+
219
+ set_task(task_id, "uploading", "")
220
+ yt_url = await upload_video(access_tok, file_path, title, desc, privacy="private", progress_cb=prog)
221
+
222
+ # record upload counter
223
+ await record_upload(uid, use_profile_id)
224
+
225
+ set_task(task_id, "done", "", yt_url=yt_url)
226
+ await safe_reply(m, f"{texts.DONE}\n\n🎬 {title}\nπŸ”— {yt_url}")
227
+
228
+ except Exception as e:
229
+ set_task(task_id, "error", str(e))
230
+ await safe_reply(m, f"❌ Upload failed: `{type(e).__name__}: {e}`")
231
+ finally:
232
+ if file_path:
233
+ cleanup_file(file_path)