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 }