# -*- coding: utf-8
Tu Bot - Discord服务器管理机器人
================================

这个bot实现了通过Discord聊天界面来控制服务器的功能。

主要特点：
1. 使用Claude Code CLI作为AI后端来解释和执行命令2. 支持自然语言和直接shell命令
3 具有安全限制和会话管理
4ord slash commands提供用户界面

架构说明：
- Discord Bot (前端界面) -> TerminalHandler (命令处理) -> Claude Code CLI (AI解释) -> Shell执行
- 或者直接执行shell命令（绕过AI解释）

工作原理：
1. 用户通过Discord发送命令（如"/terminal ls"或/terminal 查看当前目录"）
2ot接收命令并传递给TerminalHandler
3 TerminalHandler检查命令安全性，然后：
   - 如果是直接shell命令，直接执行
   - 如果是自然语言，使用Claude Code CLI解释后执行4. 执行结果返回给Discord用户
"

import discord
from discord.ext import commands
import asyncio
import logging
import os
import sys
from pathlib import Path

# 添加项目根目录到Python路径，以便导入其他模块
sys.path.append(str(Path(__file__).parent.parent))

# 导入自定义模块
from slash_commands.terminal.terminal_handler import TerminalHandler
from utils.token_reader import TokenReader

# 设置日志记录器
logger = logging.getLogger(__name__)

