Chandima Prabhath commited on
Commit
61fd286
·
1 Parent(s): 1ef8ab5

Refactor code for improved readability and consistency; adjust spacing and formatting in various functions.

Browse files
Files changed (1) hide show
  1. app.py +137 -87
app.py CHANGED
@@ -100,17 +100,22 @@ def worker():
100
  while True:
101
  t = task_queue.get()
102
  try:
103
- if t["type"]=="image":
104
- _fn_generate_images(t["message_id"],t["chat_id"],t["prompt"],t.get("num_images",1))
105
- elif t["type"]=="audio":
106
- _fn_voice_reply(t["message_id"],t["chat_id"],t["prompt"])
 
 
 
 
 
107
  except Exception as e:
108
  logging.error(f"Worker error {t}: {e}")
109
  finally:
110
  task_queue.task_done()
111
 
112
  for _ in range(4):
113
- threading.Thread(target=worker,daemon=True).start()
114
 
115
  # --- Tool Functions ---
116
 
@@ -124,14 +129,14 @@ def _fn_translate(mid, cid, lang, text):
124
 
125
  def _fn_joke(mid, cid):
126
  try:
127
- j = requests.get("https://official-joke-api.appspot.com/random_joke",timeout=5).json()
128
  joke = f"{j['setup']}\n\n{j['punchline']}"
129
  except:
130
  joke = generate_llm("Tell me a short joke.")
131
  _fn_send_text(mid, cid, joke)
132
 
133
  def _fn_weather(mid, cid, loc):
134
- raw = requests.get(f"http://sl.wttr.in/{loc}?format=4",timeout=5).text
135
  r = generate_llm(f"Give a weather report in °C:\n\n{raw}")
136
  _fn_send_text(mid, cid, r)
137
 
@@ -141,21 +146,28 @@ def _fn_inspire(mid, cid):
141
 
142
  def _fn_meme(mid, cid, txt):
143
  client.send_message(mid, cid, "🎨 Generating meme…")
144
- task_queue.put({"type":"image","message_id":mid,"chat_id":cid,"prompt":f"meme: {txt}"})
 
 
 
 
 
145
 
146
  def _fn_poll_create(mid, cid, question, options):
147
  votes = {i+1:0 for i in range(len(options))}
148
- polls[cid] = {"question":question,"options":options,"votes":votes,"voters":{}}
149
  text = f"📊 *Poll:* {question}\n" + "\n".join(f"{i+1}. {o}" for i,o in enumerate(options))
150
  _fn_send_text(mid, cid, text)
151
 
152
  def _fn_poll_vote(mid, cid, voter, choice):
153
  poll = polls.get(cid)
154
- if not poll or choice<1 or choice>len(poll["options"]): return
 
155
  prev = poll["voters"].get(voter)
156
- if prev: poll["votes"][prev]-=1
157
- poll["votes"][choice]+=1
158
- poll["voters"][voter]=choice
 
159
  _fn_send_text(mid, cid, f"✅ Voted for {poll['options'][choice-1]}")
160
 
161
  def _fn_poll_results(mid, cid):
@@ -169,7 +181,7 @@ def _fn_poll_results(mid, cid):
169
  _fn_send_text(mid, cid, txt)
170
 
171
  def _fn_poll_end(mid, cid):
172
- poll = polls.pop(cid,None)
173
  if not poll:
174
  _fn_send_text(mid, cid, "No active poll.")
175
  return
@@ -179,9 +191,9 @@ def _fn_poll_end(mid, cid):
179
  _fn_send_text(mid, cid, txt)
180
 
181
  def _fn_generate_images(mid, cid, prompt, count=1):
182
- for i in range(1,count+1):
183
  try:
184
- img,path,ret_p,url = generate_image(prompt,mid,mid,BotConfig.IMAGE_DIR)
185
  formatted = "\n\n".join(f"_{p.strip()}_" for p in ret_p.split("\n\n") if p.strip())
186
  cap = f"✨ Image {i}/{count}: {url}\n>{chr(8203)} {formatted}"
187
  client.send_media(mid, cid, path, cap, media_type="image")
@@ -206,9 +218,14 @@ def _fn_voice_reply(mid, cid, prompt):
206
  f"Just say this exactly as written in a flirty, friendly, playful, "
207
  f"happy and helpful but a little bit clumsy-cute way: {prompt}"
208
  )
209
- res = generate_voice_reply(processed,model="openai-audio",voice="coral",audio_dir=BotConfig.AUDIO_DIR)
 
 
 
 
 
210
  if res and res[0]:
