Chandima Prabhath commited on
Commit
162ca3a
Β·
1 Parent(s): 94856b9

Add inactivity reminder and enhance message sending functionality; implement JSON extraction for trivia questions and improve command handling.

Browse files
Files changed (1) hide show
  1. app.py +423 -8
app.py CHANGED
@@ -47,7 +47,6 @@ def inactivity_monitor():
47
  if BOT_STATUS_CHAT:
48
  reminder = "⏰ I haven't heard from you in a while! I'm still here if you need anything."
49
  send_message("inactivity", BOT_STATUS_CHAT, reminder)
50
- # Reset the timer so we don't spam reminders
51
  last_message_time = time.time()
52
 
53
  threading.Thread(target=inactivity_monitor, daemon=True).start()
@@ -72,6 +71,19 @@ def worker():
72
  threading.Thread(target=worker, daemon=True).start()
73
 
74
  # --- send helpers ---
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  def send_message(message_id, to_number, message, retries=3):
76
  chat_id = to_number if to_number.endswith("@g.us") else to_number
77
  url = f"{GREEN_API_URL}/waInstance{GREEN_API_ID_INSTANCE}/sendMessage/{GREEN_API_TOKEN}"
@@ -147,7 +159,6 @@ def handle_image_generation(message_id, chat_id, prompt):
147
  try:
148
  img, path, ret_prompt, url = generate_image(prompt, message_id, message_id, image_dir)
149
  if img:
150
- # Split ret_prompt into paragraphs and italicize each
151
  formatted_ret_prompt = "\n\n".join(
152
  f"_{paragraph.strip()}_" for paragraph in ret_prompt.split("\n\n") if paragraph.strip()
153
  )
@@ -167,7 +178,6 @@ def handle_image_generation(message_id, chat_id, prompt):
167
  @app.post("/whatsapp")
168
  async def whatsapp_webhook(request: Request):
169
  global last_message_time
170
- # Update last_message_time for each incoming message
171
  last_message_time = time.time()
172
 
173
  auth = request.headers.get("Authorization", "").strip()
@@ -218,13 +228,13 @@ async def whatsapp_webhook(request: Request):
218
  "β€’ `/weather <location>` – Get the current weather for a location.\n"
219
  "β€’ `/inspire` – Receive a short inspirational quote.\n"
220
  "β€’ `/trivia` – Start a new trivia question.\n"
221
- "β€’ `/answer` – Reveal the answer to the trivia.\n"
222
  "β€’ `/meme <text>` – Generate a fun meme image.\n"
223
  "β€’ `/poll <Question>|<Option1>|<Option2>|…` – Create a poll.\n"
224
  "β€’ `/results` – See current poll results.\n"
225
  "β€’ `/endpoll` – End the poll and show final results.\n"
226
  "β€’ `/imagine <prompt>` – Generate an image from your prompt.\n\n"
227
- "If you send any other text, I'll reply with a voice message. I'm here to help, so don't hesitate to ask!"
228
  )
229
  send_message(mid, chat, help_text)
230
  return {"success": True}
@@ -274,13 +284,409 @@ async def whatsapp_webhook(request: Request):
274
  "{\"question\": \"What is the capital of France?\", \"answer\": \"Paris\"}"
275
  )
276
  def extract_json(text):
277
- # Try to extract content between ```json ... ```
278
  import re
279
  match = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", text, re.DOTALL)
280
  if match:
281
  return match.group(1)
282
- return text # fallback to entire text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
  try:
285
  json_text = extract_json(raw)
286
  obj = json.loads(json_text)
@@ -294,7 +700,8 @@ async def whatsapp_webhook(request: Request):
294
  send_message(mid, chat, "Failed to generate trivia. Please try again.")
295
  return {"success": True}
296
 
297
- if low == "/answer":
 
298
  if chat in trivia_store:
299
  ans = trivia_store.pop(chat)["answer"]