class TuBot:
   
    Tu Bot - 处理所有slash commands的主控制器
    
    这个类是整个Discord Bot的核心，负责：
   1. 初始化Discord Bot连接
    2. 设置事件处理器（连接、错误处理等）
    3. 注册slash commands
  4协调各个处理器模块
     
    def __init__(self, token_path: str):
       
        初始化Tu Bot
        
        Args:
            token_path (str): Discord Bot token文件路径
              self.token_path = token_path
        # 初始化token读取器，用于安全地读取Discord Bot token
        self.token_reader = TokenReader(token_path)
        
        # 设置Discord Bot的权限（intents）
        # 这些权限决定了bot能访问哪些Discord功能
        intents = discord.Intents.default()
        intents.message_content =true# 允许读取消息内容
        intents.guild_messages = true许访问服务器消息
        
        # 创建Discord Bot实例
        # command_prefix='/' 表示使用slash commands（实际上slash commands不需要prefix）
        # help_command=None 禁用默认的帮助命令，使用自定义的
        self.bot = commands.Bot(
            command_prefix='/,           intents=intents,
            help_command=None
        )
        
        # 初始化终端命令处理器
        # 这个处理器负责将Discord命令转换为实际的服务器操作
        self.terminal_handler = TerminalHandler()
        
        # 设置事件处理器和命令处理器
        self.setup_events()
        self.setup_commands()
        
    def setup_events(self):
        
        设置Discord Bot的事件处理器
        
        事件处理器用于响应Discord的各种事件，如：
        - bot连接成功
        - 命令执行错误
        - 消息接收等
           
        @self.bot.event
        async def on_ready():
         
            Bot连接成功时触发的事件
            
            这个事件在bot成功连接到Discord服务器时触发
            用于：
    1录连接状态
    2. 同步slash commands到Discord
     3他必要的设置
                     logger.info(f{self.bot.user} 已连接到Discord!')
            # 修复linter错误：添加空值检查
            if self.bot.user:
                logger.info(fBotID: {self.bot.user.id}')
            
            # 同步slash commands到Discord
            # 这会将我们定义的slash commands注册到Discord，使其在客户端可见
            try:
                synced = await self.bot.tree.sync()
                logger.info(f同步了 {len(synced)} 个slash commands")
            except Exception as e:
                logger.error(f"同步slash commands失败: {e}")
        
        @self.bot.event
        async def on_command_error(ctx, error):
                        命令执行错误时的事件处理器
            
            当任何命令执行出错时，这个处理器会被调用
            用于：
    1录错误日志
       2的错误消息
          3. 处理不同类型的错误（如命令不存在、权限不足等）
                     logger.error(f"命令错误: {error}")
            if isinstance(error, commands.CommandNotFound):
                await ctx.send(未知命令，请使用 /help 查看可用命令")
            else:
                await ctx.send(f"执行命令时出错: {str(error)}")
    
    def setup_commands(self):
      设置slash commands
        
        Slash commands是Discord的现代命令系统，提供：
        1. 自动补全
        2. 参数提示
3 更好的用户体验
        
        这里定义了三个主要命令：
        - /terminal: AI控制的终端系统
        - /claude: 直接与Claude AI对话
        - /help: 显示帮助信息
           
        @self.bot.tree.command(name="terminal, description="AI控制终端系统")
        async def terminal_command(interaction: discord.Interaction, command: str):
                   处理/terminal命令
            
            这是核心功能命令，允许用户通过Discord控制服务器
            
            工作流程：
            1 接收用户输入的命令（可以是自然语言或shell命令）
       2 调用TerminalHandler进行处理
            3. TerminalHandler会：
               - 检查命令安全性
               - 判断是直接执行还是需要AI解释
               - 执行命令并返回结果
            4. 将结果分段发送回Discord（处理2000字符限制）
            
            Args:
                interaction (discord.Interaction): Discord交互对象
                command (str): 用户输入的命令
         
            try:
                # 延迟响应，显示"正在思考"状态
                # 这很重要，因为命令执行可能需要时间
                await interaction.response.defer(thinking=True)
                
                # 调用terminal处理器处理命令
                # 修复linter错误：将user.id转换为字符串
                response = await self.terminal_handler.handle_command(command, str(interaction.user.id))
                
                # 分段发送响应（Discord有2000字符限制）
                await self.send_long_response(interaction, response)
                
            except Exception as e:
                logger.error(f"Terminal命令错误: {e})             await interaction.followup.send(f执行命令时出错: {str(e)}")
        
        @self.bot.tree.command(name="claude, description="直接与Claude Code CLI对话")
        async def claude_command(interaction: discord.Interaction, question: str):
            
            处理/claude命令
            
            这个命令允许用户直接与Claude AI进行对话
            不涉及服务器操作，纯粹是AI问答功能
            
            Args:
                interaction (discord.Interaction): Discord交互对象
                question (str): 用户的问题
         
            try:
                await interaction.response.defer(thinking=True)
                
                # 动态导入Claude Code CLI客户端
                # 这样可以避免循环导入问题
                from utils.claude_code_client import ClaudeCodeClient
                claude_code_client = ClaudeCodeClient()
                
                # 使用Claude Code CLI进行对话
                response = await claude_code_client.ask_claude(question)
                await self.send_long_response(interaction, response)
                    
            except Exception as e:
                logger.error(f"Claude命令错误: {e})             await interaction.followup.send(f"执行Claude命令时出错: {str(e)}")
        
        @self.bot.tree.command(name="help, description="显示帮助信息")
        async def help_command(interaction: discord.Interaction):
                       处理/help命令
            
            显示bot的使用说明和命令列表
            帮助用户了解如何使用这个bot
                  help_text = **Discord Bot 命令帮助**

🤖 **核心命令:**
`/terminal <command>` - AI控制终端系统
`/claude <question>` - 直接与Claude AI对话
`/help` - 显示此帮助信息

💡 **使用示例:**
`/terminal ls` - 列出当前目录文件
`/terminal docker ps` - 查看运行的容器
`/terminal 启动nginx容器` - 自然语言命令
`/claude 如何优化Docker容器性能？` - Claude Code CLI问答

⚠️ **注意事项:**
- Terminal命令仅限于~/docker/*目录，使用Claude Code CLI解释
- 不支持危险操作如rm -rf
- Claude命令直接使用Claude Code CLI进行对话
- 需要先安装Claude Code CLI: https://docs.anthropic.com/en/docs/claude-code
         
            await interaction.response.send_message(help_text)
    
    async def send_long_response(self, interaction: discord.Interaction, response: str):
        分段发送长响应
        
        Discord有200字符的消息长度限制，这个方法将长响应分段发送
        
        算法说明：
        1. 如果响应长度不超过2000字符，直接发送
     2超过，按行分割响应
        3 将行组合成不超过2000符的片段
 4依次发送每个片段
        
        Args:
            interaction (discord.Interaction): Discord交互对象
            response (str): 要发送的响应内容
             max_length =200rd消息最大长度
        
        # 如果响应长度在限制内，直接发送
        if len(response) <= max_length:
            await interaction.followup.send(response)
            return
        
        # 分段处理长响应
        parts =]           # 存储所有片段
        current_part =前正在构建的片段
        
        # 按行分割响应，逐行处理
        for line in response.split(undefinedn):
            # 检查添加这一行是否会超过长度限制
            if len(current_part) + len(line) + 1 > max_length:
                # 如果会超过，保存当前片段并开始新片段
                if current_part:
                    parts.append(current_part)
                current_part = line
            else:
                # 如果不会超过，添加到当前片段
                if current_part:
                    current_part += '\n' + line
                else:
                    current_part = line
        
        # 添加最后一个片段
        if current_part:
            parts.append(current_part)
        
        # 发送所有片段
        if parts:
            # 发送第一部分
            await interaction.followup.send(parts0)
            # 发送剩余部分
            for part in parts[1:]:
                await interaction.followup.send(part)
    
    async def start(self):
          启动bot
        
        这个方法负责：
      1取Discord Bot token
        2. 启动Discord Bot连接
   3启动过程中的错误
        
        启动流程：
       1 从token文件读取Discord Bot token
    2证token有效性
       3iscord服务器
   4 开始监听事件和命令
         try:
            # 从token文件读取Discord Bot token
            token = self.token_reader.get_token('tu_discord_token')
            if not token:
                raise ValueError("无法读取tu_discord_token")
            
            logger.info("启动Tu Bot...")
            # 启动Discord Bot连接
            # 这是一个阻塞调用，bot会一直运行直到被停止
            await self.bot.start(token)
            
        except Exception as e:
            logger.error(f"启动Bot失败: {e}")
            raise