Untitled

 avatar
unknown
c_cpp
2 months ago
13 kB
8
Indexable
import Stdio;
import Thread;

class BotConfig {
    string host;
    int port;
    string nick;
    string realname;
    array(string) channels;
    string sasl_user;
    string sasl_pass;

    void create() {
        write("BotConfig: Initializing\n");
        host = "irc.deft.com"; // Switch to "irc.libera.chat" if needed
        port = 6667;
        nick = "PikeBot" + random(1000);
        realname = "Pike IRC Bot";
        channels = ({"#pike-test"}); // Use "#test" for irc.libera.chat
        sasl_user = "YourNickServUsername"; // Replace if needed
        sasl_pass = "YourNickServPassword"; // Replace if needed
        write("BotConfig: Initialized successfully\n");
    }
}

class IRCBot {
    BotConfig config;
    Stdio.File socket;
    Thread worker_thread;
    Queue message_queue;
    Mutex queue_mutex;
    int connected; // 0 = not connected, 1 = connected, -1 = shutting down
    string buffer;

    void create(BotConfig _config) {
        write("IRCBot: Starting create\n");
        if (!_config) {
            write("Error: No config provided\n");
            return;
        }
        config = _config;
        connected = 0;
        message_queue = Queue();
        queue_mutex = Mutex();
        socket = 0;
        worker_thread = 0;
        buffer = "";
        write("IRCBot: Config assigned\n");
        connect();
        write("IRCBot: Create completed\n");
    }

    protected void connect() {
        write("IRCBot: Starting connect\n");
        socket = Stdio.File();
        if (!socket) {
            write("Error: Failed to create socket\n");
            return;
        }
        write("IRCBot: Attempting connection to %s:%d\n", config->host, config->port);
        mixed err = catch {
            if (!socket->connect(config->host, config->port)) {
                write("Error: Failed to connect to IRC server, errno: %d\n", socket->errno());
                socket->close();
                socket = 0;
                return;
            }
        };
        if (err) {
            write("Error: Connection attempt failed: %O\n", err);
            socket->close();
            socket = 0;
            return;
        }
        write("IRCBot: Socket connection succeeded\n");
        socket->set_nonblocking(read_callback, write_callback, close_callback);
        socket->set_keepalive(1);
        write("IRCBot: Socket connected, sending initial commands\n");
        err = catch {
            socket->write("NICK %s\r\n", config->nick);
            socket->write("USER %s 0 * :%s\r\n", config->nick, config->realname);
            socket->write("MODE %s +i\r\n", config->nick);
        };
        if (err) {
            write("Error: Failed to send initial commands: %O\n", err);
            cleanup();
            return;
        }
        write("IRCBot: Initial commands sent\n");
        write("IRCBot: Waiting for connection completion\n");
        int timeout = 60;
        while (timeout > 0 && connected == 0) {
            Pike.DefaultBackend(0.1);
            timeout--;
        }
        if (connected != 1) {
            write("Error: Connection timeout waiting for 001\n");
            cleanup();
            return;
        }
        write("IRCBot: Joining channels\n");
        err = catch {
            foreach(config->channels; ; string channel) {
                socket->write("JOIN %s\r\n", channel);
                write("IRCBot: Sent JOIN for %s\n", channel);
            }
            sleep(2);
            socket->write("WHO #pike-test\r\n");
            write("IRCBot: Sent WHO #pike-test to confirm join\n");
            socket->write("NAMES #pike-test\r\n");
            write("IRCBot: Sent NAMES #pike-test to refresh state\n");
            socket->write("PRIVMSG #pike-test :Bot joined successfully!\r\n");
            write("IRCBot: Sent test message to #pike-test\n");
            socket->write("PRIVMSG %s :Self-test message\r\n", config->nick);
            write("IRCBot: Sent self-test PRIVMSG\n");
        };
        if (err) {
            write("Error: Failed to join channels or send test: %O\n", err);
            cleanup();
            return;
        }
        write("IRCBot: Connect fully completed\n");
    }

