Sanyam400 commited on
Commit
bb57d82
Β·
verified Β·
1 Parent(s): 76b1985

Update app/telegram_bot.py

Browse files
Files changed (1) hide show
  1. app/telegram_bot.py +83 -132
app/telegram_bot.py CHANGED
@@ -1,8 +1,8 @@
1
  """
2
- Telegram Bot β€” PraisonChat v4
3
- Handles: text, voice audio, images, files, code results
4
  """
5
- import os, json, asyncio, base64, traceback
6
  import httpx
7
  import config as cfg
8
 
@@ -10,39 +10,49 @@ TELEGRAM_API = "https://api.telegram.org/bot{token}/{method}"
10
  _histories: dict = {}
11
 
12
 
13
- def _url(method: str) -> str:
14
  return TELEGRAM_API.format(token=cfg.get_telegram_token(), method=method)
15
 
16
 
17
- async def _post(method: str, **kwargs) -> dict:
18
- async with httpx.AsyncClient(timeout=30) as c:
 
 
 
 
 
 
 
 
 
 
 
 
19
  r = await c.post(_url(method), **kwargs)
20
  return r.json()
21
 
22
 
23
- async def send_message(chat_id: int, text: str, parse_mode: str = "Markdown") -> bool:
24
- if not text.strip(): return True
25
  chunks = [text[i:i+3900] for i in range(0, len(text), 3900)]
26
  for chunk in chunks:
27
  try:
28
- r = await _post("sendMessage",
29
- json={"chat_id": chat_id, "text": chunk, "parse_mode": parse_mode})
30
  if not r.get("ok"):
31
  await _post("sendMessage", json={"chat_id": chat_id, "text": chunk})
32
  except Exception as e:
33
- print(f"[TG] send_message error: {e}")
34
  return True
35
 
36
 
37
- async def send_typing(chat_id: int):
38
  try:
39
- await _post("sendChatAction",
40
- json={"chat_id": chat_id, "action": "typing"})
41
  except Exception:
42
  pass
43
 
44
 
45
- async def send_voice(chat_id: int, audio_b64: str) -> bool:
46
  try:
47
  audio_bytes = base64.b64decode(audio_b64)
48
  async with httpx.AsyncClient(timeout=60) as c:
@@ -51,25 +61,24 @@ async def send_voice(chat_id: int, audio_b64: str) -> bool:
51
  data={"chat_id": str(chat_id)})
52
  return r.json().get("ok", False)
53
  except Exception as e:
54
- print(f"[TG] send_voice error: {e}")
55
  return False
56
 
57
 
58
- async def send_photo(chat_id: int, img_b64: str, filename: str, ext: str) -> bool:
59
  try:
60
  img_bytes = base64.b64decode(img_b64)
61
- mime = f"image/{ext}" if ext else "image/png"
62
  async with httpx.AsyncClient(timeout=30) as c:
63
  r = await c.post(_url("sendPhoto"),
64
- files={"photo": (filename, img_bytes, mime)},
65
  data={"chat_id": str(chat_id), "caption": f"πŸ–ΌοΈ {filename}"})
66
  return r.json().get("ok", False)
67
  except Exception as e:
68
- print(f"[TG] send_photo error: {e}")
69
  return False
70
 
71
 
72
- async def send_document(chat_id: int, file_b64: str, filename: str) -> bool:
73
  try:
74
  file_bytes = base64.b64decode(file_b64)
75
  async with httpx.AsyncClient(timeout=60) as c:
@@ -78,58 +87,48 @@ async def send_document(chat_id: int, file_b64: str, filename: str) -> bool:
78
  data={"chat_id": str(chat_id), "caption": f"πŸ“„ {filename}"})
79
  return r.json().get("ok", False)
80
  except Exception as e:
81
- print(f"[TG] send_document error: {e}")
82
  return False
83
 
84
 
85
- async def set_webhook(base_url: str) -> dict:
86
  url = base_url.rstrip("/") + "/telegram/webhook"
