bhcli

A TUI for chatting on LE PHP Chats
git clone https://git.dasho.dev/bhcli.git
Log | Files | Refs | README

bot_integration.rs (8658B)


      1 use crate::enhanced_bot_system::{EnhancedBotSystem, EnhancedBotConfig, BotChannel, EnhancedBotResponse};
      2 use crate::bot_system::{BotSystem, BotCommand, BotResponse, MessageType};
      3 use crate::chatops::{ChatOpsRouter, UserRole};
      4 use crate::ai_service::AIService;
      5 use crate::{PostType, Users};
      6 use anyhow::Result;
      7 use crossbeam_channel::Sender;
      8 use log::{error, info, warn};
      9 use std::sync::{Arc, Mutex};
     10 
     11 /// Integration layer between existing bot system and enhanced bot system
     12 pub struct BotIntegration {
     13    pub enhanced_bot: Option<Arc<Mutex<EnhancedBotSystem>>>,
     14    pub legacy_bot: Arc<BotSystem>,
     15    pub migration_mode: bool,
     16 }
     17 
     18 impl BotIntegration {
     19    pub fn new(
     20        legacy_bot: Arc<BotSystem>,
     21        enhanced_config: Option<EnhancedBotConfig>,
     22        tx: Sender<PostType>,
     23        ai_service: Option<Arc<AIService>>,
     24        chatops_router: Option<Arc<ChatOpsRouter>>,
     25    ) -> Result<Self> {
     26        let enhanced_bot = if let Some(config) = enhanced_config {
     27            let enhanced = EnhancedBotSystem::new(config, tx, ai_service, chatops_router)?;
     28            Some(Arc::new(Mutex::new(enhanced)))
     29        } else {
     30            None
     31        };
     32        
     33        Ok(Self {
     34            enhanced_bot,
     35            legacy_bot,
     36            migration_mode: enhanced_bot.is_some(),
     37        })
     38    }
     39    
     40    /// Process message with proper channel detection
     41    pub fn process_message_enhanced(
     42        &self,
     43        username: &str,
     44        content: &str,
     45        message_type: MessageType,
     46        message_id: Option<u64>,
     47        channel_source: BotChannel, // Fixed: Pass actual channel source, not content-based detection
     48        is_member: bool,
     49        users: &Users,
     50    ) -> Result<()> {
     51        if self.migration_mode {
     52            if let Some(ref enhanced) = self.enhanced_bot {
     53                let bot = enhanced.lock().unwrap();
     54                return bot.process_message(
     55                    username,
     56                    content,
     57                    message_type,
     58                    message_id,
     59                    channel_source,
     60                    is_member,
     61                    users,
     62                );
     63            }
     64        }
     65        
     66        // Fallback to legacy bot with improved channel context
     67        let channel_context = match channel_source {
     68            BotChannel::Members => Some("members"),
     69            BotChannel::Staff => Some("staff"),
     70            BotChannel::Admin => Some("admin"),
     71            _ => None,
     72        };
     73        
     74        self.legacy_bot.process_message(
     75            username,
     76            content,
     77            message_type,
     78            message_id,
     79            channel_context,
     80            is_member,
     81        )?;
     82        
     83        Ok(())
     84    }
     85 }
     86 
     87 /// Enhanced channel detection logic
     88 pub fn detect_message_channel(
     89    message_content: &str,
     90    message_context: &MessageContext,
     91    members_tag: &str,
     92    staffs_tag: &str,
     93 ) -> BotChannel {
     94    // This is the key fix: detect channel based on MESSAGE CONTEXT, not content scanning
     95    match message_context {
     96        MessageContext::MembersChannel => BotChannel::Members,
     97        MessageContext::StaffChannel => BotChannel::Staff,
     98        MessageContext::AdminChannel => BotChannel::Admin,
     99        MessageContext::PrivateMessage { to: _ } => BotChannel::Public, // PM context
    100        MessageContext::PublicChannel => BotChannel::Public,
    101        MessageContext::Unknown => {
    102            // Fallback: only use content detection as last resort
    103            if message_content.starts_with(members_tag) {
    104                BotChannel::Members
    105            } else if message_content.starts_with(staffs_tag) {
    106                BotChannel::Staff
    107            } else {
    108                BotChannel::Public
    109            }
    110        }
    111    }
    112 }
    113 
    114 /// Message context that should be determined by the message parser
    115 #[derive(Debug, Clone)]
    116 pub enum MessageContext {
    117    PublicChannel,
    118    MembersChannel,
    119    StaffChannel,
    120    AdminChannel,
    121    PrivateMessage { to: String },
    122    Unknown,
    123 }
    124 
    125 /// Fixed integration with main.rs message processing
    126 pub fn process_bot_message_fixed(
    127    bot_integration: &BotIntegration,
    128    username: &str,
    129    content: &str,
    130    message_id: Option<u64>,
    131    users: &Users,
    132    message_context: MessageContext, // This should come from proper message parsing
    133    members_tag: &str,
    134    staffs_tag: &str,
    135 ) -> Result<()> {
    136    let channel_source = detect_message_channel(content, &message_context, members_tag, staffs_tag);
    137    let is_member = users.members.iter().any(|(_, name)| name == username);
    138    
    139    bot_integration.process_message_enhanced(
    140        username,
    141        content,
    142        MessageType::Normal,
    143        message_id,
    144        channel_source,
    145        is_member,
    146        users,
    147    )?;
    148    
    149    Ok(())
    150 }
    151 
    152 /// Utility function to determine message context from le-chat-php message structure
    153 /// This should be called from the message parsing logic in main.rs
    154 pub fn parse_message_context(
    155    message_html: &str,
    156    to_field: &Option<String>,
    157    from_user: &str,
    158    members_tag: &str,
    159    staffs_tag: &str,
    160 ) -> MessageContext {
    161    // If it's a private message
    162    if let Some(to) = to_field {
    163        return MessageContext::PrivateMessage { to: to.clone() };
    164    }
    165    
    166    // Parse the message structure to determine actual channel
    167    // In le-chat-php, channel context should be determined by:
    168    // 1. The sendto parameter used when the message was sent
    169    // 2. The message structure/formatting
    170    // 3. NOT by scanning the content for tags
    171    
    172    // This is where we need to examine the actual le-chat-php message format
    173    // to properly detect which channel a message came from
    174    
    175    // For now, provide basic detection until we can examine the message format
    176    if message_html.contains(&format!("sendto={}", "s ?")) {
    177        MessageContext::MembersChannel
    178    } else if message_html.contains(&format!("sendto={}", "s %")) {
    179        MessageContext::StaffChannel
    180    } else if message_html.contains(&format!("sendto={}", "s _")) {
    181        MessageContext::AdminChannel
    182    } else {
    183        // Default to public if we can't determine
    184        MessageContext::PublicChannel
    185    }
    186 }
    187 
    188 /// Enhanced command processing that integrates with ChatOps
    189 pub fn process_enhanced_command(
    190    bot_integration: &BotIntegration,
    191    command: &str,
    192    args: &[String],
    193    username: &str,
    194    channel: BotChannel,
    195    is_admin: bool,
    196    chatops_router: Option<&ChatOpsRouter>,
    197 ) -> Result<Option<EnhancedBotResponse>> {
    198    // First try ChatOps integration for developer commands
    199    if let Some(chatops) = chatops_router {
    200        let user_role = if is_admin {
    201            UserRole::Admin
    202        } else {
    203            UserRole::Member
    204        };
    205        
    206        // Try to process as ChatOps command first
    207        if let Some(chatops_result) = chatops.process_command(
    208            &format!("/{} {}", command, args.join(" ")),
    209            username,
    210            user_role,
    211        ) {
    212            // Convert ChatOps result to bot response
    213            let messages = chatops_result.to_messages();
    214            if !messages.is_empty() {
    215                let response = EnhancedBotResponse::ChannelMessage {
    216                    content: messages.join("\n"),
    217                    channel: BotChannel::Current, // Respond in same channel
    218                };
    219                return Ok(Some(response));
    220            }
    221        }
    222    }
    223    
    224    // Then try enhanced bot commands
    225    if bot_integration.migration_mode {
    226        if let Some(ref enhanced) = bot_integration.enhanced_bot {
    227            // Enhanced bot command processing would go here
    228            // This integrates with the moderation and automation features
    229        }
    230    }
    231    
    232    Ok(None)
    233 }
    234 
    235 #[cfg(test)]
    236 mod tests {
    237    use super::*;
    238    
    239    #[test]
    240    fn test_channel_detection() {
    241        let context = MessageContext::MembersChannel;
    242        let channel = detect_message_channel("test message", &context, "[M]", "[S]");
    243        assert!(matches!(channel, BotChannel::Members));
    244        
    245        let context = MessageContext::PublicChannel;
    246        let channel = detect_message_channel("[M] test message", &context, "[M]", "[S]");
    247        // Should NOT detect as members channel just because content has [M] tag
    248        assert!(matches!(channel, BotChannel::Public));
    249    }
    250    
    251    #[test]
    252    fn test_message_context_parsing() {
    253        let context = parse_message_context("", &None, "user", "[M]", "[S]");
    254        assert!(matches!(context, MessageContext::PublicChannel));
    255        
    256        let context = parse_message_context("", &Some("target".to_string()), "user", "[M]", "[S]");
    257        assert!(matches!(context, MessageContext::PrivateMessage { .. }));
    258    }
    259 }