    void start_threads() {
        worker_thread = Thread(process_messages);
        write("IRCBot: Worker thread started\n");
    }

    protected void cleanup() {
        if (socket && socket->is_open()) {
            socket->write("QUIT :Bot shutting down\r\n");
            sleep(1);
            socket->close();
            socket = 0;
            write("Main: Socket closed cleanly\n");
        }
        connected = -1;
    }

    protected void read_callback(mixed id, string data) {
        write("Main: read_callback triggered with data length: %d\n", sizeof(data));
        if (!socket || !socket->is_open()) {
            write("Main: Socket is closed or invalid in read_callback\n");
            connected = -1;
            return;
        }
        write("Main: Socket state - is_open: %d, errno: %d\n", socket->is_open(), socket->errno());
        if (data && data != "") {
            buffer += data;
            write("Main: Read raw buffer: %O\n", data);
            array(string) lines = buffer / "\n";
            MutexKey lock = queue_mutex->lock();
            if (lock) {
                for (int i = 0; i < sizeof(lines) - 1; i++) {
                    string line = lines[i];
                    write("Main: Processing line: %s\n", line);
                    if (line && line != "" && line[-1] == '\r') {
                        line = line[..sizeof(line)-2];
                        if (connected == 0 && has_value(line, "001")) {
                            write("IRCBot: Connection completed (001 received) in read_callback\n");
                            connected = 1;
                        }
                        message_queue->write(line);
                        write("Main: Queued message: %s\n", line);
                    } else {
                        write("Main: Incomplete line skipped: %s\n", line);
                    }
                }
                buffer = lines[-1];
                write("Main: Remaining buffer: %s\n", buffer);
                destruct(lock);
            } else {
                write("Main: Failed to acquire queue lock\n");
            }
        } else {
            write("Main: Read empty buffer\n");
        }
    }

    protected void write_callback(mixed id) {
        write("Main: Write callback triggered\n");
    }

    protected void close_callback(mixed id) {
        write("Main: Socket closed by server\n");
        connected = -1;
        cleanup();
    }