211
- path,_ = res
212
  client.send_media(mid, cid, path, "", media_type="audio")
213
  os.remove(path)
214
  else:
@@ -218,18 +235,18 @@ def _fn_voice_reply(mid, cid, prompt):
218
  # --- Function schema & router ---
219
 
220
  FUNCTION_SCHEMA = {
221
- "summarize": {"description":"Summarize text","params":["text"]},
222
- "translate": {"description":"Translate text","params":["lang","text"]},
223
- "joke": {"description":"Tell a joke","params":[]},
224
- "weather": {"description":"Weather report","params":["location"]},
225
- "inspire": {"description":"Inspirational quote","params":[]},
226
- "meme": {"description":"Generate meme","params":["text"]},
227
- "poll_create": {"description":"Create poll","params":["question","options"]},
228
- "poll_vote": {"description":"Vote poll","params":["choice"]},
229
- "poll_results": {"description":"Show poll results","params":[]},
230
- "poll_end": {"description":"End poll","params":[]},
231
- "generate_image":{"description":"Generate images","params":["prompt","count"]},
232
- "send_text": {"description":"Send plain text","params":["message"]}
233
  }
234
 
235
  def route_intent(user_input: str):
@@ -267,64 +284,85 @@ help_text = (
267
  async def whatsapp_webhook(request: Request):
268
  data = await request.json()
269
  if request.headers.get("Authorization") != f"Bearer {BotConfig.WEBHOOK_AUTH_TOKEN}":
270
- raise HTTPException(403,"Unauthorized")
271
 
272
  chat_id = data["senderData"]["chatId"]
273
- if chat_id != BotConfig.BOT_GROUP_CHAT or data["typeWebhook"]!="incomingMessageReceived":
274
- return {"success":True}
275
 
276
- md = data["messageData"]
277
- mid = data["idMessage"]
278
- tmd = md.get("textMessageData") or md.get("extendedTextMessageData")
279
  if not tmd:
280
- return {"success":True}
281
- body = (tmd.get("textMessage") or tmd.get("text","")).strip()
282
- ctx = tmd.get("contextInfo",{})
 
283
 
284
  # Slash commands
285
  low = body.lower()
286
- if low=="/help":
287
- _fn_send_text(mid,chat_id,help_text); return {"success":True}
 
288
  if low.startswith("/summarize "):
289
- _fn_summarize(mid,chat_id,body[11:].strip()); return {"success":True}
 
290
  if low.startswith("/translate "):
291
- lang,txt = body[11:].split("|",1)
292
- _fn_translate(mid,chat_id,lang.strip(),txt.strip()); return {"success":True}
293
- if low=="/joke":
294
- _fn_joke(mid,chat_id); return {"success":True}
 
 
295
  if low.startswith("/weather "):
296
- _fn_weather(mid,chat_id,body[9:].strip().replace(" ","+")); return {"success":True}
297
- if low=="/inspire":
298
- _fn_inspire(mid,chat_id); return {"success":True}
 
 
299
  if low.startswith("/meme "):
300
- _fn_meme(mid,chat_id,body[6:].strip()); return {"success":True}
 
301
  if low.startswith("/poll "):
302
- parts=[p.strip() for p in body[6:].split("|")]
303
- _fn_poll_create(mid,chat_id,parts[0],parts[1:]); return {"success":True}
 
304
  if chat_id in polls and low.isdigit():
305
- _fn_poll_vote(mid,chat_id,data["senderData"]["sender"],int(low)); return {"success":True}
306
- if low=="/results":
307
- _fn_poll_results(mid,chat_id); return {"success":True}
308
- if low=="/endpoll":
309
- _fn_poll_end(mid,chat_id); return {"success":True}
 
 
 
310
  if low.startswith("/gen"):
311
- parts=body[4:].split("|",1)
312
- pr=parts[0].strip()
313
- ct=int(parts[1]) if len(parts)>1 and parts[1].isdigit() else BotConfig.DEFAULT_IMAGE_COUNT
314
- client.send_message(mid,chat_id,f"✨ Generating {ct} images…")
315
- task_queue.put({"type":"image","message_id":mid,"chat_id":chat_id,"prompt":pr,"num_images":ct})
316
- return {"success":True}
 
 
 
 
 
 
317
 
318
  # Skip mentions
319
  if ctx.get("mentionedJidList"):
320
- return {"success":True}
321
 
322
  # Build effective_text (include quoted if replying to bot)
323
- if md.get("typeMessage")=="quotedMessage":
324
- ext=md["extendedTextMessageData"]
325
- quoted=md["quotedMessage"]
326
- if ext.get("participant")==BotConfig.BOT_JID:
327
- effective = f"Quoted: {quoted.get('textMessage','')}\nUser: {ext.get('text','')}"
 
 
 
328
  else:
329
  effective = body
330
  else:
@@ -334,37 +372,49 @@ async def whatsapp_webhook(request: Request):
334
  intent = route_intent(effective)
335
  action = intent.get("action")
336
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
  dispatch = {
338
- "summarize": lambda: _fn_summarize(mid,chat_id,intent["text"]),
339
- "translate": lambda: _fn_translate(mid,chat_id,intent["lang"],intent["text"]),
340
- "joke": lambda: _fn_joke(mid,chat_id),
341
- "weather": lambda: _fn_weather(mid,chat_id,intent["location"]),
342
- "inspire": lambda: _fn_inspire(mid,chat_id),
343
- "meme": lambda: _fn_meme(mid,chat_id,intent["text"]),
344
- "poll_create": lambda: _fn_poll_create(mid,chat_id,intent["question"],intent["options"]),
345
- "poll_vote": lambda: _fn_poll_vote(mid,chat_id,data["senderData"]["sender"],intent["choice"]),
346
- "poll_results": lambda: _fn_poll_results(mid,chat_id),
347
- "poll_end": lambda: _fn_poll_end(mid,chat_id),
348
- "generate_image":lambda: _fn_generate_images(mid,chat_id,intent["prompt"],intent.get("count",1)),
349
- "send_text": lambda: _fn_send_text(mid,chat_id,intent["message"]),
350
  }
351
 
352
  if action in dispatch:
353
  dispatch[action]()
354
  else:
355
- # fallback to send_text (which also queues voice)
356
- _fn_send_text(mid,chat_id,intent.get("message","Sorry, I didn't get that."))
357
 
358
- return {"success":True}
359
 
360
- @app.get("/",response_class=PlainTextResponse)
361
  def index():
362
  return "Server is running!"
363
 
364
- if __name__=="__main__":
365
  client.send_message_to(
366
  BotConfig.BOT_GROUP_CHAT,
367
  "🌟 Eve is online! Type /help to see commands."
368
  )
369
  import uvicorn
370
- uvicorn.run(app,host="0.0.0.0",port=7860)
 
100
  while True:
101
  t = task_queue.get()
102
  try:
103
+ if t["type"] == "image":
104
+ _fn_generate_images(
105
+ t["message_id"],
106
+ t["chat_id"],
107
+ t["prompt"],
108
+ t.get("num_images", 1)
109
+ )
110
+ elif t["type"] == "audio":
111
+ _fn_voice_reply(t["message_id"], t["chat_id"], t["prompt"])
112
  except Exception as e:
113
  logging.error(f"Worker error {t}: {e}")
114
  finally:
115
  task_queue.task_done()
116
 
117
  for _ in range(4):
118
+ threading.Thread(target=worker, daemon=True).start()
119
 
120
  # --- Tool Functions ---
121
 
 
129
 
130
  def _fn_joke(mid, cid):
131
  try:
132
+ j = requests.get("https://official-joke-api.appspot.com/random_joke", timeout=5).json()
133
  joke = f"{j['setup']}\n\n{j['punchline']}"
134
  except:
135
  joke = generate_llm("Tell me a short joke.")
136
  _fn_send_text(mid, cid, joke)
137
 
138
  def _fn_weather(mid, cid, loc):
139
+ raw = requests.get(f"http://sl.wttr.in/{loc}?format=4", timeout=5).text
140
  r = generate_llm(f"Give a weather report in °C:\n\n{raw}")
141
  _fn_send_text(mid, cid, r)
142
 
 
146
 
147
  def _fn_meme(mid, cid, txt):
148
  client.send_message(mid, cid, "🎨 Generating meme…")
149
+ task_queue.put({
150
+ "type": "image",
151
+ "message_id": mid,
152
+ "chat_id": cid,
153
+ "prompt": f"meme: {txt}"
154
+ })
155
 
156
  def _fn_poll_create(mid, cid, question, options):
157
  votes = {i+1:0 for i in range(len(options))}
158
+ polls[cid] = {"question": question, "options": options, "votes": votes, "voters": {}}
159
  text = f"📊 *Poll:* {question}\n" + "\n".join(f"{i+1}. {o}" for i,o in enumerate(options))
160
  _fn_send_text(mid, cid, text)
161
 
162
  def _fn_poll_vote(mid, cid, voter, choice):
163
  poll = polls.get(cid)
164
+ if not poll or choice < 1 or choice > len(poll["options"]):
165
+ return
166
  prev = poll["voters"].get(voter)
167
+ if prev:
168
+ poll["votes"][prev] -= 1
169
+ poll["votes"][choice] += 1
170
+ poll["voters"][voter] = choice
171
  _fn_send_text(mid, cid, f"✅ Voted for {poll['options'][choice-1]}")
172
 
173
  def _fn_poll_results(mid, cid):
 
181
  _fn_send_text(mid, cid, txt)
182
 
183
  def _fn_poll_end(mid, cid):
184
+ poll = polls.pop(cid, None)
185
  if not poll:
186
  _fn_send_text(mid, cid, "No active poll.")
187
  return
 
191
  _fn_send_text(mid, cid, txt)
192
 
193
  def _fn_generate_images(mid, cid, prompt, count=1):
194
+ for i in range(1, count+1):
195
  try:
196
+ img, path, ret_p, url = generate_image(prompt, mid, mid, BotConfig.IMAGE_DIR)
197
  formatted = "\n\n".join(f"_{p.strip()}_" for p in ret_p.split("\n\n") if p.strip())
198
  cap = f"✨ Image {i}/{count}: {url}\n>{chr(8203)} {formatted}"
199
  client.send_media(mid, cid, path, cap, media_type="image")
 
218
  f"Just say this exactly as written in a flirty, friendly, playful, "
219
  f"happy and helpful but a little bit clumsy-cute way: {prompt}"
220
  )
