gatewayd: images, and links in untrusted replies, are posted as code
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
This commit is contained in:
@@ -10,6 +10,7 @@ use bxcore::{
|
||||
write_frame,
|
||||
};
|
||||
|
||||
use crate::links::{NOTICE, disarm};
|
||||
use crate::mm::MmError;
|
||||
use crate::mm::rest::Client;
|
||||
use crate::sessions::{Batch, Thread};
|
||||
@@ -33,7 +34,11 @@ impl Poster for Client {
|
||||
/// How a turn ended.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Outcome {
|
||||
Answer(String),
|
||||
/// The reply, and whether its session has read untrusted content.
|
||||
Answer {
|
||||
content: String,
|
||||
untrusted: bool,
|
||||
},
|
||||
Refused(WireError),
|
||||
/// `loop.sock` could not be reached or closed early; why, for the log.
|
||||
LoopDown(String),
|
||||
@@ -122,6 +127,25 @@ pub fn split_answer(text: &str) -> Vec<String> {
|
||||
parts
|
||||
}
|
||||
|
||||
/// The posts of a reply: disarmed (M5b spec, section 9), then cut by `split_answer`. Each part is
|
||||
/// disarmed again: a cut inside a fenced block leaves the rest of it as prose in the next post.
|
||||
/// The last post ends with `NOTICE` if either pass put a link in a code span.
|
||||
pub fn answer_posts(content: &str, untrusted: bool) -> Vec<String> {
|
||||
let whole = disarm(content, untrusted);
|
||||
let mut links = whole.links;
|
||||
let mut posts = Vec::new();
|
||||
for part in split_answer(&whole.text) {
|
||||
let again = disarm(&part, untrusted);
|
||||
links = links || again.links;
|
||||
posts.push(again.text);
|
||||
}
|
||||
if links && let Some(last) = posts.last_mut() {
|
||||
last.push_str("\n\n");
|
||||
last.push_str(NOTICE);
|
||||
}
|
||||
posts
|
||||
}
|
||||
|
||||
/// Send one turn and read it to its end; `on_event` sees every event.
|
||||
fn one_turn(
|
||||
socket: &Path,
|
||||
@@ -157,8 +181,8 @@ fn one_turn(
|
||||
}
|
||||
|
||||
// 3. `read_frame` until the end: (id 1, not final, TurnEvent) -> on_event; (1, final, TurnDone)
|
||||
// -> Answer with its content; (1, final, Error) -> Refused; a read error -> LoopDown("the
|
||||
// turn ended early: <e>"); anything else -> LoopDown("an unexpected frame").
|
||||
// -> Answer with its content and untrusted; (1, final, Error) -> Refused; a read error ->
|
||||
// LoopDown("the turn ended early: <e>"); anything else -> LoopDown("an unexpected frame").
|
||||
loop {
|
||||
let env = match read_frame(&mut stream) {
|
||||
Ok(env) => env,
|
||||
@@ -169,7 +193,12 @@ fn one_turn(
|
||||
}
|
||||
match (env.r#final, &env.msg) {
|
||||
(false, Message::TurnEvent(e)) => on_event(e),
|
||||
(true, Message::TurnDone(d)) => return Outcome::Answer(d.content.clone()),
|
||||
(true, Message::TurnDone(d)) => {
|
||||
return Outcome::Answer {
|
||||
content: d.content.clone(),
|
||||
untrusted: d.untrusted,
|
||||
};
|
||||
}
|
||||
(true, Message::Error(w)) => return Outcome::Refused(w.clone()),
|
||||
_ => return Outcome::LoopDown("an unexpected frame".to_string()),
|
||||
}
|
||||
@@ -209,7 +238,7 @@ pub fn deliver(
|
||||
) {
|
||||
// Post in the batch thread. An ApprovalPending event waits up to `notice.wait` for `brokerd` to
|
||||
// bring the approval through `gateway.sock`; only when it does not, it posts the notice. Then:
|
||||
// Answer -> every part of `split_answer`, in order; Refused -> `error_text`;
|
||||
// Answer -> every post of `answer_posts`, in order; Refused -> `error_text`;
|
||||
// LoopDown(why) -> log "gatewayd: <session>: <why>" and post LOOP_DOWN. A post that fails is
|
||||
// logged, exactly "gatewayd: cannot post in <channel> (thread <root>): <error>", and the rest
|
||||
// goes on.
|
||||
@@ -222,8 +251,8 @@ pub fn deliver(
|
||||
});
|
||||
|
||||
match outcome {
|
||||
Outcome::Answer(content) => {
|
||||
for part in split_answer(&content) {
|
||||
Outcome::Answer { content, untrusted } => {
|
||||
for part in answer_posts(&content, untrusted) {
|
||||
post(poster, &batch.thread, part, log);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user