    protected void process_messages() {
        write("WorkerThread: Worker thread started\n");
        int heartbeat = 0;
        while (connected >= 0) {
            heartbeat++;
            write("WorkerThread: Loop iteration, connected=%d, queue_size=%d\n", connected, message_queue->size());
            if (heartbeat % 10 == 0) {
                write("WorkerThread: Heartbeat, connected=%d\n", connected);
                if (socket && socket->is_open()) {
                    socket->write("PRIVMSG #pike-test :Heartbeat check %d\r\n", heartbeat / 10);
                    write("WorkerThread: Sent heartbeat message\n");
                    socket->write("PING :irc.deft.com\r\n");
                    write("WorkerThread: Sent PING to keep connection alive\n");
                } else {
                    write("WorkerThread: Socket invalid during heartbeat\n");
                    connected = -1;
                    break;
                }
            }
            // Manual socket read to bypass read_callback issues
            if (socket && socket->is_open()) {
                string data = socket->read(1024, 1); // Non-blocking read
                if (data && data != "") {
                    write("WorkerThread: Manual read data: %O\n", data);
                    buffer += data;
                    array(string) lines = buffer / "\n";
                    MutexKey lock = queue_mutex->lock();
                    if (lock) {
                        for (int i = 0; i < sizeof(lines) - 1; i++) {
                            string line = lines[i];
                            if (line && line != "" && line[-1] == '\r') {
                                line = line[..sizeof(line)-2];
                                message_queue->write(line);
                                write("WorkerThread: Queued manual message: %s\n", line);
                            }
                        }
                        buffer = lines[-1];
                        destruct(lock);
                    }
                }
            }
            mixed msg;
            MutexKey lock = queue_mutex->lock();
            if (lock) {
                msg = message_queue->read();
                destruct(lock);
            } else {
                write("WorkerThread: Failed to acquire queue lock\n");
                sleep(1);
                continue;
            }
            if (msg == -1) {
                write("WorkerThread: Worker thread shutting down\n");
                break;
            }
            if (!msg) {
                sleep(0.1);
                continue;
            }
            string line = msg;
            write("WorkerThread: Processing RAW: %s\n", line);
            array(string) parts = line / " ";
            if (sizeof(parts) < 2) {
                write("WorkerThread: Skipping malformed line\n");
                continue;
            }
            string prefix = parts[0];
            string cmd = parts[1];
            write("WorkerThread: Command received: %s\n", cmd);
            if (cmd == "PING") {
                socket->write("PONG %s\r\n", parts[2..] * " ");
                write("WorkerThread: Responded to PING\n");
            } else if (cmd == "PONG") {
                write("WorkerThread: Received PONG from server\n");
            } else if (cmd == "PRIVMSG" && sizeof(parts) >= 4) {
                string target = parts[2];
                string message = parts[3..] * " ";
                if (message[0] == ':') message = message[1..];
                if (prefix[0] == ':') prefix = prefix[1..];
                string nick = (prefix / "!")[0];
                write(sprintf("WorkerThread: Received PRIVMSG from <%s> to %s: %s\n", nick, target, message));
                if (target == config->nick) {
                    write("WorkerThread: Direct message received: %s\n", message);
                    if (has_value(message, "VERSION")) {
                        socket->write("NOTICE %s :\001VERSION PikeBot 1.0\001\r\n", nick);
                        write("WorkerThread: Sent VERSION response to %s\n", nick);
                    }
                } else if (target == "#pike-test") {
                    if (has_prefix(message, "!hello")) {
                        mixed err = catch {
                            socket->write("PRIVMSG %s :Hello %s!\r\n", target, nick);
                            write("WorkerThread: Sent hello response to %s\n", nick);
                        };
                        if (err) {
                            write("WorkerThread: Error sending hello response: %O\n", err);
                        }
                    }
                    if (has_prefix(message, "!quit")) {
                        write("WorkerThread: Received quit command, shutting down\n");
                        socket->write("QUIT :Bot shutting down\r\n");
                        connected = -1;
                        break;
                    }
                }
            } else if (cmd == "353" || cmd == "366") {
                write("WorkerThread: Channel join confirmation: %s\n", line);
            } else if (cmd == "352" || cmd == "315") {
                write("WorkerThread: WHO response: %s\n", line);
            } else if (cmd == "JOIN" && prefix[0] == ':') {
                string nick = (prefix / "!")[0];
                write("WorkerThread: %s joined %s\n", nick, parts[2]);
            }
        }
        write("WorkerThread: Worker thread exited\n");
    }

    protected void _destruct() {
        if (connected > 0 && socket && socket->is_open()) {
            socket->write("QUIT :Bot shutting down\r\n");
            write("Main: Sent QUIT on destruct\n");
        }
        if (worker_thread) {
            MutexKey lock = queue_mutex->lock();
            if (lock) {
                message_queue->write(-1);
                destruct(lock);
            }
        }
        cleanup();
    }
}

int main() {
    write("Main: Starting\n");
    BotConfig config = BotConfig();
    if (!config) {
        write("Error: Failed to create config\n");
        return 1;
    }
    write("Main: Config created\n");
    IRCBot bot = IRCBot(config);
    if (!bot) {
        write("Error: Failed to create bot\n");
        return 1;
    }
    write("Main: Bot created\n");
    bot->start_threads();
    while (bot->connected >= 0) {
        Pike.DefaultBackend(0.1);
        write("Main: Running, connected=%d\n", bot->connected);
    }
    write("Main: Bot shutting down\n");
    bot->_destruct();
    return 0;
}
Editor is loading...
Leave a Comment