221
+ res = generate_voice_reply(
222
+ processed,
223
+ model="openai-audio",
224
+ voice="coral",
225
+ audio_dir=BotConfig.AUDIO_DIR
226
+ )
227
  if res and res[0]:
228
+ path, _ = res
229
  client.send_media(mid, cid, path, "", media_type="audio")
230
  os.remove(path)
231
  else:
 
235
  # --- Function schema & router ---
236
 
237
  FUNCTION_SCHEMA = {
238
+ "summarize": {"description":"Summarize text","params":["text"]},
239
+ "translate": {"description":"Translate text","params":["lang","text"]},
240
+ "joke": {"description":"Tell a joke","params":[]},
241
+ "weather": {"description":"Weather report","params":["location"]},
242
+ "inspire": {"description":"Inspirational quote","params":[]},
243
+ "meme": {"description":"Generate meme","params":["text"]},
244
+ "poll_create": {"description":"Create poll","params":["question","options"]},
245
+ "poll_vote": {"description":"Vote poll","params":["choice"]},
246
+ "poll_results": {"description":"Show poll results","params":[]},
247
+ "poll_end": {"description":"End poll","params":[]},
248
+ "generate_image": {"description":"Generate images","params":["prompt","count"]},
249
+ "send_text": {"description":"Send plain text","params":["message"]}
250
  }
