use async_trait::async_trait;
use night_fury_core::BrowserSession;
use night_fury_daemon_core::protocol::Response;
use serde_json::Value;
use tail_fin_bloomberg::{
BloombergClient, PaginateParams, SnapshotParams, StoriesParams, TopicsStoriesParams,
};
use crate::handlers::params::{optional_string_array, required_str};
use crate::handlers::response::to_response;
use crate::handlers::SiteHandler;
pub struct BloombergHandler {
session: BrowserSession,
}
impl BloombergHandler {
pub fn new(session: BrowserSession) -> Self {
Self { session }
}
}
#[async_trait]
impl SiteHandler for BloombergHandler {
async fn handle(&self, id: &str, cmd: &str, params: &Value) -> Response {
let client = BloombergClient::with_session(self.session.clone());
match cmd {
"bloomberg.section" => match required_str(params, "section") {
Ok(section) => {
let limit = params.get("limit").and_then(|v| v.as_u64()).unwrap_or(10) as usize;
let offset =
params.get("offset").and_then(|v| v.as_u64()).unwrap_or(0) as usize;
to_response(id, client.section_articles(§ion, limit, offset).await)
}
Err(e) => Response::err(id, e),
},
"bloomberg.article" => match required_str(params, "story_id") {
Ok(story_id) => to_response(id, client.article(&story_id).await),
Err(e) => Response::err(id, e),
},
"bloomberg.section-full" => match required_str(params, "section") {
Ok(section) => {
let limit = params.get("limit").and_then(|v| v.as_u64()).unwrap_or(5) as usize;
let offset =
params.get("offset").and_then(|v| v.as_u64()).unwrap_or(0) as usize;
to_response(id, client.section_full(§ion, limit, offset).await)
}
Err(e) => Response::err(id, e),
},
"bloomberg.ticker-snapshot" => match required_str(params, "ids") {
Ok(ids) => to_response(id, client.ticker_snapshot(&ids).await),
Err(e) => Response::err(id, e),
},
"bloomberg.article-story" => match required_str(params, "story_id") {
Ok(story_id) => {
let lang = params
.get("language_preference")
.and_then(|v| v.as_str())
.unwrap_or("EN");
to_response(id, client.article_story(&story_id, Some(lang)).await)
}
Err(e) => Response::err(id, e),
},
"bloomberg.article-recirc-stories" => match required_str(params, "ids") {
Ok(ids) => to_response(id, client.article_recirc_stories(&ids).await),
Err(e) => Response::err(id, e),
},
"bloomberg.blens" => match required_str(params, "ticker") {
Ok(ticker) => to_response(id, client.blens(&ticker).await),
Err(e) => Response::err(id, e),
},
"bloomberg.topics-stories" => match topics_params(params) {
Ok(p) => to_response(id, client.topics_stories(p).await),
Err(e) => Response::err(id, e),
},
"bloomberg.paginate" => match paginate_params(params) {
Ok(p) => to_response(id, client.paginate(p).await),
Err(e) => Response::err(id, e),
},
"bloomberg.newsletter" => match required_str(params, "slug") {
Ok(slug) => to_response(id, client.newsletter(&slug).await),
Err(e) => Response::err(id, e),
},
"bloomberg.stories" => match stories_params(params) {
Ok(p) => to_response(id, client.stories(p).await),
Err(e) => Response::err(id, e),
},
"bloomberg.snapshot" => to_response(id, client.snapshot(snapshot_params(params)).await),
"bloomberg.spotlights" => {
let site = params
.get("site")
.and_then(|v| v.as_str())
.unwrap_or("bcom");
to_response(id, client.spotlights(site).await)
}
"bloomberg.markets-wrap-news" => to_response(id, client.markets_wrap_news().await),
"bloomberg.media-manifest" => {
match (required_str(params, "id"), required_str(params, "variant")) {
(Ok(mid), Ok(variant)) => {
let stream_type = params.get("stream_type").and_then(|v| v.as_str());
to_response(id, client.media_manifest(&mid, &variant, stream_type).await)
}
(Err(e), _) | (_, Err(e)) => Response::err(id, e),
}
}
"bloomberg.green-dashboard" => to_response(id, client.green_dashboard().await),
"bloomberg.next-data" => match (
required_str(params, "build_id"),
required_str(params, "path"),
) {
(Ok(build_id), Ok(path)) => to_response(
id,
client
.next_data(&build_id, &path, query_params(params))
.await,
),
(Err(e), _) | (_, Err(e)) => Response::err(id, e),
},
other => Response::err(id, format!("unknown bloomberg cmd: {other}")),
}
}
}
fn topics_params(params: &Value) -> Result<TopicsStoriesParams, String> {
Ok(TopicsStoriesParams {
topic_ids: optional_string_array(params, "topic_ids")?,
story_limit: params
.get("story_limit")
.and_then(|v| v.as_u64())
.map(|v| v as usize),
dedupe_enabled: params.get("dedupe_enabled").and_then(|v| v.as_bool()),
story_ids_to_exclude: optional_string_array(params, "story_ids_to_exclude")?,
extra: query_params(params),
})
}
fn paginate_params(params: &Value) -> Result<PaginateParams, String> {
Ok(PaginateParams {
id: required_str(params, "id")?,
page: required_str(params, "page")?,
offset: params.get("offset").and_then(|v| v.as_u64()).unwrap_or(0) as usize,
extra: query_params(params),
})
}
fn stories_params(params: &Value) -> Result<StoriesParams, String> {
Ok(StoriesParams {
ids: optional_string_array(params, "ids")?,
extra: query_params(params),
})
}
fn snapshot_params(params: &Value) -> SnapshotParams {
SnapshotParams {
extra: query_params(params),
}
}
fn query_params(params: &Value) -> tail_fin_bloomberg::QueryParams {
let Some(q) = params.get("query") else {
return vec![];
};
if let Some(obj) = q.as_object() {
return obj
.iter()
.filter_map(|(k, v)| v.as_str().map(|s| (k.clone(), s.to_string())))
.collect();
}
q.as_array()
.into_iter()
.flatten()
.filter_map(|item| {
let s = item.as_str()?;
let (k, v) = s.split_once('=')?;
Some((k.to_string(), v.to_string()))
})
.collect()
}