From 7eca0763c9bbf5e4276f47b9d3d8383bf9985e22 Mon Sep 17 00:00:00 2001 From: Variet Worker Date: Wed, 11 Mar 2026 22:31:08 +0900 Subject: [PATCH] =?UTF-8?q?fix(collector):=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EB=88=84=EB=9D=BD=203=EA=B1=B4=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=E2=80=94=20Discord=20=EB=AA=85=EB=A0=B9=EC=96=B4/=EC=B1=84?= =?UTF-8?q?=ED=8C=85/=EB=93=B1=EB=A1=9D=20=EC=A4=91=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gap 1: Discord→Extension 명령어 깨짐 - bot.py: _write_command() 래퍼 — gateway.push_command()도 호출 - main.py: bot.gateway 연결 - 슬래시 명령어 + on_message 모두 _write_command 사용 Gap 2: Chat snapshot 미전달 - collector.py: _forward_chat_snapshots_loop 추가 Gap 3: Session registration 미전달 - collector.py: _forward_registrations_loop 추가 --- bot.py | 24 ++++++++++++++++++------ collector.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ main.py | 1 + 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/bot.py b/bot.py index 9169266..6082db1 100644 --- a/bot.py +++ b/bot.py @@ -180,6 +180,18 @@ class GravityBot(commands.Bot): self.session_category: discord.CategoryChannel | None = None self.guild: discord.Guild | None = None self.auto_approve_projects: set[str] = set() # projects with auto-approve enabled + self.gateway = None # Set by main.py in gateway mode + + def _write_command(self, project: str, text: str, **kwargs): + """Write command to bridge AND push to gateway (if gateway mode).""" + self.bridge.write_command(project, text, **kwargs) + if self.gateway: + import time + self.gateway.push_command(project, { + "id": str(int(time.time() * 1000)), + "text": text, + "project_name": kwargs.get('project_name', project), + }) @staticmethod def _make_channel_name(project_name: str) -> str: @@ -202,7 +214,7 @@ class GravityBot(commands.Bot): if not project: await interaction.response.send_message("⚠️ 프로젝트 채널이 아닙니다.", ephemeral=True) return - self.bridge.write_command(project, "!stop", project_name=project) + self._write_command(project, "!stop", project_name=project) await interaction.response.send_message( embed=discord.Embed( title="⏹️ AI 작업 중지", @@ -224,7 +236,7 @@ class GravityBot(commands.Bot): else: self.auto_approve_projects.add(project) enabled = True - self.bridge.write_command(project, f"!auto {'on' if enabled else 'off'}", project_name=project) + self._write_command(project, f"!auto {'on' if enabled else 'off'}", project_name=project) emoji = "🟢" if enabled else "🔴" await interaction.response.send_message( embed=discord.Embed( @@ -240,7 +252,7 @@ class GravityBot(commands.Bot): if not project: await interaction.response.send_message("⚠️ 프로젝트 채널이 아닙니다.", ephemeral=True) return - self.bridge.write_command(project, message, project_name=project) + self._write_command(project, message, project_name=project) await interaction.response.send_message( embed=discord.Embed( description=f"📨 → **{project}** IDE에 전달됨\n`{message[:100]}`", @@ -754,7 +766,7 @@ class GravityBot(commands.Bot): # Special command: !stop — cancel AI work if text == "!stop": - self.bridge.write_command(project, "!stop", project_name=project) + self._write_command(project, "!stop", project_name=project) embed = discord.Embed( title="⏹️ AI 작업 중지", description=f"프로젝트: **{project}**\n중지 요청을 Extension에 전달했습니다.", @@ -772,7 +784,7 @@ class GravityBot(commands.Bot): else: self.auto_approve_projects.add(project) enabled = True - self.bridge.write_command(project, f"!auto {'on' if enabled else 'off'}", project_name=project) + self._write_command(project, f"!auto {'on' if enabled else 'off'}", project_name=project) emoji = "🟢" if enabled else "🔴" mode = "자동 승인" if enabled else "수동 승인" embed = discord.Embed( @@ -786,7 +798,7 @@ class GravityBot(commands.Bot): # General text relay — routed by project if text: - self.bridge.write_command(project, text, project_name=project) + self._write_command(project, text, project_name=project) await message.add_reaction("📨") embed = discord.Embed( description=f"📨 → **{project}** IDE에 전달됨\n`{text[:100]}`", diff --git a/collector.py b/collector.py index 82f1e23..b81f2a4 100644 --- a/collector.py +++ b/collector.py @@ -50,6 +50,8 @@ class CollectorBridge: self._forward_pending_loop(), self._poll_responses_loop(), self._poll_commands_loop(), + self._forward_chat_snapshots_loop(), + self._forward_registrations_loop(), ] if self.event_queue: tasks.append(self._forward_events_loop()) @@ -132,6 +134,56 @@ class CollectorBridge: await asyncio.sleep(self._poll_interval) + # ─── Forward chat snapshots → Gateway ─── + + async def _forward_chat_snapshots_loop(self): + """Forward chat_snapshots/ from Extension to Gateway.""" + while self._running: + try: + snap_dir = self.local.bridge_dir / "chat_snapshots" + if snap_dir.exists(): + for f in snap_dir.glob("*.json"): + try: + data = json.loads(f.read_text(encoding="utf-8-sig")) + project = data.get("project_name", self.project_name) + content = data.get("content", "") + if content: + self.remote.send_chat(project, content) + logger.info(f"[COLLECTOR] → Gateway: chat snapshot len={len(content)}") + f.unlink() # Cleanup after forwarding + except (json.JSONDecodeError, OSError) as e: + logger.warning(f"[COLLECTOR] bad chat snapshot {f.name}: {e}") + except Exception as e: + logger.error(f"[COLLECTOR] forward_chat_snapshots error: {e}") + + await asyncio.sleep(self._poll_interval) + + # ─── Forward session registrations → Gateway ─── + + async def _forward_registrations_loop(self): + """Forward register/ files from Extension to Gateway.""" + forwarded_regs: set[str] = set() + while self._running: + try: + register_dir = self.local.bridge_dir / "register" + if register_dir.exists(): + for f in register_dir.glob("*.json"): + if f.name in forwarded_regs: + continue + try: + data = json.loads(f.read_text(encoding="utf-8-sig")) + conv_id = data.get("conversation_id", "") + project = data.get("project_name", "") + if conv_id and project: + self.remote.register_session(conv_id, project) + forwarded_regs.add(f.name) + logger.info(f"[COLLECTOR] → Gateway: register {conv_id[:8]} → {project}") + except (json.JSONDecodeError, OSError) as e: + logger.warning(f"[COLLECTOR] bad register {f.name}: {e}") + except Exception as e: + logger.error(f"[COLLECTOR] forward_registrations error: {e}") + + await asyncio.sleep(self._poll_interval * 3) # Less frequent # ─── Forward brain events → Gateway ─── async def _forward_events_loop(self): diff --git a/main.py b/main.py index 1df5939..db6c624 100644 --- a/main.py +++ b/main.py @@ -107,6 +107,7 @@ async def main(): from gateway import GatewayAPI gateway_port = int(os.environ.get('GATEWAY_PORT', '8585')) gateway = GatewayAPI(bot, port=gateway_port, api_key=Config.GATEWAY_API_KEY) + bot.gateway = gateway # Enable _write_command → gateway.push_command await gateway.start() logger.info(f"Gateway API running on port {gateway_port}")