gatewayd: strip a leading mention of this bot; recognise approval commands
This commit is contained in:
@@ -176,10 +176,7 @@ fn channels_are_answered_only_when_named_or_in_our_thread() {
|
||||
&post(&p1, KYLE, SHARED, "", "@boxmaker-straylight start", 5),
|
||||
"O",
|
||||
);
|
||||
assert_eq!(
|
||||
turns.recv_timeout(WAIT).unwrap().content,
|
||||
"@boxmaker-straylight start"
|
||||
);
|
||||
assert_eq!(turns.recv_timeout(WAIT).unwrap().content, "start");
|
||||
mm.wait_posts(1, WAIT);
|
||||
ws.posted(
|
||||
&post(&id('p', 2), KYLE, SHARED, &p1, "@hermes your turn", 6),
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use gatewayd::mm::Post;
|
||||
use gatewayd::sessions::{
|
||||
BUSY, Batch, Ignored, M4B_COMMAND, Message, Pushed, Queues, Route, Router, Thread,
|
||||
ApprovalCommand, BUSY, Batch, Ignored, Message, Pushed, Queues, Route, Router, Thread,
|
||||
UNKNOWN_COMMAND, named,
|
||||
};
|
||||
use proto::SessionId;
|
||||
@@ -185,7 +185,7 @@ fn sessions_and_threads() {
|
||||
assert!(reply.resume);
|
||||
let channel = queued(r.route(&post(SHARED, "", "@boxmaker-straylight hi"), "O", &unknown));
|
||||
assert!(channel.joins_thread && !channel.resume);
|
||||
assert_eq!(channel.text, "@boxmaker-straylight hi");
|
||||
assert_eq!(channel.text, "hi", "the leading mention is stripped (M4b)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -195,21 +195,37 @@ fn commands() {
|
||||
channel: DM.to_string(),
|
||||
root: ROOT.to_string(),
|
||||
};
|
||||
let reply = |text: &str| Route::Reply {
|
||||
thread: thread.clone(),
|
||||
text: text.to_string(),
|
||||
};
|
||||
for (message, answer) in [
|
||||
("!approve 42", M4B_COMMAND),
|
||||
("!deny 42 not now", M4B_COMMAND),
|
||||
("!deny", M4B_COMMAND),
|
||||
("!approved", UNKNOWN_COMMAND),
|
||||
("!help", UNKNOWN_COMMAND),
|
||||
("!", UNKNOWN_COMMAND),
|
||||
for (message, command) in [
|
||||
("!approve 42", ApprovalCommand::Approve(42)),
|
||||
(
|
||||
"!deny 42 not now",
|
||||
ApprovalCommand::Deny(42, Some("not now".to_string())),
|
||||
),
|
||||
("!deny", ApprovalCommand::Malformed),
|
||||
(
|
||||
"@boxmaker-straylight !approve 7",
|
||||
ApprovalCommand::Approve(7),
|
||||
),
|
||||
] {
|
||||
assert_eq!(
|
||||
r.route(&post(DM, ROOT, message), "D", &unknown),
|
||||
reply(answer),
|
||||
Route::Approval {
|
||||
thread: thread.clone(),
|
||||
command,
|
||||
user: KYLE.to_string(),
|
||||
post: POST.to_string(),
|
||||
at: 5,
|
||||
},
|
||||
"{message}"
|
||||
);
|
||||
}
|
||||
for message in ["!approved", "!help", "!"] {
|
||||
assert_eq!(
|
||||
r.route(&post(DM, ROOT, message), "D", &unknown),
|
||||
Route::Reply {
|
||||
thread: thread.clone(),
|
||||
text: UNKNOWN_COMMAND.to_string(),
|
||||
},
|
||||
"{message}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
//! A leading mention of this bot is stripped before anything reads a message, and approval
|
||||
//! commands are recognised exactly (M4b spec, section 7). Do not edit.
|
||||
|
||||
use gatewayd::mm::Post;
|
||||
use gatewayd::sessions::{
|
||||
ApprovalCommand, MAX_REASON, Route, Router, approval_command, strip_mention,
|
||||
};
|
||||
|
||||
const ME: &str = "boxmaker0";
|
||||
const BOT: &str = "b0000000000000000000000000";
|
||||
const KYLE: &str = "k0000000000000000000000000";
|
||||
const SHARED: &str = "c0000000000000000000000000";
|
||||
const ROOT: &str = "r0000000000000000000000000";
|
||||
|
||||
#[test]
|
||||
fn a_leading_mention_is_stripped() {
|
||||
assert_eq!(
|
||||
strip_mention("@boxmaker0 summarise the log", ME),
|
||||
"summarise the log"
|
||||
);
|
||||
assert_eq!(strip_mention("@BoxMaker0 !approve 42", ME), "!approve 42");
|
||||
assert_eq!(
|
||||
strip_mention("@boxmaker0 @hermes compare notes", ME),
|
||||
"@hermes compare notes"
|
||||
);
|
||||
assert_eq!(strip_mention("@boxmaker0, look", ME), ", look");
|
||||
assert_eq!(
|
||||
strip_mention("@boxmaker0\nsecond line", ME),
|
||||
"\nsecond line",
|
||||
"only spaces go with it"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn anything_else_is_kept() {
|
||||
for message in [
|
||||
"@boxmaker0",
|
||||
"@boxmaker0 ",
|
||||
"@boxmaker0x hello",
|
||||
"@hermes @boxmaker0 hello",
|
||||
"hello @boxmaker0",
|
||||
" @boxmaker0 hello",
|
||||
"@ boxmaker0 hello",
|
||||
"@@boxmaker0 hello",
|
||||
"",
|
||||
] {
|
||||
assert_eq!(strip_mention(message, ME), message, "{message:?}");
|
||||
}
|
||||
assert_eq!(
|
||||
strip_mention("@boxmaker0. hi", ME),
|
||||
". hi",
|
||||
"the trailing dot is not part of the name"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn approval_commands_exactly() {
|
||||
use ApprovalCommand::{Approve, Deny, Malformed};
|
||||
let cases: &[(&str, Option<ApprovalCommand>)] = &[
|
||||
("!approve 42", Some(Approve(42))),
|
||||
("!approve 0", Some(Approve(0))),
|
||||
("!approve 18446744073709551615", Some(Approve(u64::MAX))),
|
||||
("!deny 42", Some(Deny(42, None))),
|
||||
(
|
||||
"!deny 42 not on a Friday",
|
||||
Some(Deny(42, Some("not on a Friday".to_string()))),
|
||||
),
|
||||
(
|
||||
"!deny 42 two spaces",
|
||||
Some(Deny(42, Some(" two spaces".to_string()))),
|
||||
),
|
||||
("!approve", Some(Malformed)),
|
||||
("!approve 42", Some(Malformed)),
|
||||
("!approve 42 ", Some(Malformed)),
|
||||
("!approve 42 now", Some(Malformed)),
|
||||
("!approve +42", Some(Malformed)),
|
||||
("!approve 4x2", Some(Malformed)),
|
||||
("!approve 18446744073709551616", Some(Malformed)),
|
||||
("!deny", Some(Malformed)),
|
||||
("!deny 42 ", Some(Malformed)),
|
||||
("!deny x reason", Some(Malformed)),
|
||||
("!approved 42", None),
|
||||
("!denied 42", None),
|
||||
("!help", None),
|
||||
("approve 42", None),
|
||||
("!Approve 42", None),
|
||||
];
|
||||
for (text, want) in cases {
|
||||
assert_eq!(&approval_command(text), want, "{text:?}");
|
||||
}
|
||||
let long = format!("!deny 42 {}", "é".repeat(MAX_REASON));
|
||||
assert!(
|
||||
matches!(approval_command(&long), Some(Deny(42, Some(_)))),
|
||||
"characters, not bytes"
|
||||
);
|
||||
let over = format!("!deny 42 {}", "x".repeat(MAX_REASON + 1));
|
||||
assert_eq!(approval_command(&over), Some(Malformed));
|
||||
}
|
||||
|
||||
fn post(message: &str, root: &str) -> Post {
|
||||
Post {
|
||||
id: "p0000000000000000000000001".to_string(),
|
||||
user_id: KYLE.to_string(),
|
||||
channel_id: SHARED.to_string(),
|
||||
root_id: root.to_string(),
|
||||
message: message.to_string(),
|
||||
create_at: 9,
|
||||
delete_at: 0,
|
||||
kind: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn in_a_shared_thread_the_mention_picks_the_bot() {
|
||||
let me = Router::new(BOT, ME, &[KYLE.to_string()], &[SHARED.to_string()]);
|
||||
let ours = |root: &str| root == ROOT;
|
||||
let addressed = me.route(&post("@boxmaker0 !approve 42", ROOT), "O", &ours);
|
||||
assert!(
|
||||
matches!(
|
||||
addressed,
|
||||
Route::Approval {
|
||||
command: ApprovalCommand::Approve(42),
|
||||
..
|
||||
}
|
||||
),
|
||||
"{addressed:?}"
|
||||
);
|
||||
let other = me.route(&post("@boxmaker1 !approve 42", ROOT), "O", &ours);
|
||||
assert!(
|
||||
matches!(other, Route::Ignore(_)),
|
||||
"addressed to another bot: {other:?}"
|
||||
);
|
||||
let bare = me.route(&post("!approve 42", ROOT), "O", &ours);
|
||||
assert!(
|
||||
matches!(bare, Route::Approval { .. }),
|
||||
"a bare command in our thread is ours too: {bare:?}"
|
||||
);
|
||||
match me.route(&post("@boxmaker0 hello there", ""), "O", &ours) {
|
||||
Route::Queue(m) => assert_eq!(m.text, "hello there"),
|
||||
other => panic!("{other:?}"),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user