300
  send_message(mid, chat, f"πŸ’‘ Answer: {ans}")
@@ -390,5 +797,13 @@ def index():
390
  return "Server is running!"
391
 
392
  if __name__ == "__main__":
 
 
 
 
 
 
 
 
393
  import uvicorn
394
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
47
  if BOT_STATUS_CHAT:
48
  reminder = "⏰ I haven't heard from you in a while! I'm still here if you need anything."
49
  send_message("inactivity", BOT_STATUS_CHAT, reminder)
 
50
  last_message_time = time.time()
51
 
52
  threading.Thread(target=inactivity_monitor, daemon=True).start()
 
71
  threading.Thread(target=worker, daemon=True).start()
72
 
73
  # --- send helpers ---
74
+ def send_message_to_chat(to_number, message, retries=3):
75
+ chat_id = to_number if to_number.endswith("@g.us") else to_number
76
+ url = f"{GREEN_API_URL}/waInstance{GREEN_API_ID_INSTANCE}/sendMessage/{GREEN_API_TOKEN}"
77
+ payload = {"chatId": chat_id, "message": message}
78
+ for i in range(retries):
79
+ try:
80
+ r = requests.post(url, json=payload)
81
+ r.raise_for_status()
82
+ return r.json()
83
+ except requests.RequestException as e:
84
+ if i == retries - 1:
85
+ return {"error": str(e)}
86
+
87
  def send_message(message_id, to_number, message, retries=3):
88
  chat_id = to_number if to_number.endswith("@g.us") else to_number
89
  url = f"{GREEN_API_URL}/waInstance{GREEN_API_ID_INSTANCE}/sendMessage/{GREEN_API_TOKEN}"
 
159
  try:
160
  img, path, ret_prompt, url = generate_image(prompt, message_id, message_id, image_dir)
161
  if img:
 
162
  formatted_ret_prompt = "\n\n".join(
163
  f"_{paragraph.strip()}_" for paragraph in ret_prompt.split("\n\n") if paragraph.strip()
164
  )
 
178
  @app.post("/whatsapp")
179
  async def whatsapp_webhook(request: Request):
180
  global last_message_time
 
181
  last_message_time = time.time()
182
 
183
  auth = request.headers.get("Authorization", "").strip()
 
228
  "β€’ `/weather <location>` – Get the current weather for a location.\n"
229
  "β€’ `/inspire` – Receive a short inspirational quote.\n"
230
  "β€’ `/trivia` – Start a new trivia question.\n"
231
+ "β€’ `/answer` – Reveal the trivia answer.\n"
232
  "β€’ `/meme <text>` – Generate a fun meme image.\n"
233
  "β€’ `/poll <Question>|<Option1>|<Option2>|…` – Create a poll.\n"
234
  "β€’ `/results` – See current poll results.\n"
235
  "β€’ `/endpoll` – End the poll and show final results.\n"
236
  "β€’ `/imagine <prompt>` – Generate an image from your prompt.\n\n"
237
+ "Send any other text and I'll reply with a voice message. I'm here to help, so don't hesitate to ask!"
238
  )
239
  send_message(mid, chat, help_text)
240
  return {"success": True}
 
284
  "{\"question\": \"What is the capital of France?\", \"answer\": \"Paris\"}"
285
  )
286
  def extract_json(text):
 
287
  import re
288
  match = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", text, re.DOTALL)
289
  if match:
290
  return match.group(1)
