Untitled
unknown
c_cpp
9 months ago
13 kB
10
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