1515from __future__ import annotations
1616
1717import io
18+ import logging
1819import os
1920
2021import discord
2526from .commands import CommandHandlers
2627from .session_router import InteractionContext , to_identity
2728
29+ logger = logging .getLogger (__name__ )
30+
2831TOKEN_ENV = "DISCORD_BOT_TOKEN"
2932
3033
@@ -102,8 +105,11 @@ def __init__(self, handlers: CommandHandlers) -> None:
102105 self ._register_commands ()
103106
104107 async def setup_hook (self ) -> None :
105- # Sync slash commands with Discord on startup.
106- await self .tree .sync ()
108+ # Sync only when LANG2SQL_SYNC_COMMANDS=true (e.g. after adding/removing commands).
109+ # Skipping sync on every restart avoids Discord rate limits during dev.
110+ if os .environ .get ("LANG2SQL_SYNC_COMMANDS" , "" ).lower () == "true" :
111+ await self .tree .sync ()
112+ logger .info ("slash commands synced" )
107113
108114 def _register_commands (self ) -> None :
109115 tree = self .tree
@@ -135,6 +141,13 @@ async def define_metric(
135141 async def remember (interaction : discord .Interaction , text : str ) -> None :
136142 await self ._run (interaction , handlers .remember (to_identity (_interaction_context (interaction )), text ))
137143
144+ @tree .command (name = "enrich" , description = "LLM으로 DB 컬럼 메타데이터 자동 보강 (clear=True로 초기화)" )
145+ async def enrich (interaction : discord .Interaction , table : str = "" , clear : bool = False ) -> None :
146+ await self ._run (
147+ interaction ,
148+ handlers .enrich (to_identity (_interaction_context (interaction )), table = table , clear = clear ),
149+ )
150+
138151 @tree .command (name = "semantic_show" , description = "Show definitions in effect here" )
139152 async def semantic_show (interaction : discord .Interaction ) -> None :
140153 await self ._run (interaction , handlers .semantic_show (to_identity (_interaction_context (interaction ))))
@@ -148,7 +161,10 @@ async def _run(self, interaction: discord.Interaction, coro) -> None:
148161 await interaction .response .defer (thinking = True )
149162 message = await coro
150163 content , file = _to_sendable (message )
151- await interaction .followup .send (content = content or "(empty)" , file = file )
164+ kwargs : dict = {"content" : content or "(empty)" }
165+ if file is not None :
166+ kwargs ["file" ] = file
167+ await interaction .followup .send (** kwargs )
152168
153169 async def on_message (self , message : discord .Message ) -> None :
154170 """Treat an @mention (or a reply inside a thread) as a free-form query."""
@@ -166,9 +182,16 @@ async def on_message(self, message: discord.Message) -> None:
166182 return
167183
168184 identity = to_identity (_message_context (message ))
169- out = await self ._handlers .query (identity , text )
170- content , file = _to_sendable (out )
171- await message .channel .send (content = content or "(empty)" , file = file )
185+ try :
186+ out = await self ._handlers .query (identity , text )
187+ content , file = _to_sendable (out )
188+ if content and len (content ) > 1900 :
189+ content = content [:1900 ] + "\n …(truncated)"
190+ await message .channel .send (content = content or "(empty)" , file = file )
191+ except Exception as exc :
192+ import traceback
193+ traceback .print_exc ()
194+ await message .channel .send (content = f"❌ Error: { type (exc ).__name__ } : { exc } " )
172195
173196
174197def run () -> None :
@@ -182,6 +205,7 @@ def run() -> None:
182205 raise RuntimeError (
183206 f"{ TOKEN_ENV } is not set; export your Discord bot token to run the bot."
184207 )
185- handlers = CommandHandlers (ContextConcierge ())
208+ data_path = os .environ .get ("LANG2SQL_DATA_PATH" , "lang2sql_data.db" )
209+ handlers = CommandHandlers (ContextConcierge (path = data_path ))
186210 client = Lang2SQLBot (handlers )
187211 client .run (token )
0 commit comments