receive posts from zulip and reply

This commit is contained in:
joe 2025-12-20 14:19:18 -08:00
parent 13a8f5dc8a
commit 046d3157c2
4 changed files with 52 additions and 15 deletions

10
Cargo.lock generated
View file

@ -221,6 +221,7 @@ dependencies = [
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"unicode-segmentation", "unicode-segmentation",
"winnow",
] ]
[[package]] [[package]]
@ -3306,6 +3307,15 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "winnow"
version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "wit-bindgen" name = "wit-bindgen"
version = "0.46.0" version = "0.46.0"

View file

@ -21,6 +21,7 @@ tokio-util = "0.7.17"
tracing = "0.1.43" tracing = "0.1.43"
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] } tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
unicode-segmentation = "1.12.0" unicode-segmentation = "1.12.0"
winnow = "0.7.14"
[dev-dependencies] [dev-dependencies]
rand = "0.9.2" rand = "0.9.2"

View file

@ -30,7 +30,8 @@ pub struct BlogdorTheAggregator {
cancel: CancellationToken, cancel: CancellationToken,
endpoint: String, endpoint: String,
channel_id: u32, channel_id: u32,
email: String, blogdor_to_zulip_email: String,
zulip_to_blogdor_email: String,
zulip_token: String, // sent *to zulip* in POSTs *from us* zulip_token: String, // sent *to zulip* in POSTs *from us*
blogdor_token: String, // sent *from zulip* in POSTs *to us* blogdor_token: String, // sent *from zulip* in POSTs *to us*
} }
@ -75,7 +76,10 @@ impl BlogdorTheAggregator {
.parse() .parse()
.expect("ZULIP_CHANNEL must be an integer"); .expect("ZULIP_CHANNEL must be an integer");
let password = std::env::var("ZULIP_TOKEN").expect("ZULIP_TOKEN must be set"); let password = std::env::var("ZULIP_TOKEN").expect("ZULIP_TOKEN must be set");
let email = std::env::var("BLOGDOR_EMAIL").expect("BLOGDOR_EMAIL must be set"); let b2z_email =
std::env::var("BLOGDOR_TO_ZULIP_EMAIL").expect("BLOGDOR_TO_ZULIP_EMAIL must be set");
let z2b_email =
std::env::var("ZULIP_TO_BLOGDOR_EMAIL").expect("ZULIP_TO_BLOGDOR_EMAIL must be set");
let token = std::env::var("BLOGDOR_TOKEN").expect("BLOGDOR_TOKEN must be set"); let token = std::env::var("BLOGDOR_TOKEN").expect("BLOGDOR_TOKEN must be set");
Self { Self {
@ -84,7 +88,8 @@ impl BlogdorTheAggregator {
cancel, cancel,
endpoint, endpoint,
channel_id, channel_id,
email, blogdor_to_zulip_email: b2z_email,
zulip_to_blogdor_email: z2b_email,
zulip_token: password, zulip_token: password,
blogdor_token: token, blogdor_token: token,
} }
@ -95,7 +100,11 @@ impl BlogdorTheAggregator {
} }
pub async fn spawn_http(&self) { pub async fn spawn_http(&self) {
let state = ServerState::new(self.db.clone(), &self.email, &self.blogdor_token); let state = ServerState::new(
self.db.clone(),
&self.zulip_to_blogdor_email,
&self.blogdor_token,
);
server::spawn_server(state, self.cancel.clone()).await; server::spawn_server(state, self.cancel.clone()).await;
} }
@ -161,7 +170,7 @@ impl BlogdorTheAggregator {
match self match self
.client .client
.post(&self.endpoint) .post(&self.endpoint)
.basic_auth(&self.email, Some(&self.zulip_token)) .basic_auth(&self.blogdor_to_zulip_email, Some(&self.zulip_token))
.body(msg) .body(msg)
.header("Content-Type", "application/x-www-form-urlencoded") .header("Content-Type", "application/x-www-form-urlencoded")
.send() .send()

View file

@ -1,4 +1,4 @@
use std::{net::SocketAddr, sync::Arc}; use std::{collections::HashMap, net::SocketAddr, sync::Arc};
use axum::{ use axum::{
Router, Router,
@ -11,6 +11,7 @@ use serde::Deserialize;
use serde_json::{Map, Value}; use serde_json::{Map, Value};
use sqlx::SqlitePool; use sqlx::SqlitePool;
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
use winnow::Parser;
type Payload = Map<String, Value>; type Payload = Map<String, Value>;
@ -49,28 +50,28 @@ pub(crate) async fn spawn_server(
fn make_router(state: ServerState) -> Router { fn make_router(state: ServerState) -> Router {
Router::new() Router::new()
.route("/api/v1/add-feed", post(handle_add_feed)) .route("/blogdor/manage-feeds", post(handle_manage_feed))
.with_state(state.into()) .with_state(state.into())
} }
async fn handle_add_feed( async fn handle_manage_feed(
State(state): State<Arc<ServerState>>, State(state): State<Arc<ServerState>>,
Json(request): Json<AddFeedRequest>, Json(request): Json<ManageFeedMessage>,
) -> impl IntoResponse { ) -> impl IntoResponse {
let AddFeedRequest { let ManageFeedMessage {
bot_email, bot_email,
token, token,
message, message,
_rest: _, _rest: _,
} = request; } = request;
tracing::debug!("email: {bot_email}, token: {token}");
if state.email == bot_email && state.token == token { if state.email == bot_email && state.token == token {
tracing::debug!("gonna do a thing with {message:?}"); tracing::debug!("gonna do a thing with {message:?}");
// things
(StatusCode::IM_A_TEAPOT, "nee-ope") Json(HashMap::from([("content", "nee-ope")])).into_response()
} else { } else {
tracing::debug!("psych"); tracing::debug!("bad emal/token");
(StatusCode::IM_A_TEAPOT, "nee-ope") StatusCode::IM_A_TEAPOT.into_response()
} }
} }
@ -101,7 +102,7 @@ async fn graceful_shutdown(cancel: CancellationToken) {
} }
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
struct AddFeedRequest { struct ManageFeedMessage {
bot_email: String, bot_email: String,
token: String, token: String,
message: ZulipMessage, message: ZulipMessage,
@ -118,3 +119,19 @@ struct ZulipMessage {
#[serde(flatten)] #[serde(flatten)]
_rest: Payload, _rest: Payload,
} }
fn parse_command(input: &mut &str) -> String {
// let Ok(s) = ("***@", winnow::token::take_until(1.., "**"))
// .map(|(_, s, _)| s)
// .parse_next(input)
// else {
// return "could not find Blogdor's name".to_string();
// };
// if s != "Blogdor Outgoing Bot" {
// return "could not confirm Blogdor's name".to_string();
// }
// s.into()
todo!()
}