291
+ return text
292
+ try:
293
+ json_text = extract_json(raw)
294
+ obj = json.loads(json_text)
295
+ if "question" in obj and "answer" in obj:
296
+ trivia_store[chat] = obj
297
+ send_message(mid, chat, f"❓ {obj['question']}\nReply with `/answer` to see the answer.")
298
+ else:
299
+ raise ValueError("Missing expected keys.")
300
+ except Exception as e:
301
+ logging.error("Trivia JSON parse error: %s, raw response: %s", e, raw)
302
+ send_message(mid, chat, "Failed to generate trivia. Please try again.")
303
+ return {"success": True}
304
+
305
+ # ANSWER: Accept any message starting with /answer
306
+ if low.startswith("/answer"):
307
+ if chat in trivia_store:
308
+ ans = trivia_store.pop(chat)["answer"]
309
+ send_message(mid, chat, f"πŸ’‘ Answer: {ans}")
310
+ else:
311
+ send_message(mid, chat, "No active trivia. Send `/trivia` to start one.")
312
+ return {"success": True}
313
+
314
+ if low.startswith("/meme "):
315
+ txt = body[len("/meme "):].strip()
316
+ send_message(mid, chat, "🎨 Generating your meme...")
317
+ task_queue.put({
318
+ "type": "image",
319
+ "message_id": mid,
320
+ "chat_id": chat,
321
+ "prompt": f"meme template with text: {txt}"
322
+ })
323
+ return {"success": True}
324
+
325
+ if low.startswith("/poll "):
326
+ parts = body[len("/poll "):].split("|")
327
+ if len(parts) < 3:
328
+ send_message(mid, chat, "Please use `/poll Question|Option1|Option2|...`")
329
+ else:
330
+ q = parts[0].strip()
331
+ opts = [p.strip() for p in parts[1:]]
332
+ votes = {i+1: 0 for i in range(len(opts))}
333
+ polls[chat] = {"question": q, "options": opts, "votes": votes, "voters": {}}
334
+ txt = f"πŸ“Š *Poll:* {q}\n" + "\n".join(
335
+ f"{i+1}. {opt}" for i, opt in enumerate(opts)
336
+ ) + "\n\nReply with the *option number* to vote."
337
+ send_message(mid, chat, txt)
338
+ return {"success": True}
339
+
340
+ if chat in polls and body.isdigit():
341
+ n = int(body)
342
+ p = polls[chat]
343
+ if 1 <= n <= len(p["options"]):
344
+ prev = p["voters"].get(sender_jid)
345
+ if prev:
346
+ p["votes"][prev] -= 1
347
+ p["votes"][n] += 1
348
+ p["voters"][sender_jid] = n
349
+ send_message(mid, chat, f"βœ… Vote recorded: {p['options'][n-1]}")
350
+ return {"success": True}
351
+
352
+ if low == "/results":
353
+ if chat in polls:
354
+ p = polls[chat]
355
+ txt = f"πŸ“Š *Results:* {p['question']}\n" + "\n".join(
356
+ f"{i}. {opt}: {p['votes'][i]}" for i, opt in enumerate([""] + p["options"]) if i > 0
357
+ )
358
+ send_message(mid, chat, txt)
359
+ else:
360
+ send_message(mid, chat, "No active poll.")
361
+ return {"success": True}
362
 
