fix: channel duplication root fix - ready gate + conv_id-first + Discord API search + hash pre-init

This commit is contained in:
2026-03-07 13:09:05 +09:00
parent de6f1c7ffd
commit 7c081e70b5
2 changed files with 44 additions and 14 deletions

37
bot.py
View File

@@ -130,6 +130,7 @@ class GravityBot(commands.Bot):
self.session_names: dict[str, str] = {}
self._channel_create_lock = asyncio.Lock() # SINGLE global lock
self._sent_approval_ids: set[str] = set() # Track sent approvals
self._ready_event = asyncio.Event() # Gate: wait until on_ready finishes
self.bridge = BridgeProtocol()
self.session_category: discord.CategoryChannel | None = None
self.guild: discord.Guild | None = None
@@ -163,6 +164,10 @@ class GravityBot(commands.Bot):
# ONLY reconnect to existing Discord channels (NO new creation)
await self._reconnect_existing_channels()
# NOW allow event processing to begin
self._ready_event.set()
logger.info("Ready gate opened — event processing enabled")
async def _reconnect_existing_channels(self):
"""Scan existing Discord channels and map them — MERGE same-name channels."""
if not self.session_category:
@@ -219,32 +224,38 @@ class GravityBot(commands.Bot):
async def _ensure_channel(
self, conversation_id: str, project_name: str
) -> discord.TextChannel:
"""Get or create a channel. SINGLE channel per project name, guaranteed."""
channel_name = f"{Config.CHANNEL_PREFIX}-{project_name}"
target_name = channel_name.lower().replace(" ", "-")
"""Get or create a channel. ONE channel per conv_id, guaranteed."""
# Fast path: this conv_id already mapped
# Fast path: this conv_id already has a channel — ALWAYS return it
# (even if project name changed; name changes are cosmetic, not worth a new channel)
if conversation_id in self.session_channels:
ch = self.session_channels[conversation_id]
# Verify the channel name matches (project name might have changed)
if ch.name == target_name:
return ch
return self.session_channels[conversation_id]
async with self._channel_create_lock:
# Double-check after lock
if conversation_id in self.session_channels:
ch = self.session_channels[conversation_id]
if ch.name == target_name:
return ch
return self.session_channels[conversation_id]
channel_name = f"{Config.CHANNEL_PREFIX}-{project_name}"
target_name = channel_name.lower().replace(" ", "-")
# Check ALL mapped channels for same name
for cid, ch in self.session_channels.items():
if ch.name == target_name:
self.session_channels[conversation_id] = ch
self.session_names[conversation_id] = project_name
logger.info(f"Reusing channel #{ch.name} for {conversation_id[:8]}")
logger.info(f"Reusing mapped channel #{ch.name} for {conversation_id[:8]}")
return ch
# Check Discord API — maybe channel exists but isn't in our dict
if self.session_category:
for ch in self.session_category.text_channels:
if ch.name == target_name:
self.session_channels[conversation_id] = ch
self.session_names[conversation_id] = project_name
logger.info(f"Found existing Discord channel #{ch.name} for {conversation_id[:8]}")
return ch
# Create new channel (truly no match anywhere)
try:
channel = await self.guild.create_text_channel(
@@ -274,6 +285,8 @@ class GravityBot(commands.Bot):
async def _process_events(self):
"""Main event loop — ALL events go through here sequentially."""
await self.wait_until_ready()
await self._ready_event.wait() # Wait until on_ready + reconnect completes
logger.info("Event processor started (ready gate passed)")
while not self.is_closed():
try: