AstraOS commited on
Commit
2acbcce
Β·
verified Β·
1 Parent(s): 008ea26

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +28 -139
app.py CHANGED
@@ -5,7 +5,6 @@ import time
5
  import datetime
6
  import traceback
7
  import fractions
8
- import requests
9
 
10
  from fastapi import FastAPI, Request
11
  import av
@@ -15,17 +14,12 @@ app = FastAPI()
15
  # -------------------------------------------------------------------
16
  # Configuration & Global Variables
17
  # -------------------------------------------------------------------
18
- # We no longer use a global TELEGRAM_API_URL variable.
19
- # The bot token is obtained on demand via the helper function below.
20
- # TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
21
- # if not TELEGRAM_BOT_TOKEN:
22
- # raise ValueError("Please set the TELEGRAM_BOT_TOKEN environment variable.")
23
-
24
  # Conversation state
25
  user_inputs = {}
26
- # The conversation fields will depend on the mode.
27
- # Simple mode (default): Only "input_url" and "output_url" are required.
28
- # Advanced mode (if user sends /setting): Additional fields are required.
29
  conversation_fields = []
30
  current_step = None
31
  advanced_mode = False
@@ -39,8 +33,8 @@ default_settings = {
39
  }
40
 
41
  # Streaming state & statistics
42
- streaming_state = "idle" # "idle", "streaming", "paused", "stopped"
43
- stream_chat_id = None # Chat ID for periodic updates
44
  stream_start_time = None
45
  frames_encoded = 0
46
  bytes_sent = 0
@@ -52,32 +46,9 @@ output_stream = None
52
 
53
  # Thread references
54
  stream_thread = None
55
- live_log_thread = None
56
-
57
- # Live logging globals
58
  live_log_lines = [] # Rolling list (max 50 lines)
59
- live_log_message_id = None
60
-
61
- # -------------------------------------------------------------------
62
- # Helper: Send Telegram API Request
63
- # -------------------------------------------------------------------
64
- def send_telegram_request(method: str, payload: dict):
65
- """
66
- Helper to send a request to the Telegram API.
67
- """
68
- bot_token = os.getenv("TELEGRAM_BOT_TOKEN")
69
- if not bot_token:
70
- logging.error("Missing TELEGRAM_BOT_TOKEN environment variable.")
71
- return None
72
- url = f"https://api.telegram.org/bot{bot_token}/{method}"
73
- try:
74
- response = requests.post(url, json=payload)
75
- if not response.ok:
76
- logging.error(f"Request to {url} failed with {response.text}")
77
- return response
78
- except Exception as e:
79
- logging.error(f"Error sending request to Telegram: {e}")
80
- return None
81
 
82
  # -------------------------------------------------------------------
83
  # Enhanced Logging Setup
@@ -108,12 +79,12 @@ logger.addHandler(list_handler)
108
  # Utility Functions & UI Helpers
109
  # -------------------------------------------------------------------
110
  def create_html_message(text: str):
111
- # Wrap text in <pre> tags for monospaced output (HTML parse mode)
112
  return {"parse_mode": "HTML", "text": f"<pre>{text}</pre>"}
113
 
114
  def get_inline_keyboard_for_stream():
115
- # Inline keyboard for streaming controls after the stream has started
116
- keyboard = {
117
  "inline_keyboard": [
118
  [
119
  {"text": "⏸ Pause", "callback_data": "pause"},
@@ -125,18 +96,16 @@ def get_inline_keyboard_for_stream():
125
  ]
126
  ]
127
  }
128
- return keyboard
129
 
130
  def get_inline_keyboard_for_start():
131
- # Inline keyboard with a start button for when conversation is complete.
132
- keyboard = {
133
  "inline_keyboard": [
134
  [
135
  {"text": "πŸš€ Start Streaming", "callback_data": "start_stream"}
136
  ]
137
  ]
138
  }
139
- return keyboard
140
 
141
  def help_text():
