diff --git a/irc_bot.py b/irc_bot.py index 4ae04a3..54d9d51 100644 --- a/irc_bot.py +++ b/irc_bot.py @@ -3,12 +3,13 @@ import threading import time import re import requests +import ssl from auth_db import auth_db from utils import strip_bbcode, parse_username from config import ( - SERVER, PORT, BOTNICK, API_ENDPOINT, API_TOKEN, VERIFY_ENDPOINT, + SERVER, PORT, BOTNICK, NICKSERV_USER, NICKSERV_PASS, API_ENDPOINT, API_TOKEN, VERIFY_ENDPOINT, LOBBY_CHANNEL, MAIN_CHANNEL, ADMIN_CHANNEL, STAFF_CHANNELS, - RECONNECT_DELAY, GROUP_NAMES, STAFF_GROUP_IDS, ADMIN_GROUP_IDS + RECONNECT_DELAY, GROUP_NAMES, STAFF_GROUP_IDS, ADMIN_GROUP_IDS, OPER_USER, OPER_PASS ) class IRCBot: @@ -23,7 +24,8 @@ class IRCBot: attempt = 0 while True: try: - self.irc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + raw_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.irc = ssl.wrap_socket(raw_sock) self.irc.connect((SERVER, PORT)) self.irc.send(f"NICK {self.nickname}\r\n".encode("utf-8")) self.irc.send(f"USER {self.nickname} 0 * :{self.nickname}\r\n".encode("utf-8")) @@ -34,18 +36,45 @@ class IRCBot: if not resp: raise ConnectionError("No data received.") for line in resp.decode(errors="ignore").split("\r\n"): + if not line: + continue + + print(f"[IRC RAW] {line}") + if line.startswith("PING"): self.irc.send(f"PONG {line.split()[1]}\r\n".encode("utf-8")) + elif " 001 " in line: print(f"[+] Connected as {self.nickname}") + + # Identify with NickServ + self.irc.send(f"PRIVMSG NickServ :IDENTIFY {NICKSERV_USER} {NICKSERV_PASS}\r\n".encode("utf-8")) + time.sleep(2) # give time for identify to settle + + # Send OPER command + self.irc.send(f"OPER {OPER_USER} {OPER_PASS}\r\n".encode("utf-8")) + print("[IRC CMD] Sent OPER") + + # Optionally ghost the previous nick if not using SASL + if self.nickname != BOTNICK: + print("[*] Reclaiming original nickname") + self.irc.send(f"PRIVMSG NickServ :GHOST {BOTNICK} {NICKSERV_PASS}\r\n".encode("utf-8")) + time.sleep(1) + self.irc.send(f"NICK {BOTNICK}\r\n".encode("utf-8")) + self.nickname = BOTNICK + time.sleep(1) + self.irc.send(f"PRIVMSG NickServ :IDENTIFY {NICKSERV_USER} {NICKSERV_PASS}\r\n".encode("utf-8")) + time.sleep(1) + self.irc.send(f"JOIN {LOBBY_CHANNEL}\r\n".encode("utf-8")) return elif " 433 " in line: + # Nickname is already in use + print(f"[!] Nick {self.nickname} already in use, trying alternate...") attempt += 1 - self.nickname += "_" - self.irc.close() - time.sleep(2) - break + self.nickname = f"{BOTNICK}_{attempt}" + self.irc.send(f"NICK {self.nickname}\r\n".encode("utf-8")) + break # Break inner loop to restart connection flow with new nick except Exception as e: print(f"[-] IRC connect error: {e}") time.sleep(RECONNECT_DELAY) @@ -65,8 +94,11 @@ class IRCBot: if not data: raise ConnectionError("Disconnected.") for line in data.strip().split("\r\n"): + print(f"[IRC RAW] {line}") # DEBUG FOR NOW if line.startswith("PING"): self.irc.send(f"PONG {line.split()[1]}\r\n".encode("utf-8")) + elif "You are now an IRC Operator" in line or "oper" in line.lower(): + print("[+] Oper login succeeded") elif "PRIVMSG" in line: self.handle_message(line) elif " PART " in line or " QUIT " in line: @@ -102,9 +134,13 @@ class IRCBot: # Relay public message if user is verified if not is_private: user_data = auth_db.get_verified_user(user) + print(f"[DEBUG] Lookup for user '{user}' returned: {user_data}") if user_data: site_user, irc_key, _ = user_data + print(f"[DEBUG] Sending message from {site_user} with token {irc_key}: {msg}") self.api_call(site_user, msg, irc_key) + else: + print(f"[DEBUG] No verified user found for: {user}") # Handle verification messages from users def verify_user(self, nick, msg): @@ -134,14 +170,18 @@ class IRCBot: group_name = GROUP_NAMES.get(group_id, "Unknown") auth_db.verify_user(nick, site_user, irc_key, group_id) + print(f"[DEBUG] Verification success: {site_user} ({group_name}) - group_id={group_id}") self.send_notice(nick, f"Verified as {site_user} ({group_name})") + time.sleep(1) # Add delay before sending SAJOIN # Join main lobby + print(f"[DEBUG] Sending SAJOIN for {nick} to {MAIN_CHANNEL}") self.send_raw_command(f"SAJOIN {nick} {MAIN_CHANNEL}") # Join staff chats if in staff group if group_id in STAFF_GROUP_IDS: for channel in STAFF_CHANNELS: + print(f"[DEBUG] Sending SAJOIN for {nick} to {channel}") self.send_raw_command(f"SAJOIN {nick} {channel}") # Join admin room if in admin group @@ -181,12 +221,27 @@ class IRCBot: # API call ChatBridgeController - def api_call(self, user, msg, token): + def api_call(self, user, msg, irc_key): try: - requests.post(API_ENDPOINT, headers={ + payload = { + "username": user, + "irc_key": irc_key, + "text": msg, + "source": "irc" + } + headers = { "Authorization": f"Bearer {API_TOKEN}", - "Content-Type": "application/json" - }, json={"username": user, "text": msg, "token": token}) + "Content-Type": "application/json", + "Accept": "application/json" + } + print(f"[DEBUG] Posting to API: {API_ENDPOINT}") + print(f"[DEBUG] Headers: {headers}") + print(f"[DEBUG] Payload: {payload}") + resp = requests.post(API_ENDPOINT, headers=headers, json=payload) + + print(f"[DEBUG] API response status: {resp.status_code}") + print(f"[DEBUG] API response body: {resp.text}") + except Exception as e: print(f"[-] API call failed: {e}")