Spaces:
Running
Running
Chandima Prabhath
commited on
Commit
·
9b5a03d
1
Parent(s):
e341e67
Implement WhatsApp status updater with image generation and sending functionality
Browse files
app.py
CHANGED
|
@@ -26,6 +26,10 @@ BOT_STATUS_CHAT = "[email protected]" # Chat ID for system messages
|
|
| 26 |
image_dir = "/tmp/images"
|
| 27 |
audio_dir = "/tmp/audio"
|
| 28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
if not all([GREEN_API_URL, GREEN_API_TOKEN, GREEN_API_ID_INSTANCE, WEBHOOK_AUTH_TOKEN]):
|
| 30 |
raise ValueError("Environment variables are not set properly")
|
| 31 |
|
|
@@ -52,7 +56,7 @@ def inactivity_monitor():
|
|
| 52 |
|
| 53 |
threading.Thread(target=inactivity_monitor, daemon=True).start()
|
| 54 |
|
| 55 |
-
# --- Background Worker
|
| 56 |
def worker():
|
| 57 |
while True:
|
| 58 |
task = task_queue.get()
|
|
@@ -71,60 +75,6 @@ def worker():
|
|
| 71 |
|
| 72 |
threading.Thread(target=worker, daemon=True).start()
|
| 73 |
|
| 74 |
-
# --- WhatsApp Status Updater ---
|
| 75 |
-
def send_status_image(image_path, caption=""):
|
| 76 |
-
"""
|
| 77 |
-
Send an image as WhatsApp status using the Green API.
|
| 78 |
-
The endpoint used here is assumed to be `/sendFileStatus`.
|
| 79 |
-
Adjust the URL if the docs specify a different endpoint.
|
| 80 |
-
"""
|
| 81 |
-
url = f"{GREEN_API_MEDIA_URL}/waInstance{GREEN_API_ID_INSTANCE}/sendFileStatus/{GREEN_API_TOKEN}"
|
| 82 |
-
payload = {"caption": caption}
|
| 83 |
-
files = [("file", ("status.jpg", open(image_path, "rb"), "image/jpeg"))]
|
| 84 |
-
retries = 3
|
| 85 |
-
for i in range(retries):
|
| 86 |
-
try:
|
| 87 |
-
r = requests.post(url, data=payload, files=files)
|
| 88 |
-
r.raise_for_status()
|
| 89 |
-
logging.debug("Status image sent successfully.")
|
| 90 |
-
return r.json()
|
| 91 |
-
except requests.RequestException as e:
|
| 92 |
-
logging.error("send_status_image attempt %d failed: %s", i+1, e)
|
| 93 |
-
if i == retries - 1:
|
| 94 |
-
return {"error": str(e)}
|
| 95 |
-
|
| 96 |
-
def status_updater():
|
| 97 |
-
"""
|
| 98 |
-
Background thread that every 2 minutes:
|
| 99 |
-
1. Generates a random image prompt using the LLM.
|
| 100 |
-
2. Generates an image from that prompt.
|
| 101 |
-
3. Updates the WhatsApp status with the generated image.
|
| 102 |
-
"""
|
| 103 |
-
while True:
|
| 104 |
-
try:
|
| 105 |
-
# Generate a random creative prompt for a status image.
|
| 106 |
-
prompt_llm = "Generate a unique and creative image prompt suitable for a WhatsApp status update."
|
| 107 |
-
random_prompt = generate_llm(prompt_llm).strip()
|
| 108 |
-
logging.debug("Random status prompt: %s", random_prompt)
|
| 109 |
-
|
| 110 |
-
# Generate the image from the prompt.
|
| 111 |
-
img, path, ret_prompt, url_img = generate_image(random_prompt, "status", "status", image_dir)
|
| 112 |
-
if img:
|
| 113 |
-
caption = f"Status updated! {ret_prompt}"
|
| 114 |
-
send_status_image(path, caption=caption)
|
| 115 |
-
logging.info("WhatsApp status updated with image: %s", url_img)
|
| 116 |
-
# Clean up the image file if needed.
|
| 117 |
-
if os.path.exists(path):
|
| 118 |
-
os.remove(path)
|
| 119 |
-
else:
|
| 120 |
-
logging.error("Image generation for status failed for prompt: %s", random_prompt)
|
| 121 |
-
except Exception as e:
|
| 122 |
-
logging.error("Error in status_updater: %s", e)
|
| 123 |
-
# Wait for 2 minutes before updating status again.
|
| 124 |
-
time.sleep(120)
|
| 125 |
-
|
| 126 |
-
threading.Thread(target=status_updater, daemon=True).start()
|
| 127 |
-
|
| 128 |
# --- send helpers ---
|
| 129 |
def send_message_to_chat(to_number, message, retries=3):
|
| 130 |
chat_id = to_number if to_number.endswith("@g.us") else to_number
|
|
@@ -188,6 +138,30 @@ def send_audio(message_id, to_number, audio_path, retries=3):
|
|
| 188 |
except Exception as e:
|
| 189 |
return {"error": str(e)}
|
| 190 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 191 |
# --- core response functions ---
|
| 192 |
def response_text(message_id, chat_id, prompt):
|
| 193 |
try:
|
|
@@ -214,7 +188,8 @@ def response_audio(message_id, chat_id, prompt):
|
|
| 214 |
def handle_image_generation(message_id, chat_id, prompt):
|
| 215 |
for i in range(4):
|
| 216 |
try:
|
| 217 |
-
|
|
|
|
| 218 |
if img:
|
| 219 |
formatted_ret_prompt = "\n\n".join(
|
| 220 |
f"_{paragraph.strip()}_" for paragraph in ret_prompt.split("\n\n") if paragraph.strip()
|
|
@@ -223,14 +198,43 @@ def handle_image_generation(message_id, chat_id, prompt):
|
|
| 223 |
message_id,
|
| 224 |
chat_id,
|
| 225 |
path,
|
| 226 |
-
caption=f"✨ Image ready: {
|
| 227 |
)
|
| 228 |
else:
|
| 229 |
send_message(message_id, chat_id, "Image generation failed.")
|
|
|
|
| 230 |
except Exception as e:
|
| 231 |
logging.error("Error in handle_image_generation: %s", e)
|
| 232 |
send_message(message_id, chat_id, "Error generating image.")
|
| 233 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
# --- Startup Message ---
|
| 235 |
def send_startup_message():
|
| 236 |
if BOT_STATUS_CHAT:
|
|
@@ -272,7 +276,7 @@ async def whatsapp_webhook(request: Request):
|
|
| 272 |
data = await request.json()
|
| 273 |
chat_id = data["senderData"]["chatId"]
|
| 274 |
print(f"New message from chat ID: {chat_id}")
|
| 275 |
-
except
|
| 276 |
return JSONResponse({"error": "Invalid JSON"}, status_code=400)
|
| 277 |
if data.get("typeWebhook") != "incomingMessageReceived":
|
| 278 |
return {"success": True}
|
|
@@ -329,7 +333,7 @@ async def whatsapp_webhook(request: Request):
|
|
| 329 |
try:
|
| 330 |
joke = requests.get("https://official-joke-api.appspot.com/random_joke", timeout=5).json()
|
| 331 |
send_message(mid, chat, f"{joke['setup']}\n\n{joke['punchline']}")
|
| 332 |
-
except
|
| 333 |
send_message(mid, chat, generate_llm("Tell me a short, funny joke."))
|
| 334 |
return {"success": True}
|
| 335 |
|
|
@@ -338,7 +342,7 @@ async def whatsapp_webhook(request: Request):
|
|
| 338 |
try:
|
| 339 |
w = requests.get(f"http://sl.wttr.in/{loc}?format=4", timeout=5).text
|
| 340 |
send_message(mid, chat, w)
|
| 341 |
-
except
|
| 342 |
send_message(mid, chat, "Could not fetch weather.")
|
| 343 |
return {"success": True}
|
| 344 |
|
|
|
|
| 26 |
image_dir = "/tmp/images"
|
| 27 |
audio_dir = "/tmp/audio"
|
| 28 |
|
| 29 |
+
# Ensure necessary directories exist
|
| 30 |
+
os.makedirs(image_dir, exist_ok=True)
|
| 31 |
+
os.makedirs(audio_dir, exist_ok=True)
|
| 32 |
+
|
| 33 |
if not all([GREEN_API_URL, GREEN_API_TOKEN, GREEN_API_ID_INSTANCE, WEBHOOK_AUTH_TOKEN]):
|
| 34 |
raise ValueError("Environment variables are not set properly")
|
| 35 |
|
|
|
|
| 56 |
|
| 57 |
threading.Thread(target=inactivity_monitor, daemon=True).start()
|
| 58 |
|
| 59 |
+
# --- Background Worker ---
|
| 60 |
def worker():
|
| 61 |
while True:
|
| 62 |
task = task_queue.get()
|
|
|
|
| 75 |
|
| 76 |
threading.Thread(target=worker, daemon=True).start()
|
| 77 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
# --- send helpers ---
|
| 79 |
def send_message_to_chat(to_number, message, retries=3):
|
| 80 |
chat_id = to_number if to_number.endswith("@g.us") else to_number
|
|
|
|
| 138 |
except Exception as e:
|
| 139 |
return {"error": str(e)}
|
| 140 |
|
| 141 |
+
# --- New helper: send WhatsApp status image ---
|
| 142 |
+
def send_status_image(image_path, caption="Status Update", retries=3):
|
| 143 |
+
"""
|
| 144 |
+
Sends an image as your WhatsApp status update using Green API's sendMediaStatus endpoint.
|
| 145 |
+
"""
|
| 146 |
+
url = f"{GREEN_API_MEDIA_URL}/waInstance{GREEN_API_ID_INSTANCE}/sendMediaStatus/{GREEN_API_TOKEN}"
|
| 147 |
+
payload = {"caption": caption}
|
| 148 |
+
try:
|
| 149 |
+
with open(image_path, "rb") as f:
|
| 150 |
+
files = [("file", ("status.jpg", f, "image/jpeg"))]
|
| 151 |
+
for i in range(retries):
|
| 152 |
+
try:
|
| 153 |
+
r = requests.post(url, data=payload, files=files)
|
| 154 |
+
r.raise_for_status()
|
| 155 |
+
logging.info("Status image sent successfully.")
|
| 156 |
+
return r.json()
|
| 157 |
+
except requests.RequestException as e:
|
| 158 |
+
if i == retries - 1:
|
| 159 |
+
logging.error("send_status_image failed: %s", str(e))
|
| 160 |
+
return {"error": str(e)}
|
| 161 |
+
except Exception as e:
|
| 162 |
+
logging.error("Error opening status image: %s", e)
|
| 163 |
+
return {"error": str(e)}
|
| 164 |
+
|
| 165 |
# --- core response functions ---
|
| 166 |
def response_text(message_id, chat_id, prompt):
|
| 167 |
try:
|
|
|
|
| 188 |
def handle_image_generation(message_id, chat_id, prompt):
|
| 189 |
for i in range(4):
|
| 190 |
try:
|
| 191 |
+
# Here we pass "message_id" for both the message and chat identifiers.
|
| 192 |
+
img, path, ret_prompt, url = generate_image(prompt, message_id, message_id, image_dir)
|
| 193 |
if img:
|
| 194 |
formatted_ret_prompt = "\n\n".join(
|
| 195 |
f"_{paragraph.strip()}_" for paragraph in ret_prompt.split("\n\n") if paragraph.strip()
|
|
|
|
| 198 |
message_id,
|
| 199 |
chat_id,
|
| 200 |
path,
|
| 201 |
+
caption=f"✨ Image ready: {url}\n>{chr(8203)} {formatted_ret_prompt}"
|
| 202 |
)
|
| 203 |
else:
|
| 204 |
send_message(message_id, chat_id, "Image generation failed.")
|
| 205 |
+
break # exit on success
|
| 206 |
except Exception as e:
|
| 207 |
logging.error("Error in handle_image_generation: %s", e)
|
| 208 |
send_message(message_id, chat_id, "Error generating image.")
|
| 209 |
|
| 210 |
+
# --- New background thread: WhatsApp Status Updater ---
|
| 211 |
+
def whatsapp_status_updater():
|
| 212 |
+
"""
|
| 213 |
+
Every 2 minutes, this function uses the LLM to generate a random creative image prompt,
|
| 214 |
+
generates an image from it, and sends it as a WhatsApp status update.
|
| 215 |
+
"""
|
| 216 |
+
while True:
|
| 217 |
+
try:
|
| 218 |
+
# Generate a random image prompt via LLM
|
| 219 |
+
random_prompt = generate_llm("Generate a creative and random image prompt for a WhatsApp status update.")
|
| 220 |
+
logging.info("Random status prompt: %s", random_prompt)
|
| 221 |
+
# Generate image from the prompt; using "status" as a dummy message/chat id.
|
| 222 |
+
img, path, ret_prompt, url = generate_image(random_prompt, "status", "status", image_dir)
|
| 223 |
+
if img and os.path.exists(path):
|
| 224 |
+
caption = f"Status: {random_prompt}"
|
| 225 |
+
send_status_image(path, caption)
|
| 226 |
+
# Optionally remove the image file after sending
|
| 227 |
+
os.remove(path)
|
| 228 |
+
else:
|
| 229 |
+
logging.error("Image generation for status failed.")
|
| 230 |
+
except Exception as e:
|
| 231 |
+
logging.error("Error in whatsapp_status_updater: %s", e)
|
| 232 |
+
# Sleep for 2 minutes
|
| 233 |
+
time.sleep(120)
|
| 234 |
+
|
| 235 |
+
# Start the WhatsApp status updater thread
|
| 236 |
+
threading.Thread(target=whatsapp_status_updater, daemon=True).start()
|
| 237 |
+
|
| 238 |
# --- Startup Message ---
|
| 239 |
def send_startup_message():
|
| 240 |
if BOT_STATUS_CHAT:
|
|
|
|
| 276 |
data = await request.json()
|
| 277 |
chat_id = data["senderData"]["chatId"]
|
| 278 |
print(f"New message from chat ID: {chat_id}")
|
| 279 |
+
except:
|
| 280 |
return JSONResponse({"error": "Invalid JSON"}, status_code=400)
|
| 281 |
if data.get("typeWebhook") != "incomingMessageReceived":
|
| 282 |
return {"success": True}
|
|
|
|
| 333 |
try:
|
| 334 |
joke = requests.get("https://official-joke-api.appspot.com/random_joke", timeout=5).json()
|
| 335 |
send_message(mid, chat, f"{joke['setup']}\n\n{joke['punchline']}")
|
| 336 |
+
except:
|
| 337 |
send_message(mid, chat, generate_llm("Tell me a short, funny joke."))
|
| 338 |
return {"success": True}
|
| 339 |
|
|
|
|
| 342 |
try:
|
| 343 |
w = requests.get(f"http://sl.wttr.in/{loc}?format=4", timeout=5).text
|
| 344 |
send_message(mid, chat, w)
|
| 345 |
+
except:
|
| 346 |
send_message(mid, chat, "Could not fetch weather.")
|
| 347 |
return {"success": True}
|
| 348 |
|