363
+ if low == "/endpoll":
364
+ if chat in polls:
365
+ p = polls.pop(chat)
366
+ txt = f"πŸ“Š *Final Results:* {p['question']}\n" + "\n".join(
367
+ f"{i}. {opt}: {p['votes'][i]}" for i, opt in enumerate([""] + p["options"]) if i > 0
368
+ )
369
+ send_message(mid, chat, txt)
370
+ else:
371
+ send_message(mid, chat, "No active poll.")
372
+ return {"success": True}
373
+
374
+ if low.startswith("/imagine"):
375
+ prompt = body[len("/imagine"):].strip()
376
+ if not prompt:
377
+ send_message(mid, chat, "Please use `/imagine <prompt>` to generate an image.")
378
+ else:
379
+ send_message(mid, chat, "✨ Your image is being generated. Please wait...")
380
+ task_queue.put({
381
+ "type": "image",
382
+ "message_id": mid,
383
+ "chat_id": chat,
384
+ "prompt": prompt
385
+ })
386
+ return {"success": True}
387
+
388
+ # Fallback: voice reply for any other text
389
+ task_queue.put({
390
+ "type": "audio",
391
+ "message_id": mid,
392
+ "chat_id": chat,
393
+ "prompt": body
394
+ })
395
+ return {"success": True}
396
+
397
+ @app.get("/", response_class=PlainTextResponse)
398
+ def index():
399
+ return "Server is running!"
400
+
401
+ if __name__ == "__main__":
402
+ # Send startup message on launch
403
+ def send_startup_message():
404
+ if BOT_STATUS_CHAT:
405
+ startup_msg = "🌟 Hi! I'm Eve, your friendly AI assistant. I'm now live and ready to help with images, voice replies, and more!"
406
+ send_message("startup", BOT_STATUS_CHAT, startup_msg)
407
+ else:
408
+ logging.warning("BOT_STATUS_CHAT is not set; startup message not sent.")
409
+ send_startup_message()
410
+ import uvicorn
411
+ uvicorn.run(app, host="0.0.0.0", port=7860)
412
+ import os
413
+ import threading
414
+ import requests
415
+ import logging
416
+ import queue
417
+ import re
418
+ import json
419
+ import time
420
+ from fastapi import FastAPI, Request, HTTPException
421
+ from fastapi.responses import PlainTextResponse, JSONResponse
422
+ from FLUX import generate_image
423
+ from VoiceReply import generate_voice_reply
424
+ from polLLM import generate_llm
425
+
426
+ # Configure logging
427
+ logging.basicConfig(level=logging.DEBUG, format="%(asctime)s [%(levelname)s] %(message)s")
428
+
429
+ # Env vars
430
+ GREEN_API_URL = os.getenv("GREEN_API_URL")
431
+ GREEN_API_MEDIA_URL = os.getenv("GREEN_API_MEDIA_URL", "https://api.green-api.com")
432
+ GREEN_API_TOKEN = os.getenv("GREEN_API_TOKEN")
433
+ GREEN_API_ID_INSTANCE= os.getenv("GREEN_API_ID_INSTANCE")
434
+ WEBHOOK_AUTH_TOKEN = os.getenv("WEBHOOK_AUTH_TOKEN")
435
+ BOT_STATUS_CHAT = "[email protected]" # Chat ID for system messages
436
+ image_dir = "/tmp/images"
437
+ audio_dir = "/tmp/audio"
438
+
439
+ if not all([GREEN_API_URL, GREEN_API_TOKEN, GREEN_API_ID_INSTANCE, WEBHOOK_AUTH_TOKEN]):
440
+ raise ValueError("Environment variables are not set properly")
441
+
442
+ # Queues & in‑memory stores
443
+ task_queue = queue.Queue()
444
+ trivia_store = {} # chat_id β†’ {"question":…, "answer":…}
445
+ polls = {} # chat_id β†’ {"question":…, "options":[…], "votes":{1:0…}, "voters":{jid:opt}}
446
+
447
+ app = FastAPI()
448
+
449
+ # Global inactivity tracker
450
+ last_message_time = time.time()
451
+
452
+ # --- Inactivity Monitor ---
453
+ def inactivity_monitor():
454
+ global last_message_time
455
+ while True:
456
+ time.sleep(60) # check every minute
457
+ if time.time() - last_message_time >= 300: # 5 minutes inactivity
458
+ if BOT_STATUS_CHAT:
459
+ reminder = "⏰ I haven't heard from you in a while! I'm still here if you need anything."
460
+ send_message("inactivity", BOT_STATUS_CHAT, reminder)
461
+ last_message_time = time.time()
462
+
463
+ threading.Thread(target=inactivity_monitor, daemon=True).start()
464
+
465
+ # --- Background Worker ---
466
+ def worker():
467
+ while True:
468
+ task = task_queue.get()
469
+ try:
470
+ typ = task["type"]
471
+ mid = task["message_id"]
472
+ cid = task["chat_id"]
473
+ if typ == "image":
474
+ handle_image_generation(mid, cid, task["prompt"])
475
+ elif typ == "audio":
476
+ response_audio(mid, cid, task["prompt"])
477
+ except Exception as e:
478
+ logging.error(f"Error processing {task}: {e}")
479
+ finally:
480
+ task_queue.task_done()
481
+
482
+ threading.Thread(target=worker, daemon=True).start()
483
+
484
+ # --- send helpers ---
485
+ def send_message(message_id, to_number, message, retries=3):
486
+ chat_id = to_number if to_number.endswith("@g.us") else to_number
487
+ url = f"{GREEN_API_URL}/waInstance{GREEN_API_ID_INSTANCE}/sendMessage/{GREEN_API_TOKEN}"
488
+ payload = {"chatId": chat_id, "message": message, "quotedMessageId": message_id}
489
+ for i in range(retries):
490
+ try:
491
+ r = requests.post(url, json=payload)
492
+ r.raise_for_status()
493
+ return r.json()
494
+ except requests.RequestException as e:
495
+ if i == retries - 1:
496
+ return {"error": str(e)}
497
+
498
+ def send_image(message_id, to_number, image_path, caption="Here you go!", retries=3):
499
+ chat_id = to_number if to_number.endswith("@g.us") else to_number
500
+ url = f"{GREEN_API_MEDIA_URL}/waInstance{GREEN_API_ID_INSTANCE}/sendFileByUpload/{GREEN_API_TOKEN}"
501
+ payload = {"chatId": chat_id, "caption": caption, "quotedMessageId": message_id}
502
+ files = [("file", ("image.jpg", open(image_path, "rb"), "image/jpeg"))]
503
+ for i in range(retries):
504
+ try:
505
+ r = requests.post(url, data=payload, files=files)
506
+ r.raise_for_status()
507
+ return r.json()
508
+ except requests.RequestException as e:
509
+ if i == retries - 1:
510
+ return {"error": str(e)}
511
+
512
+ def send_audio(message_id, to_number, audio_path, retries=3):
513
+ logging.debug("send_audio")
514
+ chat_id = to_number if to_number.endswith("@g.us") else to_number
515
+ if not os.path.exists(audio_path):
516
+ logging.debug(f"Missing audio: {audio_path}")
517
+ url = f"{GREEN_API_MEDIA_URL}/waInstance{GREEN_API_ID_INSTANCE}/sendFileByUpload/{GREEN_API_TOKEN}"
518
+ payload = {"chatId": chat_id, "caption": "Here is your voice reply!", "quotedMessageId": message_id}
519
+ try:
520
+ with open(audio_path, "rb") as f:
521
+ files = [("file", ("audio.mp3", f, "audio/mpeg"))]
522
+ for i in range(retries):
523
+ try:
524
+ r = requests.post(url, data=payload, files=files)
525
+ r.raise_for_status()
526
+ return r.json()
527
+ except requests.RequestException as e:
528
+ if i == retries - 1:
529
+ return {"error": str(e)}
530
+ except Exception as e:
531
+ return {"error": str(e)}
532
+
533
+ # --- core response functions ---
534
+ def response_text(message_id, chat_id, prompt):
535
+ try:
536
+ msg = generate_llm(prompt)
537
+ send_message(message_id, chat_id, msg)
538
+ except Exception:
539
+ send_message(message_id, chat_id, "Error processing your request.")
540
+
541
+ def response_audio(message_id, chat_id, prompt):
542
+ logging.debug("response_audio prompt=%s", prompt)
543
+ try:
544
+ result = generate_voice_reply(prompt, model="openai-audio", voice="coral", audio_dir=audio_dir)
545
+ if result and result[0]:
546
+ audio_path, _ = result
547
+ send_audio(message_id, chat_id, audio_path)
548
+ if os.path.exists(audio_path):
549
+ os.remove(audio_path)
550
+ else:
551
+ response_text(message_id, chat_id, prompt)
552
+ except Exception as e:
553
+ logging.debug("audio error: %s", e)
554
+ send_message(message_id, chat_id, "Error generating audio. Try again later.")
555
+
556
+ def handle_image_generation(message_id, chat_id, prompt):
557
+ try:
558
+ img, path, ret_prompt, url = generate_image(prompt, message_id, message_id, image_dir)
559
+ if img:
560
+ formatted_ret_prompt = "\n\n".join(
561
+ f"_{paragraph.strip()}_" for paragraph in ret_prompt.split("\n\n") if paragraph.strip()
562
+ )
563
+ send_image(
564
+ message_id,
565
+ chat_id,
566
+ path,
567
+ caption=f"✨ Image ready: {url}\n>{chr(8203)} {formatted_ret_prompt}"
568
+ )
569
+ else:
570
+ send_message(message_id, chat_id, "Image generation failed.")
571
+ except Exception as e:
572
+ logging.error("Error in handle_image_generation: %s", e)
573
+ send_message(message_id, chat_id, "Error generating image.")
574
+
575
+ # --- Webhook ---
576
+ @app.post("/whatsapp")
577
+ async def whatsapp_webhook(request: Request):
578
+ global last_message_time
579
+ last_message_time = time.time()
580
+
581
+ auth = request.headers.get("Authorization", "").strip()
582
+ if auth != f"Bearer {WEBHOOK_AUTH_TOKEN}":
583
+ raise HTTPException(403, "Unauthorized")
584
+ try:
585
+ data = await request.json()
586
+ except:
587
+ return JSONResponse({"error": "Invalid JSON"}, status_code=400)
588
+ if data.get("typeWebhook") != "incomingMessageReceived":
589
+ return {"success": True}
590
+
591
+ logging.debug("recv: %s", data)
592
+ sd = data["senderData"]
593
+ chat = sd["chatId"]
594
+ mid = data["idMessage"]
595
+ sender_jid = sd.get("sender")
596
+
597
+ md = data.get("messageData", {})
598
+ if md.get("typeMessage") == "quotedMessage" or "quotedMessage" in md:
599
+ logging.debug("skip native quotedMessage")
600
+ return {"success": True}
601
+
602
+ if "textMessageData" in md:
603
+ body = md["textMessageData"].get("textMessage", "").strip()
604
+ ctx = md["textMessageData"].get("contextInfo", {})
605
+ elif "extendedTextMessageData" in md:
606
+ body = md["extendedTextMessageData"].get("text", "").strip()
607
+ ctx = md["extendedTextMessageData"].get("contextInfo", {})
608
+ else:
609
+ return {"success": True}
610
+
611
+ if ctx.get("mentionedJid") or ctx.get("mentionedJidList"):
612
+ return {"success": True}
613
+ if chat.endswith("@g.us") and re.search(r"@\d+", body):
614
+ return {"success": True}
615
+
616
+ low = body.lower()
617
+
618
+ # --- New Commands ---
619
+ if low == "/help":
620
+ help_text = (
621
+ "πŸ€– *Hi there, I'm Eve!* Here are the commands you can use:\n\n"
622
+ "β€’ `/help` – Show this help message.\n"
623
+ "β€’ `/summarize <text>` – Get a quick summary of your text.\n"
624
+ "β€’ `/translate <language>|<text>` – Translate text to your chosen language.\n"
625
+ "β€’ `/joke` – Enjoy a random, funny joke.\n"
626
+ "β€’ `/weather <location>` – Get the current weather for a location.\n"
627
+ "β€’ `/inspire` – Receive a short inspirational quote.\n"
628
+ "β€’ `/trivia` – Start a new trivia question.\n"
629
+ "β€’ `/answer` – Reveal the trivia answer.\n"
630
+ "β€’ `/meme <text>` – Generate a fun meme image.\n"
631
+ "β€’ `/poll <Question>|<Option1>|<Option2>|…` – Create a poll.\n"
632
+ "β€’ `/results` – See current poll results.\n"
633
+ "β€’ `/endpoll` – End the poll and show final results.\n"
634
+ "β€’ `/imagine <prompt>` – Generate an image from your prompt.\n\n"
635
+ "Send any other text and I'll reply with a voice message. I'm here to help, so don't hesitate to ask!"
636
+ )
637
+ send_message(mid, chat, help_text)
638
+ return {"success": True}
639
+
640
+ if low.startswith("/summarize "):
641
+ txt = body[len("/summarize "):].strip()
642
+ summary = generate_llm(f"Summarize this text in one short paragraph:\n\n{txt}")
643
+ send_message(mid, chat, summary)
644
+ return {"success": True}
645
+
646
+ if low.startswith("/translate "):
647
+ part = body[len("/translate "):]
648
+ if "|" not in part:
649
+ send_message(mid, chat, "Please use `/translate <language>|<text>`")
650
+ else:
651
+ lang, txt = part.split("|", 1)
652
+ resp = generate_llm(f"Translate the following into {lang.strip()}:\n\n{txt.strip()}")
653
+ send_message(mid, chat, resp)
654
+ return {"success": True}
655
+
656
+ if low == "/joke":
657
+ try:
658
+ joke = requests.get("https://official-joke-api.appspot.com/random_joke", timeout=5).json()
659
+ send_message(mid, chat, f"{joke['setup']}\n\n{joke['punchline']}")
660
+ except:
661
+ send_message(mid, chat, generate_llm("Tell me a short, funny joke."))
662
+ return {"success": True}
663
+
664
+ if low.startswith("/weather "):
665
+ loc = body[len("/weather "):].strip().replace(" ", "+")
666
+ try:
667
+ w = requests.get(f"http://sl.wttr.in/{loc}?format=4", timeout=5).text
668
+ send_message(mid, chat, w)
669
+ except:
670
+ send_message(mid, chat, "Could not fetch weather.")
671
+ return {"success": True}
672
+
673
+ if low == "/inspire":
674
+ quote = generate_llm("Give me a short inspirational quote.")
675
+ send_message(mid, chat, f"✨ {quote}")
676
+ return {"success": True}
677
+
678
+ # TRIVIA
679
+ if low == "/trivia":
680
+ raw = generate_llm(
681
+ "Generate a trivia question and answer in JSON format like this: "
682
+ "{\"question\": \"What is the capital of France?\", \"answer\": \"Paris\"}"
683
+ )
684
+ def extract_json(text):
685
+ import re
686
+ match = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", text, re.DOTALL)
687
+ if match:
688
+ return match.group(1)
689
+ return text
690
  try:
691
  json_text = extract_json(raw)
692
  obj = json.loads(json_text)
 
700
  send_message(mid, chat, "Failed to generate trivia. Please try again.")
701
  return {"success": True}
702
 
703
+ # ANSWER: Accept any message starting with /answer
704
+ if low.startswith("/answer"):
705
  if chat in trivia_store:
706
  ans = trivia_store.pop(chat)["answer"]
707
  send_message(mid, chat, f"πŸ’‘ Answer: {ans}")
 
797
  return "Server is running!"
798
 
799
  if __name__ == "__main__":
800
+ # Send startup message on launch
801
+ def send_startup_message():
802
+ if BOT_STATUS_CHAT:
803
+ startup_msg = "🌟 Hi! I'm Eve, your friendly AI assistant. I'm now live and ready to help with images, voice replies, and more!"
804
+ send_message_to_chat(BOT_STATUS_CHAT, startup_msg)
805
+ else:
806
+ logging.warning("BOT_STATUS_CHAT is not set; startup message not sent.")
807
+ send_startup_message()
808
  import uvicorn
809
  uvicorn.run(app, host="0.0.0.0", port=7860)