AstraOS commited on
Commit
4a0b1c1
·
verified ·
1 Parent(s): e0d8f08

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +78 -67
app.py CHANGED
@@ -85,7 +85,7 @@ def create_html_message(text: str):
85
  return {"parse_mode": "HTML", "text": f"<pre>{text}</pre>"}
86
 
87
  def get_inline_keyboard_for_stream():
88
- # Inline keyboard for streaming controls (when streaming is active)
89
  keyboard = {
90
  "inline_keyboard": [
91
  [
@@ -101,7 +101,7 @@ def get_inline_keyboard_for_stream():
101
  return keyboard
102
 
103
  def get_inline_keyboard_for_start():
104
- # Inline keyboard with a start button (when inputs are complete)
105
  keyboard = {
106
  "inline_keyboard": [
107
  [
@@ -117,7 +117,7 @@ def help_text():
117
  "*/start* - Begin setup for streaming (simple mode: only Input & Output URL)\n"
118
  "*/setting* - Enter advanced settings (Input URL, Quality Settings, Video Codec, Audio Codec, Output URL)\n"
119
  "*/help* - Display this help text\n"
120
- "*/logs* - Show the live log history\n\n"
121
  "After inputs are collected, press the inline *Start Streaming* button.\n\n"
122
  "While streaming, you can use inline buttons or commands:\n"
123
  "*/pause* - Pause the stream\n"
@@ -127,7 +127,7 @@ def help_text():
127
  )
128
 
129
  def send_guide_message(chat_id, message):
130
- # Return a response dictionary to be sent as the webhook reply
131
  logging.info(f"Sending message to chat {chat_id}: {message}")
132
  return {
133
  "method": "sendMessage",
@@ -136,6 +136,16 @@ def send_guide_message(chat_id, message):
136
  "parse_mode": "Markdown"
137
  }
138
 
 
 
 
 
 
 
 
 
 
 
139
  def reset_statistics():
140
  global stream_start_time, frames_encoded, bytes_sent
141
  stream_start_time = datetime.datetime.now()
@@ -155,6 +165,19 @@ def validate_inputs():
155
  return False, f"Missing fields: {', '.join(missing)}"
156
  return True, ""
157
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  # -------------------------------------------------------------------
159
  # Error Notification Helper
160
  # -------------------------------------------------------------------
@@ -170,9 +193,8 @@ def live_log_updater():
170
  global live_log_display, streaming_state
171
  try:
172
  while streaming_state in ["streaming", "paused"]:
173
- # Include a header with the current update time in the log display.
174
- current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
175
- live_log_display = f"<pre>Live Logs (Updated at {current_time}):\n" + "\n".join(live_log_lines[-15:]) + "</pre>"
176
  time.sleep(1)
177
  except Exception as e:
178
  logging.error(f"Error in live log updater: {e}")
@@ -182,9 +204,9 @@ def live_log_updater():
182
  # -------------------------------------------------------------------
183
  def logs_history(chat_id):
184
  global live_log_display, error_notification
185
- # Use the global live_log_display; if empty, show a default message.
186
  log_text = live_log_display if live_log_display else "<pre>No logs available yet.</pre>"
187
- # If an error occurred, prepend the error notification.
188
  if error_notification:
189
  if log_text.startswith("<pre>"):
190
  log_text = f"<pre>ERROR: {error_notification}\n\n" + log_text[5:]
@@ -202,23 +224,25 @@ def logs_history(chat_id):
202
  # -------------------------------------------------------------------
203
  def handle_start(chat_id):
204
  global current_step, user_inputs, conversation_fields, advanced_mode
205
- # By default, simple mode is used unless advanced_mode is set via /setting.
206
  user_inputs = {}
207
  if not advanced_mode:
208
  conversation_fields = ["input_url", "output_url"]
209
  else:
210
  conversation_fields = ["input_url", "quality_settings", "video_codec", "audio_codec", "output_url"]
211
  current_step = conversation_fields[0]
212
- text = ("👋 *Welcome to the Stream Bot!*\n\n"
213
- "Let's set up your stream.\n"
214
- f"Please enter the *{current_step.replace('_', ' ')}*"
215
- f"{' (no default)' if current_step not in default_settings else f' _(default: {default_settings[current_step]})_'}:")
 
 
216
  logging.info(f"/start command from chat {chat_id} (advanced_mode={advanced_mode})")
217
  return {
218
  "method": "sendMessage",
219
  "chat_id": chat_id,
220
  "text": text,
221
- "parse_mode": "Markdown"
222
  }
223
 
224
  def handle_setting(chat_id):
@@ -227,14 +251,16 @@ def handle_setting(chat_id):
227
  conversation_fields = ["input_url", "quality_settings", "video_codec", "audio_codec", "output_url"]
228
  user_inputs = {}
229
  current_step = conversation_fields[0]
230
- text = ("⚙️ *Advanced Mode Activated!*\n\n"
231
- "Please enter the *input url*:")
 
 
232
  logging.info(f"/setting command from chat {chat_id} - advanced mode enabled")
233
  return {
234
  "method": "sendMessage",
235
  "chat_id": chat_id,
236
  "text": text,
237
- "parse_mode": "Markdown"
238
  }
239
 
240
  def handle_help(chat_id):
@@ -249,7 +275,6 @@ def handle_help(chat_id):
249
  def handle_conversation(chat_id, text):
250
  global current_step, user_inputs, conversation_fields
251
  if current_step:
252
- # If the response is empty and a default exists, use the default.
253
  if text.strip() == "" and current_step in default_settings:
254
  user_inputs[current_step] = default_settings[current_step]
255
  logging.info(f"Using default for {current_step}: {default_settings[current_step]}")
@@ -259,31 +284,28 @@ def handle_conversation(chat_id, text):
259
  idx = conversation_fields.index(current_step)
260
  if idx < len(conversation_fields) - 1:
261
  current_step = conversation_fields[idx + 1]
262
- prompt = f"Please enter the *{current_step.replace('_', ' ')}*"
263
  if current_step in default_settings:
264
- prompt += f" _(default: {default_settings[current_step]})_"
265
- return send_guide_message(chat_id, prompt)
266
  else:
267
- # All inputs have been collected.
268
  current_step = None
269
  valid, msg = validate_inputs()
270
  if not valid:
271
- return send_guide_message(chat_id, f"Validation error: {msg}")
272
- # In simple mode, fill in advanced fields with defaults.
273
  if not advanced_mode:
274
  user_inputs.setdefault("quality_settings", default_settings["quality_settings"])
275
  user_inputs.setdefault("video_codec", default_settings["video_codec"])
276
  user_inputs.setdefault("audio_codec", default_settings["audio_codec"])
277
- # Instead of asking the user to type "start", send an inline button.
278
  return {
279
  "method": "sendMessage",
280
  "chat_id": chat_id,
281
- "text": "All inputs received. Press *🚀 Start Streaming* to begin.\n\n" + live_log_display,
282
  "reply_markup": get_inline_keyboard_for_start(),
283
- "parse_mode": "Markdown"
284
  }
285
  else:
286
- return send_guide_message(chat_id, "Unrecognized input. Type /help for available commands.")
287
 
288
  # -------------------------------------------------------------------
289
  # Background Streaming Functions
@@ -296,7 +318,6 @@ def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, out
296
  reset_statistics()
297
  input_stream = av.open(input_url)
298
  output_stream = av.open(output_url, mode='w', format='flv')
299
- # Configure video stream
300
  video_stream = output_stream.add_stream(video_codec, rate=30)
301
  video_stream.width = input_stream.streams.video[0].width
302
  video_stream.height = input_stream.streams.video[0].height
@@ -311,20 +332,19 @@ def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, out
311
  elif quality_settings.lower() == "low":
312
  video_stream.bit_rate = 800000
313
  video_stream.bit_rate_tolerance = 200000
314
- # Configure audio stream
315
  audio_stream_in = input_stream.streams.audio[0]
316
  out_audio_stream = output_stream.add_stream(audio_codec, rate=audio_stream_in.rate)
317
  out_audio_stream.layout = "stereo"
318
  video_stream.codec_context.time_base = fractions.Fraction(1, video_stream.rate)
319
  logging.info("Streaming started successfully.")
320
- # Start the live log updater in a background thread if not already running.
321
  global live_log_thread
322
  if live_log_thread is None or not live_log_thread.is_alive():
323
  live_log_thread = threading.Thread(target=live_log_updater)
324
  live_log_thread.daemon = True
325
  live_log_thread.start()
326
  logging.info("Live log updater thread started.")
327
- # Stream loop: process packets until state changes
328
  while streaming_state in ["streaming", "paused"]:
329
  for packet in input_stream.demux():
330
  if streaming_state == "stopped":
@@ -348,14 +368,12 @@ def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, out
348
  output_stream.mux(out_packet)
349
  if hasattr(out_packet, "size"):
350
  bytes_sent += out_packet.size
351
- # Flush remaining packets
352
  for out_packet in video_stream.encode():
353
  output_stream.mux(out_packet)
354
  for out_packet in out_audio_stream.encode():
355
  output_stream.mux(out_packet)
356
  if streaming_state == "paused":
357
  time.sleep(1)
358
- # Clean up resources
359
  try:
360
  video_stream.close()
361
  out_audio_stream.close()
@@ -369,14 +387,13 @@ def stream_to_youtube(input_url, quality_settings, video_codec, audio_codec, out
369
  error_message = f"An error occurred during streaming: {str(e)}\n\n{traceback.format_exc()}"
370
  logging.error(error_message)
371
  streaming_state = "idle"
372
- # Notify the user about the error by storing it in a global variable
373
  notify_error(chat_id, error_message)
374
 
375
  def start_streaming(chat_id):
376
  global stream_thread, stream_chat_id
377
  valid, msg = validate_inputs()
378
  if not valid:
379
- return send_guide_message(chat_id, f"Validation error: {msg}")
380
  stream_chat_id = chat_id
381
  try:
382
  stream_thread = threading.Thread(
@@ -393,20 +410,18 @@ def start_streaming(chat_id):
393
  stream_thread.daemon = True
394
  stream_thread.start()
395
  logging.info("Streaming thread started.")
396
- # Immediately inform the user that streaming has started, along with current live logs.
397
- response_text = "🚀 *Streaming initiated!* Live logs are now updating below:\n\n" + live_log_display
398
  return {
399
  "method": "sendMessage",
400
  "chat_id": chat_id,
401
- "text": response_text,
402
  "reply_markup": get_inline_keyboard_for_stream(),
403
- "parse_mode": "Markdown"
404
  }
405
  except Exception as e:
406
  error_message = f"Failed to start streaming: {str(e)}"
407
  logging.error(error_message)
408
  notify_error(chat_id, error_message)
409
- return send_guide_message(chat_id, error_message)
410
 
411
  # -------------------------------------------------------------------
412
  # Stream Control Handlers
@@ -419,10 +434,11 @@ def pause_stream(chat_id):
419
  return {
420
  "method": "sendMessage",
421
  "chat_id": chat_id,
422
- "text": "⏸ *Streaming paused.*",
423
- "parse_mode": "Markdown"
 
424
  }
425
- return send_guide_message(chat_id, "Streaming is not active.")
426
 
427
  def resume_stream(chat_id):
428
  global streaming_state
@@ -432,10 +448,11 @@ def resume_stream(chat_id):
432
  return {
433
  "method": "sendMessage",
434
  "chat_id": chat_id,
435
- "text": "▶️ *Streaming resumed.*",
436
- "parse_mode": "Markdown"
 
437
  }
438
- return send_guide_message(chat_id, "Streaming is not paused.")
439
 
440
  def abort_stream(chat_id):
441
  global streaming_state
@@ -445,24 +462,19 @@ def abort_stream(chat_id):
445
  return {
446
  "method": "sendMessage",
447
  "chat_id": chat_id,
448
- "text": "⏹ *Streaming aborted.*",
449
- "parse_mode": "Markdown"
 
450
  }
451
- return send_guide_message(chat_id, "No active streaming to abort.")
452
 
453
  def stream_status(chat_id):
454
- stats = (
455
- f"*Stream Status:*\n\n"
456
- f"• **State:** {streaming_state}\n"
457
- f"• **Uptime:** {get_uptime()}\n"
458
- f"• **Frames Encoded:** {frames_encoded}\n"
459
- f"• **Bytes Sent:** {bytes_sent}\n"
460
- )
461
  return {
462
  "method": "sendMessage",
463
  "chat_id": chat_id,
464
- "text": stats,
465
- "parse_mode": "Markdown"
 
466
  }
467
 
468
  # -------------------------------------------------------------------
@@ -494,13 +506,14 @@ async def telegram_webhook(request: Request):
494
  elif text.startswith("/status"):
495
  return stream_status(chat_id)
496
  else:
497
- # Process conversation setup inputs
498
  return handle_conversation(chat_id, text)
 
499
  # Process inline keyboard callback queries
500
  elif "callback_query" in update:
501
  callback_data = update["callback_query"]["data"]
502
  chat_id = update["callback_query"]["message"]["chat"]["id"]
503
  message_id = update["callback_query"]["message"]["message_id"]
 
504
  if callback_data == "pause":
505
  response = pause_stream(chat_id)
506
  elif callback_data == "resume":
@@ -512,13 +525,11 @@ async def telegram_webhook(request: Request):
512
  elif callback_data == "start_stream":
513
  response = start_streaming(chat_id)
514
  else:
515
- response = send_guide_message(chat_id, "❓ Unknown callback command.")
516
- # Ensure the inline keyboard remains in the updated message.
517
- if streaming_state in ["streaming", "paused"]:
518
- response["reply_markup"] = get_inline_keyboard_for_stream()
519
- elif current_step is None:
520
- response["reply_markup"] = get_inline_keyboard_for_start()
521
  response["method"] = "editMessageText"
522
  response["message_id"] = message_id
 
523
  return response
 
524
  return {"status": "ok"}
 
85
  return {"parse_mode": "HTML", "text": f"<pre>{text}</pre>"}
86
 
87
  def get_inline_keyboard_for_stream():
88
+ # Inline keyboard for streaming controls that remain visible in the message
89
  keyboard = {
90
  "inline_keyboard": [
91
  [
 
101
  return keyboard
102
 
103
  def get_inline_keyboard_for_start():
104
+ # Inline keyboard with a start button for when setup is complete.
105
  keyboard = {
106
  "inline_keyboard": [
107
  [
 
117
  "*/start* - Begin setup for streaming (simple mode: only Input & Output URL)\n"
118
  "*/setting* - Enter advanced settings (Input URL, Quality Settings, Video Codec, Audio Codec, Output URL)\n"
119
  "*/help* - Display this help text\n"
120
+ "*/logs* - Show the log history (live log display)\n\n"
121
  "After inputs are collected, press the inline *Start Streaming* button.\n\n"
122
  "While streaming, you can use inline buttons or commands:\n"
123
  "*/pause* - Pause the stream\n"
 
127
  )
128
 
129
  def send_guide_message(chat_id, message):
130
+ # Returns a response dictionary (Markdown format) to be sent as the webhook reply
131
  logging.info(f"Sending message to chat {chat_id}: {message}")
132
  return {
133
  "method": "sendMessage",
 
136
  "parse_mode": "Markdown"
137
  }
138
 
139
+ def send_guide_message_html(chat_id, message):
140
+ # Returns a response dictionary (HTML format) to be sent as the webhook reply
141
+ logging.info(f"Sending HTML message to chat {chat_id}: {message}")
142
+ return {
143
+ "method": "sendMessage",
144
+ "chat_id": chat_id,
145
+ "text": message,
146
+ "parse_mode": "HTML"
147
+ }
148
+
149
  def reset_statistics():
150
  global stream_start_time, frames_encoded, bytes_sent
151
  stream_start_time = datetime.datetime.now()
 
165
  return False, f"Missing fields: {', '.join(missing)}"
166
  return True, ""
167
 
168
+ def get_streaming_display_message(prefix=""):
169
+ # Build an HTML message with a prefix, current streaming status, and live logs.
170
+ global live_log_display, streaming_state, frames_encoded, bytes_sent
171
+ status = (
172
+ f"<b>Stream Status:</b>\n"
173
+ f"State: {streaming_state}\n"
174
+ f"Uptime: {get_uptime()}\n"
175
+ f"Frames Encoded: {frames_encoded}\n"
176
+ f"Bytes Sent: {bytes_sent}\n"
177
+ )
178
+ logs = live_log_display if live_log_display else "<pre>No logs available yet.</pre>"
179
+ return f"{prefix}\n{status}\n\nLive Logs:\n{logs}"
180
+
181
  # -------------------------------------------------------------------
182
  # Error Notification Helper
183
  # -------------------------------------------------------------------
 
193
  global live_log_display, streaming_state
194
  try:
195
  while streaming_state in ["streaming", "paused"]:
196
+ # Update live_log_display with the last 15 log lines in HTML format
197
+ live_log_display = "<pre>" + "\n".join(live_log_lines[-15:]) + "</pre>"
 
198
  time.sleep(1)
199
  except Exception as e:
200
  logging.error(f"Error in live log updater: {e}")
 
204
  # -------------------------------------------------------------------
205
  def logs_history(chat_id):
206
  global live_log_display, error_notification
207
+ # Use live_log_display if available; otherwise, show a default message.
208
  log_text = live_log_display if live_log_display else "<pre>No logs available yet.</pre>"
209
+ # Prepend any error notification if present.
210
  if error_notification:
211
  if log_text.startswith("<pre>"):
212
  log_text = f"<pre>ERROR: {error_notification}\n\n" + log_text[5:]
 
224
  # -------------------------------------------------------------------
225
  def handle_start(chat_id):
226
  global current_step, user_inputs, conversation_fields, advanced_mode
227
+ # Use simple mode by default (unless advanced_mode was set via /setting)
228
  user_inputs = {}
229
  if not advanced_mode:
230
  conversation_fields = ["input_url", "output_url"]
231
  else:
232
  conversation_fields = ["input_url", "quality_settings", "video_codec", "audio_codec", "output_url"]
233
  current_step = conversation_fields[0]
234
+ text = (
235
+ "👋 <b>Welcome to the Stream Bot!</b>\n\n"
236
+ "Let's set up your stream.\n"
237
+ f"Please enter the <b>{current_step.replace('_', ' ')}</b>"
238
+ f"{' (no default)' if current_step not in default_settings else f' (default: {default_settings[current_step]})'}:"
239
+ )
240
  logging.info(f"/start command from chat {chat_id} (advanced_mode={advanced_mode})")
241
  return {
242
  "method": "sendMessage",
243
  "chat_id": chat_id,
244
  "text": text,
245
+ "parse_mode": "HTML"
246
  }
247
 
248
  def handle_setting(chat_id):
 
251
  conversation_fields = ["input_url", "quality_settings", "video_codec", "audio_codec", "output_url"]
252
  user_inputs = {}
253
  current_step = conversation_fields[0]
254
+ text = (
255
+ "⚙️ <b>Advanced Mode Activated!</b>\n\n"
256
+ "Please enter the <b>input url</b>:"
257
+ )
258
  logging.info(f"/setting command from chat {chat_id} - advanced mode enabled")
259
  return {
260
  "method": "sendMessage",
261
  "chat_id": chat_id,
262
  "text": text,
263
+ "parse_mode": "HTML"
264
  }
265
 
266
  def handle_help(chat_id):
 
275
  def handle_conversation(chat_id, text):
276
  global current_step, user_inputs, conversation_fields
277
  if current_step:
 
278
  if text.strip() == "" and current_step in default_settings:
279
  user_inputs[current_step] = default_settings[current_step]
280
  logging.info(f"Using default for {current_step}: {default_settings[current_step]}")
 
284
  idx = conversation_fields.index(current_step)
285
  if idx < len(conversation_fields) - 1:
286
  current_step = conversation_fields[idx + 1]
287
+ prompt = f"Please enter the <b>{current_step.replace('_', ' ')}</b>"
288
  if current_step in default_settings:
289
+ prompt += f" (default: {default_settings[current_step]})"
290
+ return send_guide_message_html(chat_id, prompt)
291
  else:
 
292
  current_step = None
293
  valid, msg = validate_inputs()
294
  if not valid:
295
+ return send_guide_message_html(chat_id, f"Validation error: {msg}")
 
296
  if not advanced_mode:
297
  user_inputs.setdefault("quality_settings", default_settings["quality_settings"])
298
  user_inputs.setdefault("video_codec", default_settings["video_codec"])
299
  user_inputs.setdefault("audio_codec", default_settings["audio_codec"])
 
300
  return {
301
  "method": "sendMessage",
302
  "chat_id": chat_id,
303
+ "text": "All inputs received. Press <b>🚀 Start Streaming</b> to begin.",
304
  "reply_markup": get_inline_keyboard_for_start(),
305
+ "parse_mode": "HTML"
306
  }
307
  else:
308
+ return send_guide_message_html(chat_id, "Unrecognized input. Type /help for available commands.")
309
 
310
  # -------------------------------------------------------------------
311
  # Background Streaming Functions
 
318
  reset_statistics()
319
  input_stream = av.open(input_url)
320
  output_stream = av.open(output_url, mode='w', format='flv')
 
321
  video_stream = output_stream.add_stream(video_codec, rate=30)
322
  video_stream.width = input_stream.streams.video[0].width
323
  video_stream.height = input_stream.streams.video[0].height
 
332
  elif quality_settings.lower() == "low":
333
  video_stream.bit_rate = 800000
334
  video_stream.bit_rate_tolerance = 200000
 
335
  audio_stream_in = input_stream.streams.audio[0]
336
  out_audio_stream = output_stream.add_stream(audio_codec, rate=audio_stream_in.rate)
337
  out_audio_stream.layout = "stereo"
338
  video_stream.codec_context.time_base = fractions.Fraction(1, video_stream.rate)
339
  logging.info("Streaming started successfully.")
340
+ # Start live log updater if not already running
341
  global live_log_thread
342
  if live_log_thread is None or not live_log_thread.is_alive():
343
  live_log_thread = threading.Thread(target=live_log_updater)
344
  live_log_thread.daemon = True
345
  live_log_thread.start()
346
  logging.info("Live log updater thread started.")
347
+ # Stream loop
348
  while streaming_state in ["streaming", "paused"]:
349
  for packet in input_stream.demux():
350
  if streaming_state == "stopped":
 
368
  output_stream.mux(out_packet)
369
  if hasattr(out_packet, "size"):
370
  bytes_sent += out_packet.size
 
371
  for out_packet in video_stream.encode():
372
  output_stream.mux(out_packet)
373
  for out_packet in out_audio_stream.encode():
374
  output_stream.mux(out_packet)
375
  if streaming_state == "paused":
376
  time.sleep(1)
 
377
  try:
378
  video_stream.close()
379
  out_audio_stream.close()
 
387
  error_message = f"An error occurred during streaming: {str(e)}\n\n{traceback.format_exc()}"
388
  logging.error(error_message)
389
  streaming_state = "idle"
 
390
  notify_error(chat_id, error_message)
391
 
392
  def start_streaming(chat_id):
393
  global stream_thread, stream_chat_id
394
  valid, msg = validate_inputs()
395
  if not valid:
396
+ return send_guide_message_html(chat_id, f"Validation error: {msg}")
397
  stream_chat_id = chat_id
398
  try:
399
  stream_thread = threading.Thread(
 
410
  stream_thread.daemon = True
411
  stream_thread.start()
412
  logging.info("Streaming thread started.")
 
 
413
  return {
414
  "method": "sendMessage",
415
  "chat_id": chat_id,
416
+ "text": get_streaming_display_message("<b>Streaming initiated!</b>"),
417
  "reply_markup": get_inline_keyboard_for_stream(),
418
+ "parse_mode": "HTML"
419
  }
420
  except Exception as e:
421
  error_message = f"Failed to start streaming: {str(e)}"
422
  logging.error(error_message)
423
  notify_error(chat_id, error_message)
424
+ return send_guide_message_html(chat_id, error_message)
425
 
426
  # -------------------------------------------------------------------
427
  # Stream Control Handlers
 
434
  return {
435
  "method": "sendMessage",
436
  "chat_id": chat_id,
437
+ "text": get_streaming_display_message("<b>Streaming paused.</b>"),
438
+ "reply_markup": get_inline_keyboard_for_stream(),
439
+ "parse_mode": "HTML"
440
  }
441
+ return send_guide_message_html(chat_id, "Streaming is not active.")
442
 
443
  def resume_stream(chat_id):
444
  global streaming_state
 
448
  return {
449
  "method": "sendMessage",
450
  "chat_id": chat_id,
451
+ "text": get_streaming_display_message("<b>Streaming resumed.</b>"),
452
+ "reply_markup": get_inline_keyboard_for_stream(),
453
+ "parse_mode": "HTML"
454
  }
455
+ return send_guide_message_html(chat_id, "Streaming is not paused.")
456
 
457
  def abort_stream(chat_id):
458
  global streaming_state
 
462
  return {
463
  "method": "sendMessage",
464
  "chat_id": chat_id,
465
+ "text": get_streaming_display_message("<b>Streaming aborted.</b>"),
466
+ "reply_markup": get_inline_keyboard_for_stream(),
467
+ "parse_mode": "HTML"
468
  }
469
+ return send_guide_message_html(chat_id, "No active streaming to abort.")
470
 
471
  def stream_status(chat_id):
 
 
 
 
 
 
 
472
  return {
473
  "method": "sendMessage",
474
  "chat_id": chat_id,
475
+ "text": get_streaming_display_message("<b>Current streaming status:</b>"),
476
+ "reply_markup": get_inline_keyboard_for_stream(),
477
+ "parse_mode": "HTML"
478
  }
479
 
480
  # -------------------------------------------------------------------
 
506
  elif text.startswith("/status"):
507
  return stream_status(chat_id)
508
  else:
 
509
  return handle_conversation(chat_id, text)
510
+
511
  # Process inline keyboard callback queries
512
  elif "callback_query" in update:
513
  callback_data = update["callback_query"]["data"]
514
  chat_id = update["callback_query"]["message"]["chat"]["id"]
515
  message_id = update["callback_query"]["message"]["message_id"]
516
+
517
  if callback_data == "pause":
518
  response = pause_stream(chat_id)
519
  elif callback_data == "resume":
 
525
  elif callback_data == "start_stream":
526
  response = start_streaming(chat_id)
527
  else:
528
+ response = send_guide_message_html(chat_id, "❓ Unknown callback command.")
529
+ # Update the original message while preserving inline keyboard
 
 
 
 
530
  response["method"] = "editMessageText"
531
  response["message_id"] = message_id
532
+ response["reply_markup"] = get_inline_keyboard_for_stream()
533
  return response
534
+
535
  return {"status": "ok"}