Spaces:
Running
Running
Chandima Prabhath
commited on
Commit
Β·
1ef8ab5
1
Parent(s):
8dc8276
Refactor message handling to use _fn_send_text for consistent text and audio responses; update voice reply prompt for a playful tone.
Browse files
app.py
CHANGED
@@ -86,6 +86,7 @@ class BotClient:
|
|
86 |
files = [("file",(os.path.basename(file_path),f,mime))]
|
87 |
return self.send(endpoint,payload,files=files)
|
88 |
|
|
|
89 |
BotConfig.validate()
|
90 |
client = BotClient(BotConfig)
|
91 |
|
@@ -115,11 +116,11 @@ for _ in range(4):
|
|
115 |
|
116 |
def _fn_summarize(mid, cid, text):
|
117 |
s = generate_llm(f"Summarize:\n\n{text}")
|
118 |
-
|
119 |
|
120 |
def _fn_translate(mid, cid, lang, text):
|
121 |
r = generate_llm(f"Translate to {lang}:\n\n{text}")
|
122 |
-
|
123 |
|
124 |
def _fn_joke(mid, cid):
|
125 |
try:
|
@@ -127,82 +128,92 @@ def _fn_joke(mid, cid):
|
|
127 |
joke = f"{j['setup']}\n\n{j['punchline']}"
|
128 |
except:
|
129 |
joke = generate_llm("Tell me a short joke.")
|
130 |
-
|
131 |
|
132 |
-
def _fn_weather(mid,cid,loc):
|
133 |
raw = requests.get(f"http://sl.wttr.in/{loc}?format=4",timeout=5).text
|
134 |
r = generate_llm(f"Give a weather report in Β°C:\n\n{raw}")
|
135 |
-
|
136 |
-
task_queue.put({"type":"audio","message_id":mid,"chat_id":cid,"prompt":r})
|
137 |
|
138 |
-
def _fn_inspire(mid,cid):
|
139 |
-
q = generate_llm("Give me a unique random short inspirational quote.")
|
140 |
-
|
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 |
-
|
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 |
-
|
160 |
|
161 |
-
def _fn_poll_results(mid,cid):
|
162 |
poll = polls.get(cid)
|
163 |
if not poll:
|
164 |
-
|
165 |
return
|
166 |
txt = f"π *Results:* {poll['question']}\n" + "\n".join(
|
167 |
f"{i}. {o}: {poll['votes'][i]}" for i,o in enumerate(poll["options"],1)
|
168 |
)
|
169 |
-
|
170 |
|
171 |
-
def _fn_poll_end(mid,cid):
|
172 |
poll = polls.pop(cid,None)
|
173 |
if not poll:
|
174 |
-
|
175 |
return
|
176 |
txt = f"π *Final Results:* {poll['question']}\n" + "\n".join(
|
177 |
f"{i}. {o}: {poll['votes'][i]}" for i,o in enumerate(poll["options"],1)
|
178 |
)
|
179 |
-
|
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")
|
188 |
os.remove(path)
|
189 |
except Exception as e:
|
190 |
logging.warning(f"Img {i}/{count} failed: {e}")
|
191 |
-
|
192 |
-
|
193 |
-
def _fn_send_text(mid,cid,message):
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
199 |
if res and res[0]:
|
200 |
path,_ = res
|
201 |
-
client.send_media(mid,cid,path,"",media_type="audio")
|
202 |
os.remove(path)
|
203 |
else:
|
204 |
-
|
205 |
-
|
206 |
|
207 |
# --- Function schema & router ---
|
208 |
|
@@ -210,8 +221,7 @@ FUNCTION_SCHEMA = {
|
|
210 |
"summarize": {"description":"Summarize text","params":["text"]},
|
211 |
"translate": {"description":"Translate text","params":["lang","text"]},
|
212 |
"joke": {"description":"Tell a joke","params":[]},
|
213 |
-
"weather": {"description":"
|
214 |
-
"weather_poem": {"description":"Poetic weather","params":["location"]},
|
215 |
"inspire": {"description":"Inspirational quote","params":[]},
|
216 |
"meme": {"description":"Generate meme","params":["text"]},
|
217 |
"poll_create": {"description":"Create poll","params":["question","options"]},
|
@@ -246,13 +256,11 @@ help_text = (
|
|
246 |
"β’ /translate <lang>|<text>\n"
|
247 |
"β’ /joke\n"
|
248 |
"β’ /weather <loc>\n"
|
249 |
-
"β’ /weatherpoem <loc>\n"
|
250 |
"β’ /inspire\n"
|
251 |
-
"β’ /trivia / /answer\n"
|
252 |
"β’ /meme <text>\n"
|
253 |
"β’ /poll <Q>|β¦ / /results / /endpoll\n"
|
254 |
"β’ /gen <prompt>|<count>\n"
|
255 |
-
"Otherwise chat
|
256 |
)
|
257 |
|
258 |
@app.post("/whatsapp")
|
@@ -276,7 +284,7 @@ async def whatsapp_webhook(request: Request):
|
|
276 |
# Slash commands
|
277 |
low = body.lower()
|
278 |
if low=="/help":
|
279 |
-
|
280 |
if low.startswith("/summarize "):
|
281 |
_fn_summarize(mid,chat_id,body[11:].strip()); return {"success":True}
|
282 |
if low.startswith("/translate "):
|
@@ -344,10 +352,8 @@ async def whatsapp_webhook(request: Request):
|
|
344 |
if action in dispatch:
|
345 |
dispatch[action]()
|
346 |
else:
|
347 |
-
# fallback
|
348 |
-
|
349 |
-
_fn_send_text(mid,chat_id,txt)
|
350 |
-
task_queue.put({"type":"audio","message_id":mid,"chat_id":chat_id,"prompt":txt})
|
351 |
|
352 |
return {"success":True}
|
353 |
|
@@ -356,7 +362,9 @@ def index():
|
|
356 |
return "Server is running!"
|
357 |
|
358 |
if __name__=="__main__":
|
359 |
-
client.send_message_to(
|
360 |
-
|
|
|
|
|
361 |
import uvicorn
|
362 |
uvicorn.run(app,host="0.0.0.0",port=7860)
|
|
|
86 |
files = [("file",(os.path.basename(file_path),f,mime))]
|
87 |
return self.send(endpoint,payload,files=files)
|
88 |
|
89 |
+
# Validate env
|
90 |
BotConfig.validate()
|
91 |
client = BotClient(BotConfig)
|
92 |
|
|
|
116 |
|
117 |
def _fn_summarize(mid, cid, text):
|
118 |
s = generate_llm(f"Summarize:\n\n{text}")
|
119 |
+
_fn_send_text(mid, cid, s)
|
120 |
|
121 |
def _fn_translate(mid, cid, lang, text):
|
122 |
r = generate_llm(f"Translate to {lang}:\n\n{text}")
|
123 |
+
_fn_send_text(mid, cid, r)
|
124 |
|
125 |
def _fn_joke(mid, cid):
|
126 |
try:
|
|
|
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 |
|
138 |
+
def _fn_inspire(mid, cid):
|
139 |
+
q = generate_llm("Give me a unique, random short inspirational quote.")
|
140 |
+
_fn_send_text(mid, cid, f"β¨ {q}")
|
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):
|
162 |
poll = polls.get(cid)
|
163 |
if not poll:
|
164 |
+
_fn_send_text(mid, cid, "No active poll.")
|
165 |
return
|
166 |
txt = f"π *Results:* {poll['question']}\n" + "\n".join(
|
167 |
f"{i}. {o}: {poll['votes'][i]}" for i,o in enumerate(poll["options"],1)
|
168 |
)
|
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
|
176 |
txt = f"π *Final Results:* {poll['question']}\n" + "\n".join(
|
177 |
f"{i}. {o}: {poll['votes'][i]}" for i,o in enumerate(poll["options"],1)
|
178 |
)
|
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")
|
188 |
os.remove(path)
|
189 |
except Exception as e:
|
190 |
logging.warning(f"Img {i}/{count} failed: {e}")
|
191 |
+
_fn_send_text(mid, cid, f"π’ Failed to generate image {i}/{count}.")
|
192 |
+
|
193 |
+
def _fn_send_text(mid, cid, message):
|
194 |
+
# send text...
|
195 |
+
client.send_message(mid, cid, message)
|
196 |
+
# ...and queue voice with the same content
|
197 |
+
task_queue.put({
|
198 |
+
"type": "audio",
|
199 |
+
"message_id": mid,
|
200 |
+
"chat_id": cid,
|
201 |
+
"prompt": message
|
202 |
+
})
|
203 |
+
|
204 |
+
def _fn_voice_reply(mid, cid, prompt):
|
205 |
+
processed = (
|
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:
|
215 |
+
# fallback to text+voice
|
216 |
+
_fn_send_text(mid, cid, prompt)
|
217 |
|
218 |
# --- Function schema & router ---
|
219 |
|
|
|
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"]},
|
|
|
256 |
"β’ /translate <lang>|<text>\n"
|
257 |
"β’ /joke\n"
|
258 |
"β’ /weather <loc>\n"
|
|
|
259 |
"β’ /inspire\n"
|
|
|
260 |
"β’ /meme <text>\n"
|
261 |
"β’ /poll <Q>|β¦ / /results / /endpoll\n"
|
262 |
"β’ /gen <prompt>|<count>\n"
|
263 |
+
"Otherwise chat or reply to my message to invoke tools."
|
264 |
)
|
265 |
|
266 |
@app.post("/whatsapp")
|
|
|
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 "):
|
|
|
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 |
|
|
|
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)
|