87
  return await _post("setWebhook",
88
  json={"url": url, "allowed_updates": ["message"], "drop_pending_updates": True})
89
 
90
 
91
- async def delete_webhook() -> dict:
92
  return await _post("deleteWebhook", json={})
93
 
94
 
95
- async def get_bot_info() -> dict:
96
- return await _post("getMe")
97
 
98
 
99
- async def get_webhook_info() -> dict:
100
- return await _post("getWebhookInfo")
101
 
102
 
103
- async def handle_update(update: dict, api_key: str, model: str):
104
  from agent_system import orchestrator
105
 
106
  msg = update.get("message") or update.get("edited_message")
107
- if not msg:
108
- return
109
 
110
  chat_id = msg["chat"]["id"]
111
- text = msg.get("text", "").strip()
112
- username = msg.get("from", {}).get("first_name", "User")
113
 
114
- if not text:
115
- return
116
-
117
- print(f"[TG] {username} ({chat_id}): {text[:80]}")
118
 
119
  # Commands
120
  if text == "/start":
121
  await send_message(chat_id,
122
- f"πŸ‘‹ Hello {username}! I'm *PraisonChat* 🐱\n\n"
123
- "I have a real Python interpreter β€” I write code to solve your tasks:\n"
124
- "πŸ” Real web search (DuckDuckGo)\n"
125
- "πŸ• Real date & time\n"
126
- "🐍 Real code execution\n"
127
- "πŸ“¦ Auto-install any Python package\n"
128
- "πŸ”Š Voice audio generation\n"
129
- "πŸ–ΌοΈ Image generation/processing\n"
130
- "πŸ“„ File creation & download\n\n"
131
- "Just type anything β€” I'll write code to make it happen!\n\n"
132
- "/clear - Clear history | /help - Help")
133
  return
134
 
135
  if text == "/clear":
@@ -137,29 +136,31 @@ async def handle_update(update: dict, api_key: str, model: str):
137
  await send_message(chat_id, "βœ… History cleared!")
138
  return
139
 
 
 
 
 
 
 
140
  if text == "/help":
141
  await send_message(chat_id,
142
- "πŸ€– *Try these:*\n"
143
- "β€’ `What time is it?`\n"
144
- "β€’ `Search for latest AI news`\n"
145
- "β€’ `Create a QR code for https://example.com`\n"
146
- "β€’ `Plot a bar chart of population by continent`\n"
147
- "β€’ `Say hello in voice`\n"
148
- "β€’ `Write a Python script to generate a password`\n"
149
- "β€’ `Fetch the weather in London`\n\n"
150
- "I write *real Python code* and execute it β€” not simulated!")
151
  return
152
 
153
- # Immediate ack
154
  await send_typing(chat_id)
155
- await send_message(chat_id, "⏳ On it…")
156
 
157
- history = _histories.get(chat_id, [])
158
  full_response = ""
159
  audio_b64 = None
160
  has_media = False
161
- activity_msgs = []
162
- code_outputs = []
163
 
164
  try:
165
  async for chunk_json in orchestrator.stream_response(text, history, api_key, model):
@@ -168,106 +169,56 @@ async def handle_update(update: dict, api_key: str, model: str):
168
  except Exception:
169
  continue
170
 
171
- t = ev.get("type", "")
172
-
173
- if t == "thinking":
174
- pass
175
-
176
- elif t == "code_executing":
177
- idx = ev.get("index", 0)
178
- activity_msgs.append(f"🐍 Running code block {idx+1}…")
179
-
180
- elif t == "code_result":
181
- stdout = ev.get("stdout", "").strip()
182
- ok = ev.get("ok", False)
183
- if stdout and len(stdout) < 400:
184
- code_outputs.append(f"{'βœ…' if ok else '❌'} ```\n{stdout}\n```")
185
- elif not ok:
186
- stderr = ev.get("stderr","")[:200]
187
- code_outputs.append(f"❌ Error: `{stderr}`")
188
-
189
- elif t == "pkg_install":
190
- pkg = ev.get("package", "")
191
- ok = ev.get("ok", False)
192
- if pkg:
193
- activity_msgs.append(f"{'πŸ“¦βœ…' if ok else 'πŸ“¦βŒ'} `{pkg}`")
194
-
195
- elif t == "agent_created":
196
- activity_msgs.append(f"πŸ€– Agent: *{ev.get('name','')}*")
197
-
198
- elif t == "token":
199
- full_response += ev.get("content", "")
200
 
 
 
201
  elif t == "audio_response":
202
  audio_b64 = ev.get("audio_b64")
203
- has_media = True
204
-
205
  elif t == "image_response":
206
- img_b64 = ev.get("image_b64", "")
207
- filename = ev.get("filename", "image.png")
208
- ext = ev.get("ext", "png")
209
  if img_b64:
210
  await send_typing(chat_id)
211
  await send_photo(chat_id, img_b64, filename, ext)
212
  has_media = True
213
-
214
  elif t == "file_response":
215
- file_b64 = ev.get("file_b64", "")
216
- filename = ev.get("filename", "file")
217
  if file_b64:
218
  await send_typing(chat_id)
219
  await send_document(chat_id, file_b64, filename)
220
  has_media = True
221
-
222
- elif t == "voice_fallback":
223
- vtext = ev.get("text", "")
224
- if vtext:
225
- full_response += f"\n\nπŸ”Š _{vtext[:300]}_"
226
-
227
  elif t == "error":
228
- err = ev.get("message", "unknown")
229
- print(f"[TG] Error: {err}")
230
- await send_message(chat_id, f"❌ Error: {err[:300]}")
231
  return
232
 
233
  except Exception as e:
234
- print(f"[TG] Exception: {e}\n{traceback.format_exc()}")
235
  await send_message(chat_id, f"❌ Error: {str(e)[:200]}")
236
  return
237
 
238
- # Send activity summary
239
- if activity_msgs:
240
- await send_message(chat_id, "\n".join(activity_msgs[:6]))
241
-
242
- # Send code outputs if short
243
- if code_outputs and not full_response:
244
- await send_message(chat_id, "\n\n".join(code_outputs[:3]))
245
-
246
- # Send main text response
247
  clean = (full_response
248
  .replace("**","*")
249
- .replace("<execute>","```python\n")
250
- .replace("</execute>","\n```")
251
- .replace("[VOICE:","").strip())
252
- # Remove VOICE] tags
253
  import re
254
- clean = re.sub(r'\[VOICE:.*?\]', '', clean, flags=re.DOTALL).strip()
255
 
256
  if clean:
257
  await send_message(chat_id, clean)
258
- elif not has_media and not code_outputs:
259
- await send_message(chat_id, "_(Processing complete β€” no text output)_")
260
 
261
- # Send voice
262
  if audio_b64:
263
  await send_typing(chat_id)
264
- ok = await send_voice(chat_id, audio_b64)
265
- if not ok:
266
- await send_message(chat_id, "⚠️ Could not send voice file")
267
 
268
  # Update history
269
- history.append({"role": "user", "content": text})
270
- history.append({"role": "assistant", "content": full_response or "(media response)"})
271
  _histories[chat_id] = history[-24:]
272
 
273
- print(f"[TG] Done: {username} ({chat_id}), {len(full_response)} chars")
 
1
  """
2
+ Telegram Bot β€” PraisonChat v6
3
+ Fixed: DNS error handling, env var fallback, robust connection
4
  """
5
+ import os, json, asyncio, base64, traceback, socket
6
  import httpx
7
  import config as cfg
8
 
 
10
  _histories: dict = {}
11
 
12
 
13
+ def _url(method):
14
  return TELEGRAM_API.format(token=cfg.get_telegram_token(), method=method)
15
 
16
 
17
+ def check_telegram_reachable() -> tuple[bool, str]:
18
+ """Check if api.telegram.org is reachable via DNS."""
19
+ try:
20
+ socket.setdefaulttimeout(5)
21
+ socket.gethostbyname("api.telegram.org")
22
+ return True, "OK"
23
+ except socket.gaierror as e:
24
+ return False, f"DNS error: api.telegram.org not reachable from this server. Error: {e}"
25
+ except Exception as e:
26
+ return False, str(e)
27
+
28
+
29
+ async def _post(method, timeout=15, **kwargs):
30
+ async with httpx.AsyncClient(timeout=timeout) as c:
31
  r = await c.post(_url(method), **kwargs)
32
  return r.json()
33
 
34
 
35
+ async def send_message(chat_id, text, parse_mode="Markdown"):
36
+ if not text or not text.strip(): return True
37
  chunks = [text[i:i+3900] for i in range(0, len(text), 3900)]
38
  for chunk in chunks:
39
  try:
40
+ r = await _post("sendMessage", json={"chat_id": chat_id, "text": chunk, "parse_mode": parse_mode})
 
41
  if not r.get("ok"):
42
  await _post("sendMessage", json={"chat_id": chat_id, "text": chunk})
43
  except Exception as e:
44
+ print(f"[TG] send error: {e}")
45
  return True
46
 
47
 
48
+ async def send_typing(chat_id):
49
  try:
50
+ await _post("sendChatAction", json={"chat_id": chat_id, "action": "typing"})
 
51
  except Exception:
52
  pass
53
 
54
 
55
+ async def send_voice(chat_id, audio_b64):
56
  try:
57
  audio_bytes = base64.b64decode(audio_b64)
58
  async with httpx.AsyncClient(timeout=60) as c:
 
61
  data={"chat_id": str(chat_id)})
62
  return r.json().get("ok", False)
63
  except Exception as e:
64
+ print(f"[TG] voice error: {e}")
65
  return False
66
 
67
 
68
+ async def send_photo(chat_id, img_b64, filename, ext):
69
  try:
70
  img_bytes = base64.b64decode(img_b64)
 
71
  async with httpx.AsyncClient(timeout=30) as c:
72
  r = await c.post(_url("sendPhoto"),
73
+ files={"photo": (filename, img_bytes, f"image/{ext}")},
74
  data={"chat_id": str(chat_id), "caption": f"πŸ–ΌοΈ {filename}"})
75
  return r.json().get("ok", False)
76
  except Exception as e:
77
+ print(f"[TG] photo error: {e}")
78
  return False
79
 
80
 
81
+ async def send_document(chat_id, file_b64, filename):
82
  try:
83
  file_bytes = base64.b64decode(file_b64)
84
  async with httpx.AsyncClient(timeout=60) as c:
 
87
  data={"chat_id": str(chat_id), "caption": f"πŸ“„ {filename}"})
88
  return r.json().get("ok", False)
89
  except Exception as e:
90
+ print(f"[TG] doc error: {e}")
91
  return False
92
 
93
 
94
+ async def set_webhook(base_url):
95
  url = base_url.rstrip("/") + "/telegram/webhook"
96
  return await _post("setWebhook",
97
  json={"url": url, "allowed_updates": ["message"], "drop_pending_updates": True})
98
 
99
 
100
+ async def delete_webhook():
101
  return await _post("deleteWebhook", json={})
102
 
103
 
104
+ async def get_bot_info():
105
+ return await _post("getMe", timeout=10)
106
 
107
 
108
+ async def get_webhook_info():
109
+ return await _post("getWebhookInfo", timeout=10)
110
 
111
 
112
+ async def handle_update(update, api_key, model):
113
  from agent_system import orchestrator
114
 
115
  msg = update.get("message") or update.get("edited_message")
116
+ if not msg: return
 
117
 
118
  chat_id = msg["chat"]["id"]
119
+ text = msg.get("text","").strip()
120
+ username = msg.get("from",{}).get("first_name","User")
121
 
122
+ if not text: return
123
+ print(f"[TG] {username} ({chat_id}): {text[:60]}")
 
 
124
 
125
  # Commands
