chore: make stuff work

This commit is contained in:
Jacob Jonsson 2025-11-30 22:52:22 +01:00
parent 936bf470c7
commit 1e4c90822a
Signed by: Jassob
GPG key ID: 7E30B9B047F7202E
4 changed files with 267 additions and 117 deletions

View file

@ -1,9 +1,13 @@
const std = @import("std");
const zigeru = @import("zigeru");
const zircon = @import("zircon");
const zigeru = @import("zigeru");
const Bot = zigeru.bot.Bot;
const Error = zigeru.bot.Error;
const BotCommand = zigeru.bot.Command;
const AdminCommand = zigeru.bot.AdminCommand;
const BotMessage = zigeru.bot.Message;
var debug_allocator = std.heap.DebugAllocator(.{}).init;
@ -26,30 +30,23 @@ pub fn main() !void {
defer client.deinit();
var bot_adapter = try BotAdapter.init(allocator);
const adapter = bot_adapter.adapter();
defer bot_adapter.deinit();
client.register_message_closure(bot_adapter.closure());
// Connect to the IRC server and perform registration.
try client.connect();
try client.register();
try client.join("#eru-tests");
try client.join("#eru-admin");
// Enter the main loop that keeps reading incoming IRC messages forever.
// The client loop accepts a LoopConfig struct with two optional fields.
//
// These two fields, .msg_callback and .spawn_thread are callback pointers.
// You set them to custom functions you define to customize the main loop.
//
// .msg_callback lets you answer any received IRC messages with another one.
//
// .spawn_thread lets you tweak if you spawn a thread to run .msg_callback.
client.loop(.{ .msg_callback = adapter.callbackFn }) catch |err| {
std.debug.print("eru exited with error: {}", .{err});
client.loop(.{}) catch |err| {
std.debug.print("eru exited with error: {}\n", .{err});
return;
};
}
pub const Adapter = struct {
ptr: ?*anyopaque,
callbackFn: *const fn (?*anyopaque, zircon.Message) ?zircon.Message,
ptr: *anyopaque,
callbackFn: *const fn (*anyopaque, zircon.Message) ?zircon.Message,
};
pub const BotAdapter = struct {
@ -70,47 +67,66 @@ pub const BotAdapter = struct {
pub fn callback(self: *BotAdapter, message: zircon.Message) ?zircon.Message {
switch (message) {
.PRIVMSG => |msg| {
if (Command.parse(msg.prefix, msg.targets, msg.text)) |command| {
return command.handle(&self.bot);
std.log.debug("received message: nick {?s}, user: {?s}, host: {?s}, targets: {s}, text: {s}", .{
msg.prefix.?.nick,
msg.prefix.?.user,
msg.prefix.?.host,
msg.targets,
msg.text,
});
const nick = if (msg.prefix) |prefix| if (prefix.nick) |nick| nick else "unknown" else "unknown";
if (BotCommand.parse(nick, msg.text)) |command| {
return self.bot.execute(&command, msg.prefix, msg.targets) catch |err| {
const err_msg = switch (err) {
Error.NoMessage => "no matching message",
Error.OutOfMemory => "out of memory",
Error.WriteFailed => "write failed",
};
return .{ .PRIVMSG = .{ .prefix = msg.prefix, .targets = "#eru-admin", .text = err_msg } };
};
}
self.bot.hear(zigeru.bot.Message{
.author = msg.prefix.?.nick orelse "unknown",
.timestamp = std.time.timestamp(),
.content = msg.text,
if (AdminCommand.parse(msg.text)) |command| {
return self.bot.execute_admin(&command, msg.prefix, "#eru-admin") catch |err| {
const err_msg = switch (err) {
Error.NoMessage => "no matching message",
Error.OutOfMemory => "out of memory",
Error.WriteFailed => "write failed",
};
return .{ .PRIVMSG = .{ .prefix = msg.prefix, .targets = "#eru-admin", .text = err_msg } };
};
}
self.bot.hear(BotMessage.new_owned(
self.allocator,
std.time.timestamp(),
msg.prefix.?.nick orelse "unknown",
msg.targets,
msg.text,
) catch |err| {
const error_msg = switch (err) {
Error.OutOfMemory => "eru failed to listen to a message with error: no memory",
else => unreachable,
};
return zircon.Message{
.PRIVMSG = .{ .targets = "jassob", .text = error_msg },
};
});
},
else => {},
else => {
std.log.debug("received unknown message {}", .{message});
},
}
return null;
}
pub fn adapter(self: *BotAdapter) Adapter {
pub fn erased_callback(self: *anyopaque, message: zircon.Message) ?zircon.Message {
const a: *@This() = @ptrCast(@alignCast(self));
return a.callback(message);
}
pub fn closure(self: *BotAdapter) zircon.MessageClosure {
return .{
.ptr = self,
.callbackFn = self.callback,
};
}
};
pub const Command = struct {
command: BotCommand,
prefix: ?zircon.Prefix,
targets: []const u8,
pub fn parse(prefix: ?zircon.Prefix, targets: []const u8, text: []const u8) ?Command {
const nick = prefix.?.nick.?;
const command = BotCommand.parse(nick, text) orelse return null;
return .{
.command = command,
.prefix = prefix,
.targets = targets,
};
}
pub fn handle(self: Command, bot: *Bot) ?zircon.Message {
return bot.execute(&self.command, self.prefix, self.targets) catch |err| {
std.debug.print("Failed to handle {}: {}", .{ self, err });
return null;
.callbackFn = BotAdapter.erased_callback,
};
}
};
@ -136,5 +152,5 @@ test "substitute" {
};
const response = bot_adapter.callback(cmd_msg);
try std.testing.expect(response != null);
try std.testing.expectEqualStrings("hello zig", response.?.PRIVMSG.text);
try std.testing.expectEqualStrings("jassob: \"hello zig\"", response.?.PRIVMSG.text);
}