251
 
252
  def route_intent(user_input: str):
 
284
  async def whatsapp_webhook(request: Request):
285
  data = await request.json()
286
  if request.headers.get("Authorization") != f"Bearer {BotConfig.WEBHOOK_AUTH_TOKEN}":
287
+ raise HTTPException(403, "Unauthorized")
288
 
289
  chat_id = data["senderData"]["chatId"]
290
+ if chat_id != BotConfig.BOT_GROUP_CHAT or data["typeWebhook"] != "incomingMessageReceived":
291
+ return {"success": True}
292
 
293
+ md = data["messageData"]
294
+ mid = data["idMessage"]
295
+ tmd = md.get("textMessageData") or md.get("extendedTextMessageData")
296
  if not tmd:
297
+ return {"success": True}
298
+
299
+ body = (tmd.get("textMessage") or tmd.get("text", "")).strip()
300
+ ctx = tmd.get("contextInfo", {})
301
 
302
  # Slash commands
303
  low = body.lower()
304
+ if low == "/help":
305
+ _fn_send_text(mid, chat_id, help_text)
306
+ return {"success": True}
307
  if low.startswith("/summarize "):
308
+ _fn_summarize(mid, chat_id, body[11:].strip())
309
+ return {"success": True}
310
  if low.startswith("/translate "):
311
+ lang, txt = body[11:].split("|", 1)
312
+ _fn_translate(mid, chat_id, lang.strip(), txt.strip())
313
+ return {"success": True}
314
+ if low == "/joke":
315
+ _fn_joke(mid, chat_id)
316
+ return {"success": True}
317
  if low.startswith("/weather "):
