#!/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()