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();
|
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 {
|
pub fn execute(self: *Bot, cmd: *const UserCommand, targets: []const u8) Error!Response {
|
||||||
switch (cmd.*) {
|
switch (cmd.*) {
|
||||||
.substitute => |command| {
|
.substitute => |command| {
|
||||||
|
|
@ -216,7 +229,7 @@ test "hear and deinit has no leaks" {
|
||||||
defer bot.deinit();
|
defer bot.deinit();
|
||||||
|
|
||||||
const testMessage = try newTestMessage(allocator, "test");
|
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);
|
try std.testing.expectEqual(0, bot.backlog.top);
|
||||||
}
|
}
|
||||||
|
|
@ -228,7 +241,7 @@ test "a few hears and deinit has no leaks" {
|
||||||
|
|
||||||
for (0..2) |_| {
|
for (0..2) |_| {
|
||||||
const testMessage = try newTestMessage(std.testing.allocator, "test");
|
const testMessage = try newTestMessage(std.testing.allocator, "test");
|
||||||
bot.store(testMessage);
|
_ = bot.hear(testMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
try std.testing.expectEqual(1, bot.backlog.top);
|
try std.testing.expectEqual(1, bot.backlog.top);
|
||||||
|
|
@ -240,7 +253,7 @@ test "hear wraps" {
|
||||||
|
|
||||||
for (0..1025) |_| {
|
for (0..1025) |_| {
|
||||||
const testMessage = try newTestMessage(std.testing.allocator, "test");
|
const testMessage = try newTestMessage(std.testing.allocator, "test");
|
||||||
bot.store(testMessage);
|
_ = bot.hear(testMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
try std.testing.expectEqual(0, bot.backlog.top);
|
try std.testing.expectEqual(0, bot.backlog.top);
|
||||||
|
|
@ -251,12 +264,9 @@ test "hear wraps" {
|
||||||
test "execute substitution no previous message" {
|
test "execute substitution no previous message" {
|
||||||
var bot = try Bot.init(std.testing.allocator);
|
var bot = try Bot.init(std.testing.allocator);
|
||||||
defer bot.deinit();
|
defer bot.deinit();
|
||||||
const cmd = UserCommand{ .substitute = .{
|
|
||||||
.author = "jassob",
|
const substitution = try newTestMessage(std.testing.allocator, "s/What/what/");
|
||||||
.needle = "What",
|
try std.testing.expectError(Error.NoMessage, bot.hear(substitution).?);
|
||||||
.replacement = "what",
|
|
||||||
} };
|
|
||||||
try std.testing.expectError(Error.NoMessage, bot.execute(&cmd, "#test"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "execute substitution" {
|
test "execute substitution" {
|
||||||
|
|
@ -266,11 +276,11 @@ test "execute substitution" {
|
||||||
|
|
||||||
// hear original message with typo
|
// hear original message with typo
|
||||||
const msg = try newTestMessage(allocator, "What");
|
const msg = try newTestMessage(allocator, "What");
|
||||||
bot.store(msg);
|
try std.testing.expectEqual(null, bot.hear(msg));
|
||||||
|
|
||||||
// execute substitution
|
// execute substitution
|
||||||
const cmd = UserCommand.init_substitute("jassob", "What", "what", false);
|
const sub = try newTestMessage(allocator, "s/What/what/");
|
||||||
const response = try bot.execute(&cmd, "#test");
|
const response = try bot.hear(sub).?;
|
||||||
|
|
||||||
// expect response matching the correct message
|
// expect response matching the correct message
|
||||||
switch (response) {
|
switch (response) {
|
||||||
|
|
@ -282,29 +292,31 @@ test "execute substitution" {
|
||||||
}
|
}
|
||||||
|
|
||||||
test "execute substitution with no matching needle" {
|
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();
|
defer bot.deinit();
|
||||||
|
|
||||||
// hear original message
|
// hear original message
|
||||||
const msg = try newTestMessage(std.testing.allocator, "original");
|
const msg = try newTestMessage(allocator, "original");
|
||||||
bot.store(msg);
|
try std.testing.expectEqual(null, bot.hear(msg));
|
||||||
|
|
||||||
// execute substitution
|
// execute substitution
|
||||||
const cmd = UserCommand.init_substitute("jassob", "something else", "weird", false);
|
const sub = try newTestMessage(allocator, "s/something else/weird/");
|
||||||
try std.testing.expectError(Error.NoMessage, bot.execute(&cmd, "#test"));
|
try std.testing.expectError(Error.NoMessage, bot.hear(sub).?);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "recursive substitutions does not cause issues" {
|
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();
|
defer bot.deinit();
|
||||||
|
|
||||||
// hear original message
|
// hear original message
|
||||||
const msg = try newTestMessage(std.testing.allocator, "original");
|
const msg = try newTestMessage(allocator, "original");
|
||||||
bot.store(msg);
|
try std.testing.expectEqual(null, bot.hear(msg));
|
||||||
|
|
||||||
// execute substitution
|
// execute substitution
|
||||||
const cmd = UserCommand.init_substitute("jassob", "original", "something else", false);
|
const sub = try newTestMessage(allocator, "s/original/something else/");
|
||||||
switch (try bot.execute(&cmd, "#test")) {
|
switch (try bot.hear(sub).?) {
|
||||||
.privmsg => |message| {
|
.privmsg => |message| {
|
||||||
try std.testing.expectEqualDeep("jassob: \"something else\"", message.text);
|
try std.testing.expectEqualDeep("jassob: \"something else\"", message.text);
|
||||||
},
|
},
|
||||||
|
|
@ -312,6 +324,14 @@ test "recursive substitutions does not cause issues" {
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute second substitution
|
// execute second substitution
|
||||||
const cmd2 = UserCommand.init_substitute("jassob", "s/original/something else/", "something else", false);
|
const sub2 = try newTestMessage(
|
||||||
try std.testing.expectError(Error.NoMessage, bot.execute(&cmd2, "#test"));
|
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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
27
src/main.zig
27
src/main.zig
|
|
@ -67,26 +67,24 @@ pub const BotAdapter = struct {
|
||||||
pub fn callback(self: *BotAdapter, message: zircon.Message) ?zircon.Message {
|
pub fn callback(self: *BotAdapter, message: zircon.Message) ?zircon.Message {
|
||||||
switch (message) {
|
switch (message) {
|
||||||
.PRIVMSG => |msg| {
|
.PRIVMSG => |msg| {
|
||||||
std.log.debug(
|
const nick = if (msg.prefix != null and msg.prefix.?.nick != null) msg.prefix.?.nick.? else "unknown";
|
||||||
"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 },
|
// create message
|
||||||
);
|
const bot_message = BotMessage.init_owned(
|
||||||
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(
|
|
||||||
self.allocator,
|
self.allocator,
|
||||||
std.time.timestamp(),
|
std.time.timestamp(),
|
||||||
nick,
|
nick,
|
||||||
msg.targets,
|
msg.targets,
|
||||||
msg.text,
|
msg.text,
|
||||||
) catch |err| return report_error(err);
|
) catch |err| return report_error(err);
|
||||||
self.bot.store(bot_msg);
|
|
||||||
|
// send message to bot
|
||||||
|
const response = self.bot.hear(bot_message) orelse {
|
||||||
return null;
|
return null;
|
||||||
|
} catch |err| {
|
||||||
|
return report_error(err);
|
||||||
|
};
|
||||||
|
return toIRC(response);
|
||||||
},
|
},
|
||||||
.JOIN => |msg| {
|
.JOIN => |msg| {
|
||||||
std.log.debug("received join message: channels {s}", .{msg.channels});
|
std.log.debug("received join message: channels {s}", .{msg.channels});
|
||||||
|
|
@ -162,9 +160,8 @@ test "substitute" {
|
||||||
test "get empty backlog message" {
|
test "get empty backlog message" {
|
||||||
var bot_adapter = try BotAdapter.init(std.testing.allocator);
|
var bot_adapter = try BotAdapter.init(std.testing.allocator);
|
||||||
defer bot_adapter.deinit();
|
defer bot_adapter.deinit();
|
||||||
const prefix = zircon.Prefix{ .nick = "jassob", .user = "jassob", .host = "localhost" };
|
|
||||||
const msg = zircon.Message{
|
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);
|
try std.testing.expectEqualDeep("no matching message", bot_adapter.callback(msg).?.PRIVMSG.text);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue