refactor: move message dispatch logic to bot
This commit moves the logic that governs what action to take when a message is heard by the bot from the BotAdapter (which should be a layer only responsible for translating IRC messages to our internal representation) to the Bot. This makes it possible to test full conversations in the bot tests.
This commit is contained in:
parent
49a6b79fd9
commit
bd1891521e
2 changed files with 57 additions and 40 deletions
68
src/bot.zig
68
src/bot.zig
|
|
@ -94,6 +94,19 @@ pub const Bot = struct {
|
|||
self.backlog.deinit();
|
||||
}
|
||||
|
||||
pub fn hear(self: *Bot, message: *const Message) ?Error!Response {
|
||||
// Store the message to keep track of the allocation
|
||||
defer self.store(message);
|
||||
|
||||
if (UserCommand.parse(message.author, message.content)) |cmd| {
|
||||
return self.execute(&cmd, message.targets);
|
||||
}
|
||||
if (AdminCommand.parse(message.content)) |cmd| {
|
||||
return self.execute_admin(&cmd, "#eru-admin");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn execute(self: *Bot, cmd: *const UserCommand, targets: []const u8) Error!Response {
|
||||
switch (cmd.*) {
|
||||
.substitute => |command| {
|
||||
|
|
@ -216,7 +229,7 @@ test "hear and deinit has no leaks" {
|
|||
defer bot.deinit();
|
||||
|
||||
const testMessage = try newTestMessage(allocator, "test");
|
||||
bot.store(testMessage);
|
||||
try std.testing.expectEqual(null, bot.hear(testMessage));
|
||||
|
||||
try std.testing.expectEqual(0, bot.backlog.top);
|
||||
}
|
||||
|
|
@ -228,7 +241,7 @@ test "a few hears and deinit has no leaks" {
|
|||
|
||||
for (0..2) |_| {
|
||||
const testMessage = try newTestMessage(std.testing.allocator, "test");
|
||||
bot.store(testMessage);
|
||||
_ = bot.hear(testMessage);
|
||||
}
|
||||
|
||||
try std.testing.expectEqual(1, bot.backlog.top);
|
||||
|
|
@ -240,7 +253,7 @@ test "hear wraps" {
|
|||
|
||||
for (0..1025) |_| {
|
||||
const testMessage = try newTestMessage(std.testing.allocator, "test");
|
||||
bot.store(testMessage);
|
||||
_ = bot.hear(testMessage);
|
||||
}
|
||||
|
||||
try std.testing.expectEqual(0, bot.backlog.top);
|
||||
|
|
@ -251,12 +264,9 @@ test "hear wraps" {
|
|||
test "execute substitution no previous message" {
|
||||
var bot = try Bot.init(std.testing.allocator);
|
||||
defer bot.deinit();
|
||||
const cmd = UserCommand{ .substitute = .{
|
||||
.author = "jassob",
|
||||
.needle = "What",
|
||||
.replacement = "what",
|
||||
} };
|
||||
try std.testing.expectError(Error.NoMessage, bot.execute(&cmd, "#test"));
|
||||
|
||||
const substitution = try newTestMessage(std.testing.allocator, "s/What/what/");
|
||||
try std.testing.expectError(Error.NoMessage, bot.hear(substitution).?);
|
||||
}
|
||||
|
||||
test "execute substitution" {
|
||||
|
|
@ -266,11 +276,11 @@ test "execute substitution" {
|
|||
|
||||
// hear original message with typo
|
||||
const msg = try newTestMessage(allocator, "What");
|
||||
bot.store(msg);
|
||||
try std.testing.expectEqual(null, bot.hear(msg));
|
||||
|
||||
// execute substitution
|
||||
const cmd = UserCommand.init_substitute("jassob", "What", "what", false);
|
||||
const response = try bot.execute(&cmd, "#test");
|
||||
const sub = try newTestMessage(allocator, "s/What/what/");
|
||||
const response = try bot.hear(sub).?;
|
||||
|
||||
// expect response matching the correct message
|
||||
switch (response) {
|
||||
|
|
@ -282,29 +292,31 @@ test "execute substitution" {
|
|||
}
|
||||
|
||||
test "execute substitution with no matching needle" {
|
||||
var bot = try Bot.init(std.testing.allocator);
|
||||
const allocator = std.testing.allocator;
|
||||
var bot = try Bot.init(allocator);
|
||||
defer bot.deinit();
|
||||
|
||||
// hear original message
|
||||
const msg = try newTestMessage(std.testing.allocator, "original");
|
||||
bot.store(msg);
|
||||
const msg = try newTestMessage(allocator, "original");
|
||||
try std.testing.expectEqual(null, bot.hear(msg));
|
||||
|
||||
// execute substitution
|
||||
const cmd = UserCommand.init_substitute("jassob", "something else", "weird", false);
|
||||
try std.testing.expectError(Error.NoMessage, bot.execute(&cmd, "#test"));
|
||||
const sub = try newTestMessage(allocator, "s/something else/weird/");
|
||||
try std.testing.expectError(Error.NoMessage, bot.hear(sub).?);
|
||||
}
|
||||
|
||||
test "recursive substitutions does not cause issues" {
|
||||
var bot = try Bot.init(std.testing.allocator);
|
||||
const allocator = std.testing.allocator;
|
||||
var bot = try Bot.init(allocator);
|
||||
defer bot.deinit();
|
||||
|
||||
// hear original message
|
||||
const msg = try newTestMessage(std.testing.allocator, "original");
|
||||
bot.store(msg);
|
||||
const msg = try newTestMessage(allocator, "original");
|
||||
try std.testing.expectEqual(null, bot.hear(msg));
|
||||
|
||||
// execute substitution
|
||||
const cmd = UserCommand.init_substitute("jassob", "original", "something else", false);
|
||||
switch (try bot.execute(&cmd, "#test")) {
|
||||
const sub = try newTestMessage(allocator, "s/original/something else/");
|
||||
switch (try bot.hear(sub).?) {
|
||||
.privmsg => |message| {
|
||||
try std.testing.expectEqualDeep("jassob: \"something else\"", message.text);
|
||||
},
|
||||
|
|
@ -312,6 +324,14 @@ test "recursive substitutions does not cause issues" {
|
|||
}
|
||||
|
||||
// execute second substitution
|
||||
const cmd2 = UserCommand.init_substitute("jassob", "s/original/something else/", "something else", false);
|
||||
try std.testing.expectError(Error.NoMessage, bot.execute(&cmd2, "#test"));
|
||||
const sub2 = try newTestMessage(
|
||||
allocator,
|
||||
"s|s/original/something else/|something else|",
|
||||
);
|
||||
switch (try bot.hear(sub2).?) {
|
||||
.privmsg => |message| {
|
||||
try std.testing.expectEqualDeep("jassob: \"something else\"", message.text);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
29
src/main.zig
29
src/main.zig
|
|
@ -67,26 +67,24 @@ pub const BotAdapter = struct {
|
|||
pub fn callback(self: *BotAdapter, message: zircon.Message) ?zircon.Message {
|
||||
switch (message) {
|
||||
.PRIVMSG => |msg| {
|
||||
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 (UserCommand.parse(nick, msg.text)) |cmd| {
|
||||
return toIRC(self.bot.execute(&cmd, msg.targets) catch |err| return report_error(err));
|
||||
}
|
||||
if (AdminCommand.parse(msg.text)) |cmd| {
|
||||
return toIRC(self.bot.execute_admin(&cmd, "#eru-admin") catch |err| return report_error(err));
|
||||
}
|
||||
const bot_msg = BotMessage.init_owned(
|
||||
const nick = if (msg.prefix != null and msg.prefix.?.nick != null) msg.prefix.?.nick.? else "unknown";
|
||||
|
||||
// create message
|
||||
const bot_message = BotMessage.init_owned(
|
||||
self.allocator,
|
||||
std.time.timestamp(),
|
||||
nick,
|
||||
msg.targets,
|
||||
msg.text,
|
||||
) catch |err| return report_error(err);
|
||||
self.bot.store(bot_msg);
|
||||
return null;
|
||||
|
||||
// send message to bot
|
||||
const response = self.bot.hear(bot_message) orelse {
|
||||
return null;
|
||||
} catch |err| {
|
||||
return report_error(err);
|
||||
};
|
||||
return toIRC(response);
|
||||
},
|
||||
.JOIN => |msg| {
|
||||
std.log.debug("received join message: channels {s}", .{msg.channels});
|
||||
|
|
@ -162,9 +160,8 @@ test "substitute" {
|
|||
test "get empty backlog message" {
|
||||
var bot_adapter = try BotAdapter.init(std.testing.allocator);
|
||||
defer bot_adapter.deinit();
|
||||
const prefix = zircon.Prefix{ .nick = "jassob", .user = "jassob", .host = "localhost" };
|
||||
const msg = zircon.Message{
|
||||
.PRIVMSG = .{ .prefix = prefix, .targets = "#eru-admin", .text = "!backlog 0" },
|
||||
.PRIVMSG = .{ .prefix = null, .targets = "#eru-admin", .text = "!backlog 0" },
|
||||
};
|
||||
try std.testing.expectEqualDeep("no matching message", bot_adapter.callback(msg).?.PRIVMSG.text);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue