CCCPaste

dual.py

#!/usr/bin/env python3
# dual.py - Complete Dual Bot dengan Semua Methods
import socket
import time
import threading
import random
from config import SERVER, PORT, CHANNELS
from waktu import TimeAnnouncer
from ctcp import RealPingMonitor
from events import EventHandler
from prompt import irc_prompt, debug_prompt, status_prompt
from api import api_manager
from focus import autofocus

class DualBot:
    def __init__(self):
        self.master_sock = None
        self.client_sock = None
        self.running = True
        self.joined_channels = set()

        # Initialize modules
        self.events = EventHandler()
        self.ping_monitor = RealPingMonitor()
        self.time_announcer = None

        print(debug_prompt("Dual Bot dengan Focus System Initialized"))

    def should_respond_to_message(self, nick, target, message, is_master=True):
        """Simple wrapper untuk focus system"""
        bot_name = "www" if is_master else "deep"

        # Ignore bots
        if nick in ["www", "deep"]:
            return False

        # GUNA AUTO-FOCUS SYSTEM dari focus.py
        return autofocus.should_respond(nick, message, bot_name)

    def send_message_delayed(self, target, message, is_master=True):
        """Delay 1.5-2.5s"""
        bot_name = "www" if is_master else "deep"

        # Random delay
        delay_time = random.uniform(1.5, 2.5)
        print(f"⏳ {bot_name} delay: {delay_time:.1f}s")
        time.sleep(delay_time)

        # Send message
        print(f"🎯 {bot_name} sending: '{message}'")

        if is_master:
            self.events.send_message(target, message, is_master=True)
        else:
            self.events.send_message(target, message, is_master=False)

    def connect_master(self):
        """Connect master bot"""
        try:
            print(f"🔌 Connecting master to {SERVER}:{PORT}...")
            self.master_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.master_sock.settimeout(30)
            self.master_sock.connect((SERVER, PORT))
            self.master_sock.settimeout(None)
            print("✅ Master socket connected!")

            self.master_sock.send(f"NICK www\r\n".encode())
            self.master_sock.send(f"USER www 0 * :Master Bot\r\n".encode())
            print("📤 Master registration sent")

            self.events.master_sock = self.master_sock
            return True

        except Exception as e:
            print(f"❌ Master connection failed: {e}")
            return False

    def connect_client(self):
        """Connect client bot"""
        try:
            print(f"🔌 Connecting client to {SERVER}:{PORT}...")
            self.client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.client_sock.settimeout(30)
            self.client_sock.connect((SERVER, PORT))
            self.client_sock.settimeout(None)
            print("✅ Client socket connected!")

            self.client_sock.send(f"NICK deep\r\n".encode())
            self.client_sock.send(f"USER deep 0 * :AI Assistant\r\n".encode())
            print("📤 Client registration sent")

            self.events.client_sock = self.client_sock
            return True

        except Exception as e:
            print(f"❌ Client connection failed: {e}")
            return False

    def identify_bots(self):
        """Identify both bots to NickServ menggunakan config yang betul"""
        try:
            from config import BOTS

            # Cara yang betul untuk identify
            master_nick = BOTS['www']['nick']
            master_pass = BOTS['www']['password']
            client_nick = BOTS['deep']['nick'] 
            client_pass = BOTS['deep']['password']

            # Format yang betul: "IDENTIFY " BUKAN "IDENTIFY  "
            self.master_sock.send(f"PRIVMSG NickServ :IDENTIFY {master_pass}\r\n".encode())
            time.sleep(1)
            self.client_sock.send(f"PRIVMSG NickServ :IDENTIFY {client_pass}\r\n".encode())
            time.sleep(2)
            print(status_prompt("AUTH", "Identification commands sent"))
        except Exception as e:
            print(debug_prompt(f"Identification failed: {e}"))

    def join_channels(self):
        """Join semua channels"""
        try:
            for channel in CHANNELS:
                if not channel.startswith('#'):
                    channel = '#' + channel

                self.master_sock.send(f"JOIN {channel}\r\n".encode())
                self.client_sock.send(f"JOIN {channel}\r\n".encode())
                self.joined_channels.add(channel)
                time.sleep(0.5)

            print(status_prompt("CHANNEL", f"Joined {len(CHANNELS)} channels"))
        except Exception as e:
            print(debug_prompt(f"Channel join failed: {e}"))

    def rejoin_channel(self, channel):
        """Auto-rejoin channel apabila kena kick"""
        try:
            if channel in self.joined_channels:
                print(debug_prompt(f"Rejoining channel {channel}..."))
                self.master_sock.send(f"JOIN {channel}\r\n".encode())
                self.client_sock.send(f"JOIN {channel}\r\n".encode())
                time.sleep(1)
                print(status_prompt("CHANNEL", f"Rejoined {channel}"))
        except Exception as e:
            print(debug_prompt(f"Rejoin failed: {e}"))

    def _server_ping_timer(self):
        """Send PING to server every 60 seconds"""
        while self.running:
            try:
                time.sleep(60)

                if self.master_sock:
                    self.master_sock.send(b'PING :keepalive_60s\r\n')
                    print("🔁 MASTER SERVER PING SENT (60s)")
                if self.client_sock:
                    self.client_sock.send(b'PING :keepalive_60s\r\n')  
                    print("🔁 CLIENT SERVER PING SENT (60s)")

            except Exception as e:
                print(f"❌ Server ping error: {e}")

    def on_connect_actions(self):
        """Execute actions on connect"""
        try:
            self.master_sock.send(b'PRIVMSG www :\x01PING connect_test\x01\r\n')
            self.client_sock.send(b'PRIVMSG deep :\x01PING connect_test\x01\r\n')
            print("🔁 CTCP self-ping on connect")
        except:
            pass

    def handle_master_message(self, line):
        """Handle incoming messages untuk master bot"""
        print(f"🔍 MASTER IN: {line}")

        # STANDARD IRC SERVER PING RESPONSE
        if line.startswith('PING '):
            ping_data = line.split('PING ')[1]
            pong_msg = f"PONG {ping_data}\r\n"
            self.master_sock.send(pong_msg.encode())
            print(f"🔁 MASTER SERVER PONG: {pong_msg.strip()}")
            return

        # STANDARD IRC PING (format lain)
        if line.startswith(':') and ' PING ' in line:
            ping_data = line.split(' PING ')[1]
            self.ping_monitor.log_master_ping(ping_data)
            pong_msg = f"PONG {ping_data}\r\n"
            self.master_sock.send(pong_msg.encode())
            self.ping_monitor.log_master_pong()
            print(f"🔁 MASTER PONG SENT: {pong_msg.strip()}")
            return

        # CTCP PING HANDLING
        if '\x01PING' in line and 'PRIVMSG' in line:
            print(f"🔁 MASTER CTCP PING: {line}")
            try:
                nick = line.split('!')[0][1:]
                ping_data = line.split('\x01PING ')[1].split('\x01')[0]
                ctcp_reply = f"NOTICE {nick} :\x01PING {ping_data}\x01\r\n"
                self.master_sock.send(ctcp_reply.encode())
                print(f"🔁 MASTER CTCP PONG to {nick}: {ping_data}")
            except Exception as e:
                print(f"❌ MASTER CTCP error: {e}")
            return

        if 'KICK' in line:
            try:
                parts = line.split(' ')
                channel = parts[2]
                kicked_nick = parts[3]
                if kicked_nick in ['www', 'deep']:
                    print(debug_prompt(f"Bot was kicked from {channel}"))
                    self.rejoin_channel(channel)
            except Exception as e:
                print(debug_prompt(f"Kick handling error: {e}"))
            return

        # 🆕 FIXED: PRIVMSG HANDLING - INI PERLU DI LUAR DARI if 'KICK' block!
        if 'PRIVMSG' in line:
            try:
                parts = line.split(' ', 3)
                if len(parts) >= 4:
                    nick = parts[0][1:].split('!')[0]
                    target = parts[2]
                    message = parts[3][1:]

                    print(f"🎯 MASTER processing PRIVMSG from {nick}: {message}")

                    # GUNA AUTO-FOCUS SYSTEM
                    should_respond = self.should_respond_to_message(nick, target, message, is_master=True)

                    if not should_respond:
                        print(f"🛑 MASTER: Skip {nick} (no focus/mention)")
                        return

                    print(f"✅ MASTER: Processing message from {nick}")

                    # Display dengan prompt system
                    prompt = irc_prompt.get_prompt("www", target)
                    print(f"{prompt}{nick}: {message}")

                    # Handle ACTION (/me) messages
                    if message.startswith('\x01ACTION ') and message.endswith('\x01'):
                        action_text = message[8:-1]
                        response = self.events.handle_action(nick, target, action_text, is_master=True)
                        if response:
                            self.send_message_delayed(target, response, is_master=True)
                            irc_prompt.log_interaction("www", nick, message, response)

                    # Handle normal PRIVMSG
                    else:
                        response = self.events.handle_privmsg(nick, target, message, is_master=True)
                        print(f"🔧 DEBUG: Response = {response}")

                        if response:
                            self.send_message_delayed(target, response, is_master=True)
                            irc_prompt.log_interaction("www", nick, message, response)

            except Exception as e:
                print(debug_prompt(f"Master message error: {e}"))

        elif 'NOTICE' in line:
            try:
                parts = line.split(' ', 3)
                if len(parts) >= 4:
                    nick = parts[0][1:].split('!')[0]
                    notice_text = parts[3][1:]

                    prompt = irc_prompt.get_prompt("www", "NOTICE")
                    print(f"{prompt}From {nick}: {notice_text}")

                    response = self.events.handle_notice(nick, notice_text, is_master=True)
                    if response:
                        self.send_message_delayed(nick, response, is_master=True)
            except Exception as e:
                print(debug_prompt(f"Master notice error: {e}"))

    def handle_client_message(self, line):
        """Handle incoming messages untuk client bot"""
        print(f"🔍 CLIENT IN: {line}")

        # STANDARD IRC SERVER PING RESPONSE
        if line.startswith('PING '):
            ping_data = line.split('PING ')[1]
            pong_msg = f"PONG {ping_data}\r\n"
            self.client_sock.send(pong_msg.encode())
            print(f"🔁 CLIENT SERVER PONG: {pong_msg.strip()}")
            return

        # STANDARD IRC PING (format lain)
        if line.startswith(':') and ' PING ' in line:
            ping_data = line.split(' PING ')[1]
            self.ping_monitor.log_client_ping(ping_data)
            pong_msg = f"PONG {ping_data}\r\n"
            self.client_sock.send(pong_msg.encode())
            self.ping_monitor.log_client_pong()
            print(f"🔁 CLIENT PONG SENT: {pong_msg.strip()}")
            return

        # CTCP PING HANDLING
        if '\x01PING' in line and 'PRIVMSG' in line:
            print(f"🔁 CLIENT CTCP PING: {line}")
            try:
                nick = line.split('!')[0][1:]
                ping_data = line.split('\x01PING ')[1].split('\x01')[0]
                ctcp_reply = f"NOTICE {nick} :\x01PING {ping_data}\x01\r\n"
                self.client_sock.send(ctcp_reply.encode())
                print(f"🔁 CLIENT CTCP PONG to {nick}: {ping_data}")
            except Exception as e:
                print(f"❌ CLIENT CTCP error: {e}")
            return

        if 'KICK' in line:
            try:
                parts = line.split(' ')
                channel = parts[2]
                kicked_nick = parts[3]
                if kicked_nick in ['www', 'deep']:
                    print(debug_prompt(f"Bot was kicked from {channel}"))
                    self.rejoin_channel(channel)
            except Exception as e:
                print(debug_prompt(f"Kick handling error: {e}"))
            return

        if 'PRIVMSG' in line:
            try:
                parts = line.split(' ', 3)
                if len(parts) >= 4:
                    nick = parts[0][1:].split('!')[0]
                    target = parts[2]
                    message = parts[3][1:]

                    print(f"🎯 CLIENT processing PRIVMSG from {nick}: {message}")

                    # GUNA AUTO-FOCUS SYSTEM
                    should_respond = self.should_respond_to_message(nick, target, message, is_master=False)

                    if not should_respond:
                        print(f"🛑 CLIENT: Skip {nick} (no focus/mention)")
                        return

                    print(f"✅ CLIENT: Processing message from {nick}")

                    # Display dengan prompt system
                    prompt = irc_prompt.get_prompt("deep", target)
                    print(f"{prompt}{nick}: {message}")

                    # Handle ACTION (/me) messages
                    if message.startswith('\x01ACTION ') and message.endswith('\x01'):
                        action_text = message[8:-1]
                        response = self.events.handle_action(nick, target, action_text, is_master=False)
                        if response:
                            self.send_message_delayed(target, response, is_master=False)
                            irc_prompt.log_interaction("deep", nick, message, response)

                    # Handle normal PRIVMSG
                    else:
                        response = self.events.handle_privmsg(nick, target, message, is_master=False)
                        print(f"🔧 DEBUG: Response = {response}")

                        if response:
                            self.send_message_delayed(target, response, is_master=False)
                            irc_prompt.log_interaction("deep", nick, message, response)

            except Exception as e:
                print(debug_prompt(f"Client message error: {e}"))

        elif 'NOTICE' in line:
            try:
                parts = line.split(' ', 3)
                if len(parts) >= 4:
                    nick = parts[0][1:].split('!')[0]
                    notice_text = parts[3][1:]

                    prompt = irc_prompt.get_prompt("deep", "NOTICE")
                    print(f"{prompt}From {nick}: {notice_text}")

                    response = self.events.handle_notice(nick, notice_text, is_master=False)
                    if response:
                        self.send_message_delayed(nick, response, is_master=False)
            except Exception as e:
                print(debug_prompt(f"Client notice error: {e}"))

    def master_loop(self):
        """Main loop untuk master bot"""
        buffer = ""
        while self.running and self.master_sock:
            try:
                data = self.master_sock.recv(1024).decode('utf-8', errors='ignore')
                if not data:
                    break

                buffer += data
                lines = buffer.split('\r\n')
                buffer = lines.pop()

                for line in lines:
                    line = line.strip()
                    if line:
                        self.handle_master_message(line)

            except Exception as e:
                print(debug_prompt(f"Master loop error: {e}"))
                break

    def client_loop(self):
        """Main loop untuk client bot"""
        buffer = ""
        while self.running and self.client_sock:
            try:
                data = self.client_sock.recv(1024).decode('utf-8', errors='ignore')
                if not data:
                    break

                buffer += data
                lines = buffer.split('\r\n')
                buffer = lines.pop()

                for line in lines:
                    line = line.strip()
                    if line:
                        self.handle_client_message(line)

            except Exception as e:
                print(debug_prompt(f"Client loop error: {e}"))
                break

    def start(self):
        """Start dual bot system"""
        try:
            print(debug_prompt("Starting Dual Bot..."))

            if not self.connect_master():
                print(debug_prompt("Master connection failed"))
                return False

            if not self.connect_client():
                print(debug_prompt("Client connection failed"))
                return False

            self.on_connect_actions()
            time.sleep(3)
            self.identify_bots()
            self.join_channels()

            print(status_prompt("SYSTEM", "Dual Bot System READY!"))

            # Start background modules
            self.time_announcer = TimeAnnouncer(self.master_sock, CHANNELS)
            ping_thread = self.ping_monitor.start()
            time_thread = self.time_announcer.start()

            # SERVER PING TIMER (60s)
            server_ping_thread = threading.Thread(target=self._server_ping_timer, daemon=True)
            server_ping_thread.start()

            # Start bot loops
            master_thread = threading.Thread(target=self.master_loop, daemon=True)
            client_thread = threading.Thread(target=self.client_loop, daemon=True)

            master_thread.start()
            client_thread.start()

            while self.running and master_thread.is_alive() and client_thread.is_alive():
                time.sleep(5)

            return True

        except Exception as e:
            print(debug_prompt(f"Bot startup failed: {e}"))
            return False

    def is_running(self):
        return self.running

    def stop(self):
        self.running = False
        try:
            if self.master_sock:
                self.master_sock.close()
            if self.client_sock:
                self.client_sock.close()
            self.ping_monitor.stop()
            print(status_prompt("SYSTEM", "Dual Bot stopped"))
        except Exception as e:
            print(debug_prompt(f"Stop error: {e}"))

if __name__ == "__main__":
    bot = DualBot()
    bot.start()