Mafia2008 commited on
Commit
b351d03
Β·
verified Β·
1 Parent(s): 219e24c

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +65 -76
main.py CHANGED
@@ -1,6 +1,8 @@
1
  import os
2
  import math
3
  import logging
 
 
4
  from contextlib import asynccontextmanager
5
  from pyrogram import Client
6
  from pyrogram.session import Session, Auth
@@ -10,10 +12,34 @@ from fastapi import FastAPI, Request, HTTPException
10
  from fastapi.responses import StreamingResponse
11
  import uvicorn
12
 
13
- # --- 1. ENABLE DEBUG LOGGING ---
14
- logging.basicConfig(level=logging.INFO)
15
- logger = logging.getLogger("pyrogram")
16
- logger.setLevel(logging.WARNING) # Switch to INFO if you need deeper debug
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  # --- CONFIGURATION ---
19
  try:
@@ -22,10 +48,10 @@ try:
22
  BOT_TOKEN = os.environ.get("BOT_TOKEN")
23
  STORAGE_CHANNEL = int(os.environ.get("STORAGE_CHANNEL"))
24
  except ValueError:
25
- print("❌ ERROR: Env vars are missing or invalid! Check API_ID/STORAGE_CHANNEL.")
26
  exit(1)
27
 
28
- # --- BOT CLIENT (HARDENED) ---
29
  client = Client(
30
  "worker_bot",
31
  api_id=API_ID,
@@ -33,41 +59,41 @@ client = Client(
33
  bot_token=BOT_TOKEN,
34
  in_memory=True,
35
  no_updates=True,
36
- ipv6=False, # FORCE IPv4
37
- workdir="/tmp" # FORCE WRITABLE DIR
38
  )
39
 
40
  @asynccontextmanager
41
  async def lifespan(app: FastAPI):
42
- print(f"⏳ Connecting to Telegram with API_ID: {API_ID}...")
43
- try:
44
- await client.start()
45
- me = await client.get_me()
46
- print(f"βœ… Worker Started as: {me.first_name} (@{me.username})")
47
- except Exception as e:
48
- print(f"❌ CRITICAL CONNECTION ERROR: {e}")
49
- # We don't exit here so the web server keeps running to show logs
50
 
 
 
 
 
 
 
 
 
 
 
 
51
  yield
52
 
53
- print("πŸ›‘ Stopping Worker...")
54
  try:
55
- if client.is_connected:
56
- await client.stop()
57
- except:
58
- pass
59
 
60
  app = FastAPI(lifespan=lifespan)
61
 
62
  # --- STREAMING LOGIC ---
63
  class ByteStreamer:
64
- def __init__(self, client: Client):
65
- self.client = client
66
-
67
  async def yield_file(self, file_id, offset, first_part_cut, last_part_cut, part_count, chunk_size):
68
  client = self.client
 
69
 
70
- # Safe Session Getting
71
  try:
72
  ms = client.media_sessions.get(file_id.dc_id)
73
  if not ms:
@@ -75,90 +101,53 @@ class ByteStreamer:
75
  auth_key = await Auth(client, file_id.dc_id, await client.storage.test_mode()).create()
76
  ms = Session(client, file_id.dc_id, auth_key, await client.storage.test_mode(), is_media=True)
77
  await ms.start()
78
- else:
79
- ms = client.session
80
  client.media_sessions[file_id.dc_id] = ms
81
- except Exception as e:
82
- print(f"Session Error: {e}")
83
- return
84
 
85
- loc = raw.types.InputDocumentFileLocation(
86
- id=file_id.media_id,
87
- access_hash=file_id.access_hash,
88
- file_reference=file_id.file_reference,
89
- thumb_size=file_id.thumbnail_size
90
- )
91
-
92
  curr = 1
93
  while curr <= part_count:
94
  try:
95
- r = await ms.invoke(
96
- raw.functions.upload.GetFile(location=loc, offset=offset, limit=chunk_size),
97
- retries=3
98
- )
99
  if isinstance(r, raw.types.upload.File):
100
  chunk = r.bytes
101
  if not chunk: break
102
-
103
  if part_count == 1: yield chunk[first_part_cut:last_part_cut]
104
  elif curr == 1: yield chunk[first_part_cut:]
105
  elif curr == part_count: yield chunk[:last_part_cut]
106
  else: yield chunk
107
-
108
  curr += 1
109
  offset += chunk_size
110
  else: break
111
- except Exception as e:
112
- print(f"Chunk Error: {e}")
113
- break
114
 
115
  @app.get("/stream/{message_id}/{filename}")
116
  async def stream_handler(req: Request, message_id: int, filename: str):
117
- if not client.is_connected:
118
- # Try to reconnect if startup failed
119
- try: await client.start()
120
- except: raise HTTPException(500, "Bot failed to connect to Telegram.")
121
-
122
  try:
123
  msg = await client.get_messages(STORAGE_CHANNEL, message_id)
124
  media = msg.document or msg.video or msg.audio
125
-
126
  if not media: raise HTTPException(404, "File not found")
127
-
128
  file_id = FileId.decode(media.file_id)
129
  file_size = media.file_size
130
-
131
  range_header = req.headers.get("Range", 0)
132
  from_bytes, until_bytes = 0, file_size - 1
133
  if range_header:
134
  s = range_header.replace("bytes=", "").split("-")
135
  from_bytes = int(s[0])
136
  if s[1]: until_bytes = int(s[1])
137
-
138
  req_len = until_bytes - from_bytes + 1
139
- chunk_size = 1048576
140
-
141
- offset = (from_bytes // chunk_size) * chunk_size
142
- first_part_cut = from_bytes - offset
143
- last_part_cut = (until_bytes % chunk_size) + 1
144
- part_count = math.ceil(req_len / chunk_size)
145
-
146
  streamer = ByteStreamer(client)
147
- body = streamer.yield_file(file_id, offset, first_part_cut, last_part_cut, part_count, chunk_size)
148
-
149
- headers = {
150
- "Content-Type": media.mime_type or "application/octet-stream",
151
- "Content-Disposition": f'inline; filename="{media.file_name}"',
152
- "Accept-Ranges": "bytes",
153
- "Content-Range": f"bytes {from_bytes}-{until_bytes}/{file_size}",
154
- "Content-Length": str(req_len),
155
- }
156
  return StreamingResponse(body, status_code=206 if range_header else 200, headers=headers)
157
-
158
- except Exception as e:
159
- print(f"Stream Error: {e}")
160
- raise HTTPException(404, "File Not Found")
161
 
162
  if __name__ == "__main__":
163
  uvicorn.run(app, host="0.0.0.0", port=7860)
164
-
 
1
  import os
2
  import math
3
  import logging
4
+ import socket
5
+ import requests # Make sure 'requests' is in requirements.txt
6
  from contextlib import asynccontextmanager
7
  from pyrogram import Client
8
  from pyrogram.session import Session, Auth
 
12
  from fastapi.responses import StreamingResponse
13
  import uvicorn
14
 
15
+ # --- 1. GLOBAL IPv4 PATCH (The "Nuclear Option") ---
16
+ # This forces the entire Python script to ignore IPv6.
17
+ # If this doesn't fix it, your Space IP is blacklisted.
18
+ def force_ipv4():
19
+ old_getaddrinfo = socket.getaddrinfo
20
+ def new_getaddrinfo(*args, **kwargs):
21
+ responses = old_getaddrinfo(*args, **kwargs)
22
+ return [response for response in responses if response[0] == socket.AF_INET]
23
+ socket.getaddrinfo = new_getaddrinfo
24
+
25
+ force_ipv4()
26
+
27
+ # --- 2. HTTP CONNECTIVITY CHECK ---
28
+ def check_telegram_api():
29
+ print("πŸ“‘ TEST: Checking connection to Telegram API via HTTP...")
30
+ try:
31
+ # We try to reach the standard HTTP API.
32
+ # If this fails, the NETWORK is blocked.
33
+ r = requests.get(f"https://api.telegram.org/bot{os.environ.get('BOT_TOKEN')}/getMe", timeout=10)
34
+ if r.status_code == 200:
35
+ print("βœ… HTTP TEST PASSED: Network is fine. Bot Token is valid.")
36
+ return True
37
+ else:
38
+ print(f"⚠️ HTTP TEST FAILED: Status {r.status_code} - {r.text}")
39
+ return False
40
+ except Exception as e:
41
+ print(f"❌ HTTP TEST FAILED: Could not reach Telegram. The IP is likely blocked. Error: {e}")
42
+ return False
43
 
44
  # --- CONFIGURATION ---
45
  try:
 
48
  BOT_TOKEN = os.environ.get("BOT_TOKEN")
49
  STORAGE_CHANNEL = int(os.environ.get("STORAGE_CHANNEL"))
50
  except ValueError:
51
+ print("❌ ERROR: Env vars are missing or invalid!")
52
  exit(1)
53
 
54
+ # --- BOT CLIENT ---
55
  client = Client(
56
  "worker_bot",
57
  api_id=API_ID,
 
59
  bot_token=BOT_TOKEN,
60
  in_memory=True,
61
  no_updates=True,
62
+ ipv6=False,
63
+ workdir="/tmp"
64
  )
65
 
66
  @asynccontextmanager
67
  async def lifespan(app: FastAPI):
68
+ # Run the diagnostic
69
+ network_ok = check_telegram_api()
 
 
 
 
 
 
70
 
71
+ if network_ok:
72
+ print("⏳ Connecting via Pyrogram (MTProto)...")
73
+ try:
74
+ await client.start()
75
+ me = await client.get_me()
76
+ print(f"βœ… Worker Started as: {me.first_name} (@{me.username})")
77
+ except Exception as e:
78
+ print(f"❌ PYROGRAM ERROR: HTTP worked, but MTProto failed. {e}")
79
+ else:
80
+ print("πŸ›‘ ABORTING: Cannot reach Telegram API.")
81
+
82
  yield
83
 
 
84
  try:
85
+ if client.is_connected: await client.stop()
86
+ except: pass
 
 
87
 
88
  app = FastAPI(lifespan=lifespan)
89
 
90
  # --- STREAMING LOGIC ---
91
  class ByteStreamer:
92
+ def __init__(self, client: Client): self.client = client
 
 
93
  async def yield_file(self, file_id, offset, first_part_cut, last_part_cut, part_count, chunk_size):
94
  client = self.client
95
+ if not client.is_connected: return
96
 
 
97
  try:
98
  ms = client.media_sessions.get(file_id.dc_id)
99
  if not ms:
 
101
  auth_key = await Auth(client, file_id.dc_id, await client.storage.test_mode()).create()
102
  ms = Session(client, file_id.dc_id, auth_key, await client.storage.test_mode(), is_media=True)
103
  await ms.start()
104
+ else: ms = client.session
 
105
  client.media_sessions[file_id.dc_id] = ms
106
+ except: return
 
 
107
 
108
+ loc = raw.types.InputDocumentFileLocation(id=file_id.media_id, access_hash=file_id.access_hash, file_reference=file_id.file_reference, thumb_size=file_id.thumbnail_size)
 
 
 
 
 
 
109
  curr = 1
110
  while curr <= part_count:
111
  try:
112
+ r = await ms.invoke(raw.functions.upload.GetFile(location=loc, offset=offset, limit=chunk_size), retries=3)
 
 
 
113
  if isinstance(r, raw.types.upload.File):
114
  chunk = r.bytes
115
  if not chunk: break
 
116
  if part_count == 1: yield chunk[first_part_cut:last_part_cut]
117
  elif curr == 1: yield chunk[first_part_cut:]
118
  elif curr == part_count: yield chunk[:last_part_cut]
119
  else: yield chunk
 
120
  curr += 1
121
  offset += chunk_size
122
  else: break
123
+ except: break
 
 
124
 
125
  @app.get("/stream/{message_id}/{filename}")
126
  async def stream_handler(req: Request, message_id: int, filename: str):
127
+ if not client.is_connected: raise HTTPException(503, "Bot not connected to Telegram")
 
 
 
 
128
  try:
129
  msg = await client.get_messages(STORAGE_CHANNEL, message_id)
130
  media = msg.document or msg.video or msg.audio
 
131
  if not media: raise HTTPException(404, "File not found")
 
132
  file_id = FileId.decode(media.file_id)
133
  file_size = media.file_size
 
134
  range_header = req.headers.get("Range", 0)
135
  from_bytes, until_bytes = 0, file_size - 1
136
  if range_header:
137
  s = range_header.replace("bytes=", "").split("-")
138
  from_bytes = int(s[0])
139
  if s[1]: until_bytes = int(s[1])
 
140
  req_len = until_bytes - from_bytes + 1
141
+ chunk = 1048576
142
+ offset = (from_bytes // chunk) * chunk
143
+ first_cut = from_bytes - offset
144
+ last_cut = (until_bytes % chunk) + 1
145
+ parts = math.ceil(req_len / chunk)
 
 
146
  streamer = ByteStreamer(client)
147
+ body = streamer.yield_file(file_id, offset, first_cut, last_cut, parts, chunk)
148
+ headers = {"Content-Type": media.mime_type or "application/octet-stream", "Content-Disposition": f'inline; filename="{media.file_name}"', "Accept-Ranges": "bytes", "Content-Range": f"bytes {from_bytes}-{until_bytes}/{file_size}", "Content-Length": str(req_len)}
 
 
 
 
 
 
 
149
  return StreamingResponse(body, status_code=206 if range_header else 200, headers=headers)
150
+ except: raise HTTPException(404, "File Not Found")
 
 
 
151
 
152
  if __name__ == "__main__":
153
  uvicorn.run(app, host="0.0.0.0", port=7860)