126
  if text == "/start":
127
  await send_message(chat_id,
128
+ f"πŸ‘‹ Hello {username}! I'm *PraisonChat* 🦞\n\n"
129
+ "I'm an autonomous AI agent with real code execution.\n\n"
130
+ "Commands: /clear /help /status\n\n"
131
+ "Just type anything!")
 
 
 
 
 
 
 
132
  return
133
 
134
  if text == "/clear":
 
136
  await send_message(chat_id, "βœ… History cleared!")
137
  return
138
 
139
+ if text == "/status":
140
+ await send_message(chat_id,
141
+ f"βœ… Online\nModel: `{model}`\n"
142
+ f"API: `{'set βœ…' if api_key else 'missing ❌'}`")
143
+ return
144
+
145
  if text == "/help":
146
  await send_message(chat_id,
147
+ "*PraisonChat* can:\n"
148
+ "β€’ πŸ” Search the web in real-time\n"
149
+ "β€’ πŸ• Tell you date and time\n"
150
+ "β€’ 🐍 Execute Python code\n"
151
+ "β€’ πŸ”Š Generate voice audio\n"
152
+ "β€’ πŸ“Š Create charts and images\n"
153
+ "β€’ πŸ€– Spawn sub-agents\n\n"
154
+ "Try: `What time is it?` or `Search for latest AI news`")
 
155
  return
156
 
 
157
  await send_typing(chat_id)
158
+ await send_message(chat_id, "⏳ Working on it…")
159
 
160
+ history = _histories.get(chat_id, [])
161
  full_response = ""
162
  audio_b64 = None
163
  has_media = False
 
 
164
 
165
  try:
166
  async for chunk_json in orchestrator.stream_response(text, history, api_key, model):
 
169
  except Exception:
170
  continue
171
 
172
+ t = ev.get("type","")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
 
174
+ if t == "token":
175
+ full_response += ev.get("content","")
176
  elif t == "audio_response":
177
  audio_b64 = ev.get("audio_b64")
178
+ has_media = True
 
179
  elif t == "image_response":
180
+ img_b64 = ev.get("image_b64","")
181
+ filename = ev.get("filename","image.png")
182
+ ext = ev.get("ext","png")
183
  if img_b64:
184
  await send_typing(chat_id)
185
  await send_photo(chat_id, img_b64, filename, ext)
186
  has_media = True
 
187
  elif t == "file_response":
188
+ file_b64 = ev.get("file_b64","")
189
+ filename = ev.get("filename","file")
190
  if file_b64:
191
  await send_typing(chat_id)
192
  await send_document(chat_id, file_b64, filename)
193
  has_media = True
 
 
 
 
 
 
194
  elif t == "error":
195
+ await send_message(chat_id, f"❌ {ev.get('message','Error')[:300]}")
 
 
196
  return
197
 
198
  except Exception as e:
199
+ print(f"[TG] exception: {e}\n{traceback.format_exc()}")
200
  await send_message(chat_id, f"❌ Error: {str(e)[:200]}")
201
  return
202
 
203
+ # Send text response
 
 
 
 
 
 
 
 
204
  clean = (full_response
205
  .replace("**","*")
206
+ .replace("[SPEAK:","").replace("]",""))
 
 
 
207
  import re
208
+ clean = re.sub(r'<[^>]+>.*?</[^>]+>', '', clean, flags=re.DOTALL).strip()
209
 
210
  if clean:
211
  await send_message(chat_id, clean)
212
+ elif not has_media:
213
+ await send_message(chat_id, "_(Done β€” no text output)_")
214
 
 
215
  if audio_b64:
216
  await send_typing(chat_id)
217
+ await send_voice(chat_id, audio_b64)
 
 
218
 
219
  # Update history
220
+ history.append({"role":"user","content":text})
221
+ history.append({"role":"assistant","content":full_response or "(media)"})
222
  _histories[chat_id] = history[-24:]
223
 
224
+ print(f"[TG] Replied to {username}: {len(full_response)} chars")