longbridge/alert/
context.rs1use std::sync::Arc;
2
3use longbridge_httpcli::{HttpClient, Json, Method};
4use serde::{Serialize, de::DeserializeOwned};
5use tracing::{Subscriber, dispatcher, instrument::WithSubscriber};
6
7use crate::{Config, Result, alert::types::*, utils::counter::symbol_to_counter_id};
8
9struct InnerAlertContext {
10 http_cli: HttpClient,
11 log_subscriber: Arc<dyn Subscriber + Send + Sync>,
12}
13
14impl Drop for InnerAlertContext {
15 fn drop(&mut self) {
16 dispatcher::with_default(&self.log_subscriber.clone().into(), || {
17 tracing::info!("alert context dropped");
18 });
19 }
20}
21
22#[derive(Clone)]
24pub struct AlertContext(Arc<InnerAlertContext>);
25
26impl AlertContext {
27 pub fn new(config: Arc<Config>) -> Self {
29 let log_subscriber = config.create_log_subscriber("alert");
30 dispatcher::with_default(&log_subscriber.clone().into(), || {
31 tracing::info!(language = ?config.language, "creating alert context");
32 });
33 let ctx = Self(Arc::new(InnerAlertContext {
34 http_cli: config.create_http_client(),
35 log_subscriber,
36 }));
37 dispatcher::with_default(&ctx.0.log_subscriber.clone().into(), || {
38 tracing::info!("alert context created");
39 });
40 ctx
41 }
42
43 #[inline]
45 pub fn log_subscriber(&self) -> Arc<dyn Subscriber + Send + Sync> {
46 self.0.log_subscriber.clone()
47 }
48
49 async fn get<R, Q>(&self, path: &'static str, query: Q) -> Result<R>
50 where
51 R: DeserializeOwned + Send + Sync + 'static,
52 Q: Serialize + Send + Sync,
53 {
54 Ok(self
55 .0
56 .http_cli
57 .request(Method::GET, path)
58 .query_params(query)
59 .response::<Json<R>>()
60 .send()
61 .with_subscriber(self.0.log_subscriber.clone())
62 .await?
63 .0)
64 }
65
66 async fn post<R, B>(&self, path: &'static str, body: B) -> Result<R>
67 where
68 R: DeserializeOwned + Send + Sync + 'static,
69 B: std::fmt::Debug + Serialize + Send + Sync + 'static,
70 {
71 Ok(self
72 .0
73 .http_cli
74 .request(Method::POST, path)
75 .body(Json(body))
76 .response::<Json<R>>()
77 .send()
78 .with_subscriber(self.0.log_subscriber.clone())
79 .await?
80 .0)
81 }
82
83 async fn http_delete<R, B>(&self, path: &'static str, body: B) -> Result<R>
84 where
85 R: DeserializeOwned + Send + Sync + 'static,
86 B: std::fmt::Debug + Serialize + Send + Sync + 'static,
87 {
88 Ok(self
89 .0
90 .http_cli
91 .request(Method::DELETE, path)
92 .body(Json(body))
93 .response::<Json<R>>()
94 .send()
95 .with_subscriber(self.0.log_subscriber.clone())
96 .await?
97 .0)
98 }
99
100 pub async fn list(&self) -> Result<AlertList> {
104 #[derive(Serialize)]
105 struct Empty {}
106 self.get("/v1/notify/reminders", Empty {}).await
107 }
108
109 pub async fn add(
113 &self,
114 symbol: impl Into<String>,
115 condition: AlertCondition,
116 trigger_value: impl Into<String>,
117 frequency: AlertFrequency,
118 ) -> Result<serde_json::Value> {
119 let cid = symbol_to_counter_id(&symbol.into());
120 let (key, val) = match condition {
121 AlertCondition::PriceRise | AlertCondition::PriceFall => {
122 ("price", trigger_value.into())
123 }
124 AlertCondition::PercentRise | AlertCondition::PercentFall => {
125 ("chg", trigger_value.into())
126 }
127 };
128 let indicator_id = condition as i32;
129 let freq = frequency as i32;
130 self.post(
131 "/v1/notify/reminders",
132 serde_json::json!({
133 "counter_id": cid,
134 "indicator_id": indicator_id.to_string(),
135 "value_map": { key: val },
136 "frequency": freq,
137 "enabled": true,
138 "scope": 0,
139 "state": [1]
140 }),
141 )
142 .await
143 }
144
145 pub async fn update(&self, item: &AlertItem) -> Result<serde_json::Value> {
154 self.post(
155 "/v1/notify/reminders",
156 serde_json::json!({
157 "id": item.id,
158 "indicator_id": item.indicator_id,
159 "frequency": item.frequency,
160 "scope": item.scope,
161 "state": item.state,
162 "value_map": item.value_map,
163 "enabled": item.enabled,
164 }),
165 )
166 .await
167 }
168
169 pub async fn delete(&self, alert_ids: Vec<String>) -> Result<serde_json::Value> {
173 self.http_delete(
174 "/v1/notify/reminders",
175 serde_json::json!({ "ids": alert_ids }),
176 )
177 .await
178 }
179}