318
+ _fn_weather(mid, chat_id, body[9:].strip().replace(" ", "+"))
319
+ return {"success": True}
320
+ if low == "/inspire":
321
+ _fn_inspire(mid, chat_id)
322
+ return {"success": True}
323
  if low.startswith("/meme "):
324
+ _fn_meme(mid, chat_id, body[6:].strip())
325
+ return {"success": True}
326
  if low.startswith("/poll "):
327
+ parts = [p.strip() for p in body[6:].split("|")]
328
+ _fn_poll_create(mid, chat_id, parts[0], parts[1:])
329
+ return {"success": True}
330
  if chat_id in polls and low.isdigit():
331
+ _fn_poll_vote(mid, chat_id, data["senderData"]["sender"], int(low))
332
+ return {"success": True}
333
+ if low == "/results":
334
+ _fn_poll_results(mid, chat_id)
335
+ return {"success": True}
336
+ if low == "/endpoll":
337
+ _fn_poll_end(mid, chat_id)
338
+ return {"success": True}
339
  if low.startswith("/gen"):
340
+ parts = body[4:].split("|", 1)
341
+ pr = parts[0].strip()
342
+ ct = int(parts[1]) if len(parts)>1 and parts[1].isdigit() else BotConfig.DEFAULT_IMAGE_COUNT
343
+ client.send_message(mid, chat_id, f"✨ Generating {ct} images…")
344
+ task_queue.put({
345
+ "type": "image",
346
+ "message_id": mid,
347
+ "chat_id": chat_id,
348
+ "prompt": pr,
349
+ "num_images": ct
350
+ })
351
+ return {"success": True}
352
 
353
  # Skip mentions
354
  if ctx.get("mentionedJidList"):
355
+ return {"success": True}
356
 
357
  # Build effective_text (include quoted if replying to bot)
358
+ if md.get("typeMessage") == "quotedMessage":
359
+ ext = md["extendedTextMessageData"]
360
+ quoted = md["quotedMessage"]
361
+ if ext.get("participant") == BotConfig.BOT_JID:
362
+ effective = (
363
+ f"Quoted: {quoted.get('textMessage','')}\n"
364
+ f"User: {ext.get('text','')}"
365
+ )
366
  else:
367
  effective = body
368
  else:
 
372
  intent = route_intent(effective)
373
  action = intent.get("action")
374
 
375
+ # helper to confirm+enqueue image generation
376
+ def _dispatch_generate_image():
377
+ prompt = intent["prompt"]
378
+ count = intent.get("count", 1)
379
+ client.send_message(mid, chat_id, f"✨ Generating {count} image(s)…")
380
+ task_queue.put({
381
+ "type": "image",
382
+ "message_id": mid,
383
+ "chat_id": chat_id,
384
+ "prompt": prompt,
385
+ "num_images": count
386
+ })
387
+
388
  dispatch = {
389
+ "summarize": lambda: _fn_summarize(mid, chat_id, intent["text"]),
390
+ "translate": lambda: _fn_translate(mid, chat_id, intent["lang"], intent["text"]),
391
+ "joke": lambda: _fn_joke(mid, chat_id),
392
+ "weather": lambda: _fn_weather(mid, chat_id, intent["location"]),
393
+ "inspire": lambda: _fn_inspire(mid, chat_id),
394
+ "meme": lambda: _fn_meme(mid, chat_id, intent["text"]),
395
+ "poll_create": lambda: _fn_poll_create(mid, chat_id, intent["question"], intent["options"]),
396
+ "poll_vote": lambda: _fn_poll_vote(mid, chat_id, data["senderData"]["sender"], intent["choice"]),
397
+ "poll_results": lambda: _fn_poll_results(mid, chat_id),
398
+ "poll_end": lambda: _fn_poll_end(mid, chat_id),
399
+ "generate_image": _dispatch_generate_image,
400
+ "send_text": lambda: _fn_send_text(mid, chat_id, intent["message"]),
401
  }
402
 
403
  if action in dispatch:
404
  dispatch[action]()
405
  else:
406
+ _fn_send_text(mid, chat_id, intent.get("message", "Sorry, I didn't get that."))
 
407
 
408
+ return {"success": True}
409
 
410
+ @app.get("/", response_class=PlainTextResponse)
411
  def index():
412
  return "Server is running!"
413
 
414
+ if __name__ == "__main__":
415
  client.send_message_to(
416
  BotConfig.BOT_GROUP_CHAT,
417
  "🌟 Eve is online! Type /help to see commands."
418
  )
419
  import uvicorn
420
+ uvicorn.run(app, host="0.0.0.0", port=7860)