FreshPixels commited on
Commit
077c98a
·
verified ·
1 Parent(s): fc5b058

Upload 2 files

Browse files
Files changed (2) hide show
  1. routes.py +30 -0
  2. telegram_handlers.py +318 -0
routes.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """HTTP маршруты"""
2
+
3
+ from http.server import BaseHTTPRequestHandler
4
+ import json
5
+ from .telegram_handlers import BotHandler
6
+
7
+ class WebhookHandler(BotHandler, BaseHTTPRequestHandler):
8
+ """Обработчик вебхуков Telegram"""
9
+
10
+ def __init__(self, request, client_address, server):
11
+ # Сначала инициализируем BotHandler (без аргументов)
12
+ BotHandler.__init__(self)
13
+ # Затем BaseHTTPRequestHandler (с аргументами)
14
+ BaseHTTPRequestHandler.__init__(self, request, client_address, server)
15
+
16
+ def do_GET(self):
17
+ self.send_response(200)
18
+ self.end_headers()
19
+ self.wfile.write(b"PinkSky v7.0 is running!")
20
+
21
+ def do_POST(self):
22
+ length = int(self.headers.get('Content-Length', 0))
23
+ data = json.loads(self.rfile.read(length))
24
+ self.send_response(200)
25
+ self.end_headers()
26
+ self.wfile.write(b"OK")
27
+ self.handle_message(data)
28
+
29
+ def log_message(self, format, *args):
30
+ pass
telegram_handlers.py ADDED
@@ -0,0 +1,318 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Обработчики команд Telegram"""
2
+
3
+ import json
4
+ import threading
5
+ from datetime import datetime
6
+ from .config import ALLOWED_USER
7
+ from .state import STATE
8
+ from .process_manager import PROCESS_MANAGER
9
+ from .internet_agent import INTERNET_AGENT
10
+ from .file_manager import FILE_MANAGER
11
+ from .notification_system import NOTIFICATIONS
12
+ from .conductor_engine import ConductorEngine
13
+ from .skill_orchestrator import SkillOrchestrator
14
+ from .build_mode_editor import BuildModeEditor
15
+ from .mode_handlers import run_chat_mode, run_skill_mode, run_build_mode
16
+ from .telegram_utils import send_tg, download_tg_file, send_tg_file
17
+ from .helpers import parse_skill_agents, parse_build_args, get_current_mode_info
18
+
19
+ class BotHandler:
20
+ HELP_TEXT = """PinkSky v7.0 — МОДУЛЬНАЯ АРХИТЕКТУРА
21
+
22
+ 🎛️ Режимы:
23
+ Chat — отправь сообщение
24
+ /skill [задача] — Open Interpreter
25
+ /build [спек] — сборка через кондуктор
26
+
27
+ 🎭 Роли:
28
+ /role [guru|hacker|architect|principal|evangelist|techlead|qa|sdet|qe|researcher|critic|universal]
29
+ /role list — список ролей
30
+
31
+ 🧠 Кондукторы:
32
+ /conductor [default|strict|creative|economy|review|build]
33
+ /conductor list — список
34
+
35
+ 🤖 Модели:
36
+ /model [имя] — переключить модель
37
+ /model list — список моделей
38
+
39
+ 📁 Файлы:
40
+ /get [путь] — скачать файл
41
+ /stop — остановить все процессы
42
+
43
+ 🌐 Интернет:
44
+ /search [запрос] — поиск в интернете
45
+ /cache — статистика кэша
46
+ /cache clear — очистить кэш
47
+
48
+ 📊 Система:
49
+ /status — текущий статус
50
+ /history — история
51
+ /export [json|md] — экспорт истории
52
+ /mode — текущий режим"""
53
+
54
+ def __init__(self, *args, **kwargs):
55
+ # Принимаем args/kwargs для совместимости с BaseHTTPRequestHandler.__init__
56
+ # BotHandler не использует их напрямую
57
+ self.mode_editor = BuildModeEditor(STATE)
58
+ self.skill_orchestrator = SkillOrchestrator(STATE)
59
+
60
+ def handle_message(self, data: dict):
61
+ if "message" not in data:
62
+ return
63
+
64
+ message = data["message"]
65
+ chat_id = str(message.get("chat", {}).get("id", ""))
66
+ if ALLOWED_USER and chat_id != ALLOWED_USER:
67
+ return
68
+
69
+ NOTIFICATIONS.set_chat_id(chat_id)
70
+
71
+ text = message.get("text", message.get("caption", "")).strip()
72
+
73
+ file_id, file_name = None, None
74
+ target_msg = message
75
+ if "reply_to_message" in message:
76
+ if "document" in message["reply_to_message"] or "photo" in message["reply_to_message"]:
77
+ target_msg = message["reply_to_message"]
78
+ if "document" in target_msg:
79
+ file_id = target_msg["document"]["file_id"]
80
+ file_name = target_msg["document"].get("file_name", "document.file")
81
+ elif "photo" in target_msg:
82
+ file_id = target_msg["photo"][-1]["file_id"]
83
+ file_name = "photo.jpg"
84
+
85
+ file_context = ""
86
+ if file_id:
87
+ dl_path = download_tg_file(file_id, file_name)
88
+ if dl_path:
89
+ file_context = f"\n\n[SYSTEM: User attached file: ./{dl_path}]"
90
+ if not text:
91
+ send_tg(chat_id, f"📁 Файл `{file_name}` сохранён. Ответь командой, например: `/build Что в этом файле?`")
92
+ return
93
+
94
+ if not text:
95
+ return
96
+
97
+ parts = text.split(maxsplit=1)
98
+ command = parts[0].lower()
99
+ args = parts[1].strip() if len(parts) > 1 else ""
100
+
101
+ # === HELP ===
102
+ if command in ["/help", "/start"]:
103
+ send_tg(chat_id, self.HELP_TEXT)
104
+ return
105
+
106
+ # === STOP ===
107
+ if command == "/stop":
108
+ result = PROCESS_MANAGER.cancel_all()
109
+ send_tg(chat_id, f"{result}\n\n{self.HELP_TEXT}")
110
+ return
111
+
112
+ # === STATUS ===
113
+ if command == "/status":
114
+ info = get_current_mode_info()
115
+ stats = f"""{info}
116
+
117
+ 📊 Моделей: {len(STATE.models)}
118
+ 📊 Ролей: {len(STATE.roles)}
119
+ 📊 Кондукторов: {len(STATE.conductors)}
120
+ 🌐 Интернет: {'✅' if STATE.build_context.get('internet_access', True) else '❌'}
121
+ 🗑️ Кэш: {INTERNET_AGENT.get_cache_stats()}
122
+ 🧵 Активных потоков: {PROCESS_MANAGER.get_active_count()}"""
123
+ send_tg(chat_id, stats)
124
+ return
125
+
126
+ # === MODE ===
127
+ if command == "/mode":
128
+ send_tg(chat_id, get_current_mode_info())
129
+ return
130
+
131
+ # === CONDUCTOR ===
132
+ if command == "/conductor":
133
+ if args == "list":
134
+ lines = ["🧠 Доступные кондукторы:"]
135
+ for name, cond in STATE.conductors.items():
136
+ marker = " [ACTIVE]" if name == STATE.current_conductor else ""
137
+ lines.append(f"- {name}{marker}: {cond.description} [rank_by={cond.auto_rank_by}]")
138
+ send_tg(chat_id, "\n".join(lines))
139
+ return
140
+ if args in STATE.conductors:
141
+ STATE.current_conductor = args
142
+ send_tg(chat_id, f"🧠 Кондуктор: {args.upper()}\n{STATE.conductors[args].description}")
143
+ else:
144
+ available = ", ".join(STATE.conductors.keys())
145
+ send_tg(chat_id, f"Текущий: {STATE.current_conductor}\nДоступные: {available}")
146
+ return
147
+
148
+ # === ROLE ===
149
+ if command == "/role":
150
+ role_parts = args.split(maxsplit=1)
151
+ subcmd = role_parts[0].lower() if role_parts else ""
152
+ subargs = role_parts[1] if len(role_parts) > 1 else ""
153
+
154
+ if subcmd == "list":
155
+ lines = ["🎭 Доступные роли:"]
156
+ for name, role in STATE.roles.items():
157
+ marker = " [ACTIVE]" if name == STATE.current_role else ""
158
+ lines.append(f"- {name}{marker}: {role.description} (complexity: {role.complexity})")
159
+ send_tg(chat_id, "\n".join(lines))
160
+ return
161
+
162
+ if subcmd == "info" and subargs:
163
+ role = STATE.roles.get(subargs)
164
+ if role:
165
+ send_tg(chat_id, f"🎭 Роль {subargs}:\n\n{role.description}\n\nComplexity: {role.complexity}\nPreferred: {', '.join(role.preferred_models)}\n\nPrompt:\n```\n{role.prompt[:500]}...\n```")
166
+ else:
167
+ send_tg(chat_id, f"❌ Роль {subargs} не найдена")
168
+ return
169
+
170
+ if subcmd == "add" and subargs:
171
+ add_parts = subargs.split(maxsplit=1)
172
+ if len(add_parts) < 2:
173
+ send_tg(chat_id, "Формат: /role add <name> <prompt>")
174
+ return
175
+ new_name, new_prompt = add_parts[0], add_parts[1]
176
+ from .models import Role
177
+ STATE.roles[new_name] = Role(
178
+ name=new_name, prompt=new_prompt,
179
+ description=f"Custom role (added {datetime.now().strftime('%Y-%m-%d')})",
180
+ complexity="medium"
181
+ )
182
+ STATE.save_roles()
183
+ send_tg(chat_id, f"✅ Роль {new_name} добавлена!")
184
+ return
185
+
186
+ if subcmd in STATE.roles:
187
+ STATE.current_role = subcmd
188
+ send_tg(chat_id, f"🎭 Роль: {subcmd.upper()}\n{STATE.roles[subcmd].description}")
189
+ else:
190
+ available = ", ".join(STATE.roles.keys())
191
+ send_tg(chat_id, f"Текущая: {STATE.current_role}\nДоступные: {available}")
192
+ return
193
+
194
+ # === MODEL ===
195
+ if command == "/model":
196
+ model_parts = args.split(maxsplit=1)
197
+ subcmd = model_parts[0].lower() if model_parts else ""
198
+ subargs = model_parts[1] if len(model_parts) > 1 else ""
199
+
200
+ if subcmd == "list":
201
+ lines = ["🤖 Модели (по coding rank):"]
202
+ for name, model in sorted(STATE.models.items(), key=lambda x: x[1].coding_rank):
203
+ if name == "hf_fallback":
204
+ continue
205
+ marker = " [ACTIVE]" if name == STATE.current_model else ""
206
+ tier = 1
207
+ if model.coding_rank > 5: tier = 2
208
+ if model.coding_rank > 12: tier = 3
209
+ if model.coding_rank > 18: tier = 4
210
+ if model.coding_rank > 24: tier = 5
211
+ cost = f"${model.cost_per_1k_output}/1k" if model.cost_per_1k_output > 0 else "free"
212
+ lines.append(f"- {name}{marker} -- TIER {tier} | coding={model.coding_rank} speed={model.speed_rank} reasoning={model.reasoning_rank} | {cost}")
213
+ send_tg(chat_id, "\n".join(lines))
214
+ return
215
+
216
+ if subcmd == "add" and subargs:
217
+ add_parts = subargs.split()
218
+ if len(add_parts) < 2:
219
+ send_tg(chat_id, "Формат: /model add <name> <endpoint>")
220
+ return
221
+ new_name, new_endpoint = add_parts[0], add_parts[1]
222
+ from .models import ModelConfig
223
+ STATE.models[new_name] = ModelConfig(
224
+ name=new_name, provider="openai", endpoint=new_endpoint,
225
+ api_key_env="NVIDIA_API_KEY"
226
+ )
227
+ STATE.save_models()
228
+ send_tg(chat_id, f"✅ Модель {new_name} добавлена!")
229
+ return
230
+
231
+ if subcmd in STATE.models:
232
+ STATE.current_model = subcmd
233
+ send_tg(chat_id, f"🤖 Модель: {subcmd.upper()}\n{STATE.models[subcmd].endpoint}")
234
+ else:
235
+ available = ", ".join([k for k in STATE.models.keys() if k != "hf_fallback"])
236
+ send_tg(chat_id, f"Текущая: {STATE.current_model}\nДоступные: {available}")
237
+ return
238
+
239
+ # === GET FILE ===
240
+ if command == "/get":
241
+ if not args:
242
+ send_tg(chat_id, "Использование: /get ./projects/test.py")
243
+ else:
244
+ send_tg_file(chat_id, args)
245
+ return
246
+
247
+ # === SEARCH ===
248
+ if command == "/search":
249
+ if not args:
250
+ send_tg(chat_id, "Использование: /search запрос")
251
+ return
252
+ results = INTERNET_AGENT.search_web(args)
253
+ if results:
254
+ lines = [f"🔍 Результаты поиска: {args}\n"]
255
+ for i, r in enumerate(results[:5], 1):
256
+ lines.append(f"{i}. [{r['title']}]({r['url']})\n{r['snippet'][:150]}...\n")
257
+ send_tg(chat_id, "\n".join(lines))
258
+ else:
259
+ send_tg(chat_id, "❌ Ничего не найдено")
260
+ return
261
+
262
+ # === CACHE ===
263
+ if command == "/cache":
264
+ if args == "clear":
265
+ result = INTERNET_AGENT.clear_cache()
266
+ send_tg(chat_id, result)
267
+ else:
268
+ send_tg(chat_id, INTERNET_AGENT.get_cache_stats())
269
+ return
270
+
271
+ # === HISTORY ===
272
+ if command == "/history":
273
+ mode = args if args in ("chat", "skill", "build") else "chat"
274
+ history = getattr(STATE, f"{mode}_history", [])
275
+ if not history:
276
+ send_tg(chat_id, f"📋 История {mode} пуста")
277
+ return
278
+ lines = [f"📋 История {mode} (последние 5):"]
279
+ for entry in history[-5:]:
280
+ ts = entry.get("timestamp", "unknown")
281
+ role = entry.get("role", "unknown")
282
+ content = entry.get("content", "")[:200]
283
+ lines.append(f"\n[{ts}] {role}:\n{content}...")
284
+ send_tg(chat_id, "\n".join(lines))
285
+ return
286
+
287
+ # === EXPORT ===
288
+ if command == "/export":
289
+ fmt = args if args in ("json", "md") else "json"
290
+ if fmt == "json":
291
+ data = STATE.export_history_json()
292
+ FILE_MANAGER.save_file("exports/history.json", data)
293
+ send_tg_file(chat_id, "exports/history.json")
294
+ else:
295
+ data = STATE.export_history_md()
296
+ FILE_MANAGER.save_file("exports/history.md", data)
297
+ send_tg_file(chat_id, "exports/history.md")
298
+ return
299
+
300
+ # === BUILD MODE ===
301
+ if command == "/build":
302
+ if not args:
303
+ send_tg(chat_id, "Укажите спецификацию проекта.")
304
+ return
305
+ params, clean_args = parse_build_args(args)
306
+ run_build_mode(chat_id, clean_args, file_context, params)
307
+ return
308
+
309
+ # === SKILL MODE ===
310
+ if command == "/skill":
311
+ if not args:
312
+ send_tg(chat_id, "Укажите задачу.")
313
+ return
314
+ run_skill_mode(chat_id, args, file_context)
315
+ return
316
+
317
+ # === DEFAULT: CHAT MODE ===
318
+ run_chat_mode(chat_id, text, file_context)