142
  return (
@@ -175,63 +144,25 @@ def get_uptime():
175
  return "0"
176
 
177
  def validate_inputs():
178
- # Ensure all fields in conversation_fields have been provided
179
  missing = [field for field in conversation_fields if field not in user_inputs or not user_inputs[field]]
180
  if missing:
181
  return False, f"Missing fields: {', '.join(missing)}"
182
  return True, ""
183
 
184
  # -------------------------------------------------------------------
185
- # Notify Error Helper
186
  # -------------------------------------------------------------------
187
  def notify_error(chat_id, error_message):
188
- payload = {
189
- "chat_id": chat_id,
190
- "text": f"⚠️ *Streaming Error Occurred:*\n\n{error_message}\n\nPlease check the live logs for details.",
191
- "parse_mode": "Markdown"
192
- }
193
- send_telegram_request("sendMessage", payload)
194
-
195
- # -------------------------------------------------------------------
196
- # Live Logging Updater (Background Thread)
197
- # -------------------------------------------------------------------
198
- def live_log_updater(chat_id):
199
- global live_log_message_id, streaming_state
200
- try:
201
- # Send initial live log message in HTML format
202
- payload = {
203
- "chat_id": chat_id,
204
- "text": "<pre>Live Logs:\nInitializing...</pre>",
205
- "parse_mode": "HTML"
206
- }
207
- resp = send_telegram_request("sendMessage", payload)
208
- if resp and resp.ok:
209
- live_log_message_id = resp.json()["result"]["message_id"]
210
- logging.info(f"Live log message sent with id {live_log_message_id}")
211
- else:
212
- logging.error("Failed to send live log message.")
213
- return
214
-
215
- # Update live log every 1 second until streaming stops
216
- while streaming_state in ["streaming", "paused"]:
217
- # Show the last 15 log lines in HTML format
218
- log_text = "<pre>" + "\n".join(live_log_lines[-15:]) + "</pre>"
219
- edit_payload = {
220
- "chat_id": chat_id,
221
- "message_id": live_log_message_id,
222
- "text": log_text,
223
- "parse_mode": "HTML"
224
- }
225
- send_telegram_request("editMessageText", edit_payload)
226
- time.sleep(1)
227
- except Exception as e:
228
- logging.error(f"Error in live log updater: {e}")
229
 
230
  # -------------------------------------------------------------------
231
  # Logs History Handler (/logs)
232
  # -------------------------------------------------------------------
233
  def logs_history(chat_id):
234
- # Show the last 50 log lines in HTML format
235
  log_text = "<pre>" + "\n".join(live_log_lines[-50:]) + "</pre>"
236
  return {
237
  "method": "sendMessage",
@@ -245,17 +176,18 @@ def logs_history(chat_id):
245
  # -------------------------------------------------------------------
246
  def handle_start(chat_id):
247
  global current_step, user_inputs, conversation_fields, advanced_mode
248
- # By default, simple mode (unless advanced_mode was set via /setting)
249
  user_inputs = {}
250
  if not advanced_mode:
251
  conversation_fields = ["input_url", "output_url"]
252
  else:
253
  conversation_fields = ["input_url", "quality_settings", "video_codec", "audio_codec", "output_url"]
254
  current_step = conversation_fields[0]
255
- text = ("πŸ‘‹ *Welcome to the Stream Bot!*\n\n"
256
- "Let's set up your stream.\n"
257
- f"Please enter the *{current_step.replace('_', ' ')}*"
258
- f"{' (no default)' if current_step not in default_settings else f' _(default: {default_settings[current_step]})_'}:")
 
 
259
  logging.info(f"/start command from chat {chat_id} (advanced_mode={advanced_mode})")
260
  return {
261
  "method": "sendMessage",
@@ -270,8 +202,7 @@ def handle_setting(chat_id):
270
  conversation_fields = ["input_url", "quality_settings", "video_codec", "audio_codec", "output_url"]
271
  user_inputs = {}
272
  current_step = conversation_fields[0]
273
- text = ("βš™οΈ *Advanced Mode Activated!*\n\n"
274
- "Please enter the *input url*:")
275
  logging.info(f"/setting command from chat {chat_id} - advanced mode enabled")
276
  return {
277
  "method": "sendMessage",
@@ -292,14 +223,12 @@ def handle_help(chat_id):
292
  def handle_conversation(chat_id, text):
293
  global current_step, user_inputs, conversation_fields
294
  if current_step:
295
- # If the response is empty and a default exists, use the default.
296
  if text.strip() == "" and current_step in default_settings:
297
  user_inputs[current_step] = default_settings[current_step]
298
  logging.info(f"Using default for {current_step}: {default_settings[current_step]}")
299
  else:
300
  user_inputs[current_step] = text.strip()
301
  logging.info(f"Received {current_step}: {text.strip()}")
302
-
303
  idx = conversation_fields.index(current_step)
304
  if idx < len(conversation_fields) - 1:
305
  current_step = conversation_fields[idx + 1]
@@ -308,17 +237,14 @@ def handle_conversation(chat_id, text):
308
  prompt += f" _(default: {default_settings[current_step]})_"
309
  return send_guide_message(chat_id, prompt)
310
  else:
311
- # All inputs have been collected.
312
  current_step = None
313
  valid, msg = validate_inputs()
314
  if not valid:
315
  return send_guide_message(chat_id, f"Validation error: {msg}")
316
- # In simple mode, fill in advanced fields with defaults.
317
  if not advanced_mode:
318
  user_inputs.setdefault("quality_settings", default_settings["quality_settings"])
319
  user_inputs.setdefault("video_codec", default_settings["video_codec"])
320
  user_inputs.setdefault("audio_codec", default_settings["audio_codec"])
321
- # Instead of asking the user to type "start", send an inline button.
322
  return {
323
  "method": "sendMessage",
324
  "chat_id": chat_id,
@@ -338,17 +264,13 @@ def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, out
338
  try:
339
  streaming_state = "streaming"
340
  reset_statistics()
341
-
342
  input_stream = av.open(input_url)
343
  output_stream = av.open(output_url, mode='w', format='flv')
344
-
345
- # Configure video stream
346
  video_stream = output_stream.add_stream(video_codec, rate=30)
347
  video_stream.width = input_stream.streams.video[0].width
348
  video_stream.height = input_stream.streams.video[0].height
349
  video_stream.pix_fmt = input_stream.streams.video[0].format.name
350
  video_stream.codec_context.options.update({'g': '30'})
351
-
352
  if quality_settings.lower() == "high":
353
  video_stream.bit_rate = 3000000
354
  video_stream.bit_rate_tolerance = 1000000
@@ -358,17 +280,11 @@ def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, out
358
  elif quality_settings.lower() == "low":
359
  video_stream.bit_rate = 800000
360
  video_stream.bit_rate_tolerance = 200000
361
-
362
- # Configure audio stream
363
  audio_stream_in = input_stream.streams.audio[0]
364
  out_audio_stream = output_stream.add_stream(audio_codec, rate=audio_stream_in.rate)
365
  out_audio_stream.layout = "stereo"
366
-
367
  video_stream.codec_context.time_base = fractions.Fraction(1, video_stream.rate)
368
-
369
  logging.info("Streaming started successfully.")
370
-
371
- # Stream loop: process packets until state changes
372
  while streaming_state in ["streaming", "paused"]:
373
  for packet in input_stream.demux():
374
  if streaming_state == "stopped":
@@ -392,17 +308,12 @@ def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, out
392
  output_stream.mux(out_packet)
393
  if hasattr(out_packet, "size"):
394
  bytes_sent += out_packet.size
395
-
396
- # Flush remaining packets
397
  for out_packet in video_stream.encode():
398
  output_stream.mux(out_packet)
399
  for out_packet in out_audio_stream.encode():
400
  output_stream.mux(out_packet)
401
-
402
  if streaming_state == "paused":
403
  time.sleep(1)
404
-
405
- # Clean up resources
406
  try:
407
  video_stream.close()
408
  out_audio_stream.close()
@@ -410,25 +321,21 @@ def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, out
410
  input_stream.close()
411
  except Exception as cleanup_error:
412
  logging.error(f"Error during cleanup: {cleanup_error}")
413
-
414
  logging.info("Streaming complete, resources cleaned up.")
415
  streaming_state = "idle"
416
  except Exception as e:
417
  error_message = f"An error occurred during streaming: {str(e)}\n\n{traceback.format_exc()}"
418
  logging.error(error_message)
419
  streaming_state = "idle"
420
- # Notify the user about the error using the live logs
421
  notify_error(chat_id, error_message)
422
 
423
  def start_streaming(chat_id):
424
- global stream_thread, live_log_thread, stream_chat_id
425
  valid, msg = validate_inputs()
426
  if not valid:
427
  return send_guide_message(chat_id, f"Validation error: {msg}")
428
-
429
  stream_chat_id = chat_id
430
  try:
431
- # Start the background streaming thread
432
  stream_thread = threading.Thread(
433
  target=stream_to_youtube,
434
  args=(
@@ -443,18 +350,10 @@ def start_streaming(chat_id):
443
  stream_thread.daemon = True
444
  stream_thread.start()
445
  logging.info("Streaming thread started.")
446
-
447
- # Start the live log updater thread (updates every 1 second in HTML format)
448
- live_log_thread = threading.Thread(target=live_log_updater, args=(chat_id,))
449
- live_log_thread.daemon = True
450
- live_log_thread.start()
451
- logging.info("Live log updater started.")
452
-
453
- # Immediately inform the user that streaming has started
454
  return {
455
  "method": "sendMessage",
456
  "chat_id": chat_id,
457
- "text": "πŸš€ *Streaming initiated!* Live logs are now updating below. Use the inline keyboard to control the stream.",
458
  "reply_markup": get_inline_keyboard_for_stream(),
459
  "parse_mode": "Markdown"
460
  }
@@ -528,12 +427,9 @@ def stream_status(chat_id):
528
  async def telegram_webhook(request: Request):
529
  update = await request.json()
530
  logging.debug(f"Received update: {update}")
531
-
532
- # Process messages from users
533
  if "message" in update:
534
  chat_id = update["message"]["chat"]["id"]
535
  text = update["message"].get("text", "").strip()
536
-
537
  if text.startswith("/setting"):
538
  return handle_setting(chat_id)
539
  elif text.startswith("/start"):
@@ -551,15 +447,11 @@ async def telegram_webhook(request: Request):
551
  elif text.startswith("/status"):
552
  return stream_status(chat_id)
553
  else:
554
- # Process conversation setup inputs
555
  return handle_conversation(chat_id, text)
556
-
557
- # Process inline keyboard callback queries
558
  elif "callback_query" in update:
559
  callback_data = update["callback_query"]["data"]
560
  chat_id = update["callback_query"]["message"]["chat"]["id"]
561
  message_id = update["callback_query"]["message"]["message_id"]
562
-
563
  if callback_data == "pause":
564
  response = pause_stream(chat_id)
565
  elif callback_data == "resume":
@@ -572,10 +464,7 @@ async def telegram_webhook(request: Request):
572
  response = start_streaming(chat_id)
573
  else:
574
  response = send_guide_message(chat_id, "❓ Unknown callback command.")
575
-
576
- # Edit the original message with updated information
577
  response["method"] = "editMessageText"
578
  response["message_id"] = message_id
579
  return response
580
-
581
  return {"status": "ok"}
 
5
  import datetime
6
  import traceback
7
  import fractions
 
8
 
9
  from fastapi import FastAPI, Request
10
  import av
 
14
  # -------------------------------------------------------------------
15
  # Configuration & Global Variables
16
  # -------------------------------------------------------------------
17
+ # (No explicit token usage here; all responses are returned as JSON.)
 
 
 
 
 
18
  # Conversation state
19
  user_inputs = {}
20
+ # Conversation fields depend on mode:
21
+ # β€’ Simple mode (default): Only "input_url" and "output_url" are required.
22
+ # β€’ Advanced mode (triggered by /setting): Additional fields are required.
23
  conversation_fields = []
24
  current_step = None
25
  advanced_mode = False
 
33
  }
34
 
35
  # Streaming state & statistics
36
+ streaming_state = "idle" # Possible values: "idle", "streaming", "paused", "stopped"
37
+ stream_chat_id = None # Chat ID for the session
38
  stream_start_time = None
39
  frames_encoded = 0
40
  bytes_sent = 0
 
46
 
47
  # Thread references
48
  stream_thread = None
49
+ # (No live log updater thread is launched because outgoing calls are not allowed.)
50
+ # Instead, live logs are maintained in a global variable.
 
51
  live_log_lines = [] # Rolling list (max 50 lines)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
  # -------------------------------------------------------------------
54
  # Enhanced Logging Setup
 
79
  # Utility Functions & UI Helpers
80
  # -------------------------------------------------------------------
81
  def create_html_message(text: str):
82
+ # Returns a response dict with HTML parse mode and wrapped in <pre> for monospaced output.
83
  return {"parse_mode": "HTML", "text": f"<pre>{text}</pre>"}
84
 
85
  def get_inline_keyboard_for_stream():
86
+ # Inline keyboard for controlling the stream.
87
+ return {
88
  "inline_keyboard": [
89
  [
90
  {"text": "⏸ Pause", "callback_data": "pause"},
 
96
  ]
97
  ]
98
  }
 
99
 
100
  def get_inline_keyboard_for_start():
101
+ # Inline keyboard with a single "Start Streaming" button.
102
+ return {
103
  "inline_keyboard": [
104
  [
105
  {"text": "πŸš€ Start Streaming", "callback_data": "start_stream"}
106
  ]
107
  ]
108
  }
 
109
 
110
  def help_text():
111
  return (
 
144
  return "0"
145
 
146
  def validate_inputs():
147
+ # Ensure that every required field is provided.
148
  missing = [field for field in conversation_fields if field not in user_inputs or not user_inputs[field]]
149
  if missing:
150
  return False, f"Missing fields: {', '.join(missing)}"
151
  return True, ""
152
 
153
  # -------------------------------------------------------------------
154
+ # Error Handling Helper
155
  # -------------------------------------------------------------------
156
  def notify_error(chat_id, error_message):
157
+ # Instead of making an outgoing call, we simply log the error.
158
+ # The error will appear in the global log history (and can be retrieved via /logs).
159
+ logging.error(f"Notify user (chat {chat_id}) about error: {error_message}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
  # -------------------------------------------------------------------
162
  # Logs History Handler (/logs)
163
  # -------------------------------------------------------------------
164
  def logs_history(chat_id):
165
+ # Return the last 50 log lines in HTML format.
166
  log_text = "<pre>" + "\n".join(live_log_lines[-50:]) + "</pre>"
167
  return {
168
  "method": "sendMessage",
 
176
  # -------------------------------------------------------------------
177
  def handle_start(chat_id):
178
  global current_step, user_inputs, conversation_fields, advanced_mode
 
179
  user_inputs = {}
180
  if not advanced_mode:
181
  conversation_fields = ["input_url", "output_url"]
182
  else:
183
  conversation_fields = ["input_url", "quality_settings", "video_codec", "audio_codec", "output_url"]
184
  current_step = conversation_fields[0]
185
+ text = (
186
+ "πŸ‘‹ *Welcome to the Stream Bot!*\n\n"
187
+ "Let's set up your stream.\n"
188
+ f"Please enter the *{current_step.replace('_', ' ')}*"
189
+ f"{' (no default)' if current_step not in default_settings else f' _(default: {default_settings[current_step]})_'}:"
190
+ )
191
  logging.info(f"/start command from chat {chat_id} (advanced_mode={advanced_mode})")
192
  return {
193
  "method": "sendMessage",
 
202
  conversation_fields = ["input_url", "quality_settings", "video_codec", "audio_codec", "output_url"]
203
  user_inputs = {}
204
  current_step = conversation_fields[0]
205
+ text = "βš™οΈ *Advanced Mode Activated!*\n\nPlease enter the *input url*:"
 
206
  logging.info(f"/setting command from chat {chat_id} - advanced mode enabled")
207
  return {
208
  "method": "sendMessage",
 
223
  def handle_conversation(chat_id, text):
224
  global current_step, user_inputs, conversation_fields
225
  if current_step:
 
226
  if text.strip() == "" and current_step in default_settings:
227
  user_inputs[current_step] = default_settings[current_step]
228
  logging.info(f"Using default for {current_step}: {default_settings[current_step]}")
229
  else:
230
  user_inputs[current_step] = text.strip()
231
  logging.info(f"Received {current_step}: {text.strip()}")
 
232
  idx = conversation_fields.index(current_step)
233
  if idx < len(conversation_fields) - 1:
234
  current_step = conversation_fields[idx + 1]
 
237
  prompt += f" _(default: {default_settings[current_step]})_"
238
  return send_guide_message(chat_id, prompt)
239
  else:
 
240
  current_step = None
241
  valid, msg = validate_inputs()
242
  if not valid:
243
  return send_guide_message(chat_id, f"Validation error: {msg}")
 
244
  if not advanced_mode:
245
  user_inputs.setdefault("quality_settings", default_settings["quality_settings"])
246
  user_inputs.setdefault("video_codec", default_settings["video_codec"])
247
  user_inputs.setdefault("audio_codec", default_settings["audio_codec"])
 
248
  return {
249
  "method": "sendMessage",
250
  "chat_id": chat_id,
 
264
  try:
265
  streaming_state = "streaming"
266
  reset_statistics()
 
267
  input_stream = av.open(input_url)
268
  output_stream = av.open(output_url, mode='w', format='flv')
 
 
269
  video_stream = output_stream.add_stream(video_codec, rate=30)
270
  video_stream.width = input_stream.streams.video[0].width
271
  video_stream.height = input_stream.streams.video[0].height
272
  video_stream.pix_fmt = input_stream.streams.video[0].format.name
273
  video_stream.codec_context.options.update({'g': '30'})
 
274
  if quality_settings.lower() == "high":
275
  video_stream.bit_rate = 3000000
276
  video_stream.bit_rate_tolerance = 1000000
 
280
  elif quality_settings.lower() == "low":
281
  video_stream.bit_rate = 800000
282
  video_stream.bit_rate_tolerance = 200000
 
 
283
  audio_stream_in = input_stream.streams.audio[0]
284
  out_audio_stream = output_stream.add_stream(audio_codec, rate=audio_stream_in.rate)
285
  out_audio_stream.layout = "stereo"
 
286
  video_stream.codec_context.time_base = fractions.Fraction(1, video_stream.rate)
 
287
  logging.info("Streaming started successfully.")
 
 
288
  while streaming_state in ["streaming", "paused"]:
289
  for packet in input_stream.demux():
290
  if streaming_state == "stopped":
 
308
  output_stream.mux(out_packet)
309
  if hasattr(out_packet, "size"):
310
  bytes_sent += out_packet.size
 
 
311
  for out_packet in video_stream.encode():
312
  output_stream.mux(out_packet)
313
  for out_packet in out_audio_stream.encode():
314
  output_stream.mux(out_packet)
 
315
  if streaming_state == "paused":
316
  time.sleep(1)
 
 
317
  try:
318
  video_stream.close()
319
  out_audio_stream.close()
 
321
  input_stream.close()
322
  except Exception as cleanup_error:
323
  logging.error(f"Error during cleanup: {cleanup_error}")
 
324
  logging.info("Streaming complete, resources cleaned up.")
325
  streaming_state = "idle"
326
  except Exception as e:
327
  error_message = f"An error occurred during streaming: {str(e)}\n\n{traceback.format_exc()}"
328
  logging.error(error_message)
329
  streaming_state = "idle"
 
330
  notify_error(chat_id, error_message)
331
 
332
  def start_streaming(chat_id):
333
+ global stream_thread, stream_chat_id
334
  valid, msg = validate_inputs()
335
  if not valid:
336
  return send_guide_message(chat_id, f"Validation error: {msg}")
 
337
  stream_chat_id = chat_id
338
  try:
 
339
  stream_thread = threading.Thread(
340
  target=stream_to_youtube,
341
  args=(
 
350
  stream_thread.daemon = True
351
  stream_thread.start()
352
  logging.info("Streaming thread started.")
 
 
 
 
 
 
 
 
353
  return {
354
  "method": "sendMessage",
355
  "chat_id": chat_id,
356
+ "text": "πŸš€ *Streaming initiated!* Live logs are available via the /logs command. Use the inline keyboard to control the stream.",
357
  "reply_markup": get_inline_keyboard_for_stream(),
358
  "parse_mode": "Markdown"
359
  }
 
427
  async def telegram_webhook(request: Request):
428
  update = await request.json()
429
  logging.debug(f"Received update: {update}")
 
 
430
  if "message" in update:
431
  chat_id = update["message"]["chat"]["id"]
432
  text = update["message"].get("text", "").strip()
 
433
  if text.startswith("/setting"):
434
  return handle_setting(chat_id)
435
  elif text.startswith("/start"):
 
447
  elif text.startswith("/status"):
448
  return stream_status(chat_id)
449
  else:
 
450
  return handle_conversation(chat_id, text)
 
 
451
  elif "callback_query" in update:
452
  callback_data = update["callback_query"]["data"]
453
  chat_id = update["callback_query"]["message"]["chat"]["id"]
454
  message_id = update["callback_query"]["message"]["message_id"]
 
455
  if callback_data == "pause":
456
  response = pause_stream(chat_id)
457
  elif callback_data == "resume":
 
464
  response = start_streaming(chat_id)
465
  else:
466
  response = send_guide_message(chat_id, "❓ Unknown callback command.")
 
 
467
  response["method"] = "editMessageText"
468
  response["message_id"] = message_id
469
  return response
 
470
  return {"status": "ok"}