use super::route_prelude::*;
use crate::hookstr::DEFAULT_RELAYS;
use std::borrow::Cow;
mod filters {
use regex::{Captures, Regex};
#[askama::filter_fn]
pub fn strip_hashtags(
s: &String,
_env: &dyn askama::Values,
) -> askama::Result<String> {
let re = Regex::new(r"#(\w+)").unwrap();
Ok(re
.replace_all(s, |caps: &Captures| caps[1].to_string())
.into())
}
}
#[derive(Template, Clone)]
#[template(path = "polls_list.gmi", escape = "txt")]
struct PollsTemplate {
polls: Events,
}
#[derive(Template, Clone)]
#[template(path = "poll.gmi", escape = "txt")]
struct PollTemplate {
poll: Event,
responses: Events,
}
pub async fn polls_list(
_ctx: RouteContext,
user: &'static mut CaracalUser,
) -> Response {
let polls_filter = Filter::new()
.kind(Kind::Poll)
.since(Timestamp::now() - dur_parse("60d").unwrap());
let Ok(polls) = user
.client
.fetch_combined_events(polls_filter, dur_parse("5s").unwrap())
.await
else {
return Response::temporary_failure(t!("fetch_polls_failed"));
};
Response::success(WindTemplate::render(PollsTemplate { polls }))
}
pub async fn poll_details(
ctx: RouteContext,
user: &'static mut CaracalUser,
) -> Response {
let Some(event_id_s) = ctx.parameters.get("event_id") else {
return Response::temporary_failure(t!("invalid_params"));
};
let Ok(event_id) = EventId::parse(event_id_s) else {
return Response::temporary_failure(t!("invalid_params"));
};
let poll_filter = Filter::new().kind(Kind::Poll).id(event_id);
let Ok(events) = user
.client
.fetch_combined_events(poll_filter, dur_parse("5s").unwrap())
.await
else {
return Response::temporary_failure(t!("fetch_polls_failed"));
};
let Some(poll) = events.first_owned() else {
return Response::temporary_failure(t!("fetch_polls_failed"));
};
let poll_relays: Vec<RelayUrl> = poll
.tags
.filter_standardized(TagKind::Custom(Cow::Borrowed("relay")))
.filter_map(|tag| {
if let TagStandard::Relay(url) = tag {
Some(url.clone())
} else {
None
}
})
.collect();
for relay in &poll_relays {
let _ = user.client.add_read_relay(relay).await;
}
let relays = if poll_relays.is_empty() {
DEFAULT_RELAYS
.iter()
.map(|u| RelayUrl::parse(u).unwrap())
.collect()
} else {
poll_relays
};
let resp_filter = Filter::new().kind(Kind::PollResponse).event(event_id);
let Ok(responses) = user
.client
.fetch_events_from(relays, resp_filter, dur_parse("5s").unwrap())
.await
else {
return Response::temporary_failure(t!("fetch_polls_failed"));
};
Response::success(WindTemplate::render(PollTemplate { poll, responses }))
}
pub async fn poll_respond(
ctx: RouteContext,
user: &'static mut CaracalUser,
) -> Response {
let Some(event_id_s) = ctx.parameters.get("event_id") else {
return Response::temporary_failure(t!("invalid_params"));
};
let Ok(event_id) = EventId::parse(event_id_s) else {
return Response::temporary_failure(t!("invalid_params"));
};
let Some(option_id) = ctx.parameters.get("option_id") else {
return Response::temporary_failure(t!("invalid_params"));
};
let poll_filter = Filter::new().kind(Kind::Poll).id(event_id);
let Ok(events) = user
.client
.fetch_events(poll_filter, dur_parse("5s").unwrap())
.await
else {
return Response::temporary_failure(t!("fetch_polls_failed"));
};
let Some(poll) = events.first() else {
return Response::temporary_failure(t!("fetch_polls_failed"));
};
let relays: Vec<RelayUrl> = poll
.tags
.filter_standardized(TagKind::Custom(Cow::Borrowed("relay")))
.filter_map(|tag| {
if let TagStandard::Relay(url) = tag {
Some(url.clone())
} else {
None
}
})
.take(2)
.collect();
for relay in &relays {
let _ = user.client.add_write_relay(relay).await;
}
let builder = EventBuilder::new(Kind::PollResponse, "")
.tag(Tag::event(event_id))
.tag(Tag::from_standardized(TagStandard::PollResponse(
option_id.to_string(),
)));
match user.client.send_event_builder_to(relays, builder).await {
Ok(_) => Response::temporary_redirect(format!("/polls/{event_id_s}")),
Err(err) => Response::temporary_failure(format!("{err}")),
}
}