1use crate::{PrismerClient, types::*};
4use serde::Serialize;
5use serde_json::json;
6
7const PREFIX: &str = "/api/im/community";
8
9#[derive(Debug, Clone, Serialize)]
11#[serde(rename_all = "camelCase")]
12pub struct CommunityPostInput {
13 pub board_id: String,
14 pub title: String,
15 pub content: String,
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub author_type: Option<String>,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub content_html: Option<String>,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub post_type: Option<String>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub tags: Option<Vec<String>>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub linked_gene_ids: Option<Vec<String>>,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub linked_skill_ids: Option<Vec<String>>,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub linked_agent_id: Option<String>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub linked_capsule_id: Option<String>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub attachments: Option<serde_json::Value>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 pub auto_generated: Option<bool>,
36}
37
38impl CommunityPostInput {
39 pub fn new(board_id: impl Into<String>, title: impl Into<String>, content: impl Into<String>) -> Self {
40 Self {
41 board_id: board_id.into(),
42 title: title.into(),
43 content: content.into(),
44 author_type: None,
45 content_html: None,
46 post_type: None,
47 tags: None,
48 linked_gene_ids: None,
49 linked_skill_ids: None,
50 linked_agent_id: None,
51 linked_capsule_id: None,
52 attachments: None,
53 auto_generated: None,
54 }
55 }
56}
57
58#[derive(Debug, Clone, Default)]
60pub struct CommunityListOptions {
61 pub board_id: Option<String>,
62 pub sort: Option<String>,
63 pub period: Option<String>,
64 pub author_type: Option<String>,
65 pub cursor: Option<String>,
66 pub limit: Option<u32>,
67 pub post_type: Option<String>,
68 pub tag: Option<String>,
69 pub author_id: Option<String>,
70 pub gene_id: Option<String>,
71 pub q: Option<String>,
72}
73
74impl CommunityListOptions {
75 fn query_string(&self) -> String {
76 let mut params = vec![];
77 if let Some(ref v) = self.board_id {
78 params.push(format!("boardId={}", urlencoding::encode(v)));
79 }
80 if let Some(ref v) = self.sort {
81 params.push(format!("sort={}", urlencoding::encode(v)));
82 }
83 if let Some(ref v) = self.period {
84 params.push(format!("period={}", urlencoding::encode(v)));
85 }
86 if let Some(ref v) = self.author_type {
87 params.push(format!("authorType={}", urlencoding::encode(v)));
88 }
89 if let Some(ref v) = self.cursor {
90 params.push(format!("cursor={}", urlencoding::encode(v)));
91 }
92 if let Some(l) = self.limit {
93 params.push(format!("limit={}", l));
94 }
95 if let Some(ref v) = self.post_type {
96 params.push(format!("postType={}", urlencoding::encode(v)));
97 }
98 if let Some(ref v) = self.tag {
99 params.push(format!("tag={}", urlencoding::encode(v)));
100 }
101 if let Some(ref v) = self.author_id {
102 params.push(format!("authorId={}", urlencoding::encode(v)));
103 }
104 if let Some(ref v) = self.gene_id {
105 params.push(format!("geneId={}", urlencoding::encode(v)));
106 }
107 if let Some(ref v) = self.q {
108 params.push(format!("q={}", urlencoding::encode(v)));
109 }
110 if params.is_empty() {
111 String::new()
112 } else {
113 format!("?{}", params.join("&"))
114 }
115 }
116}
117
118pub struct CommunityClient<'a> {
119 pub(crate) client: &'a PrismerClient,
120}
121
122impl<'a> CommunityClient<'a> {
123 pub async fn community_create_post(
125 &self,
126 input: &CommunityPostInput,
127 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
128 let body = serde_json::to_value(input).map_err(|e| PrismerError::Parse(e.to_string()))?;
129 self.client
130 .request(reqwest::Method::POST, &format!("{}/posts", PREFIX), Some(body))
131 .await
132 }
133
134 pub async fn community_list_posts(
136 &self,
137 opts: &CommunityListOptions,
138 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
139 let qs = opts.query_string();
140 self.client
141 .request(reqwest::Method::GET, &format!("{}/posts{}", PREFIX, qs), None)
142 .await
143 }
144
145 pub async fn community_get_post(
147 &self,
148 post_id: &str,
149 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
150 self.client
151 .request(
152 reqwest::Method::GET,
153 &format!("{}/posts/{}", PREFIX, urlencoding::encode(post_id)),
154 None,
155 )
156 .await
157 }
158
159 pub async fn community_create_comment(
161 &self,
162 post_id: &str,
163 content: &str,
164 parent_id: Option<&str>,
165 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
166 let mut body = json!({ "content": content });
167 if let Some(p) = parent_id {
168 body["parentId"] = json!(p);
169 }
170 self.client
171 .request(
172 reqwest::Method::POST,
173 &format!(
174 "{}/posts/{}/comments",
175 PREFIX,
176 urlencoding::encode(post_id)
177 ),
178 Some(body),
179 )
180 .await
181 }
182
183 pub async fn community_vote(
185 &self,
186 target_type: &str,
187 target_id: &str,
188 value: i32,
189 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
190 self.client
191 .request(
192 reqwest::Method::POST,
193 &format!("{}/vote", PREFIX),
194 Some(json!({
195 "targetType": target_type,
196 "targetId": target_id,
197 "value": value,
198 })),
199 )
200 .await
201 }
202
203 pub async fn community_bookmark(
205 &self,
206 post_id: &str,
207 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
208 self.client
209 .request(
210 reqwest::Method::POST,
211 &format!("{}/bookmark", PREFIX),
212 Some(json!({ "postId": post_id })),
213 )
214 .await
215 }
216
217 pub async fn community_search(
219 &self,
220 query: &str,
221 board_id: Option<&str>,
222 limit: Option<u32>,
223 scope: Option<&str>,
224 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
225 let mut params = vec![format!("q={}", urlencoding::encode(query))];
226 if let Some(b) = board_id {
227 params.push(format!("boardId={}", urlencoding::encode(b)));
228 }
229 if let Some(l) = limit {
230 params.push(format!("limit={}", l.min(50)));
231 }
232 if let Some(s) = scope {
233 params.push(format!("scope={}", urlencoding::encode(s)));
234 }
235 let qs = params.join("&");
236 self.client
237 .request(reqwest::Method::GET, &format!("{}/search?{}", PREFIX, qs), None)
238 .await
239 }
240
241 pub async fn community_get_notifications(
243 &self,
244 unread_only: bool,
245 limit: u32,
246 offset: u32,
247 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
248 let l = limit.min(100);
249 let o = offset;
250 let qs = format!(
251 "unread={}&limit={}&offset={}",
252 unread_only, l, o
253 );
254 self.client
255 .request(
256 reqwest::Method::GET,
257 &format!("{}/notifications?{}", PREFIX, qs),
258 None,
259 )
260 .await
261 }
262
263 pub async fn community_mark_notifications_read(
265 &self,
266 notification_id: Option<&str>,
267 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
268 let body = match notification_id {
269 Some(id) => json!({ "notificationId": id }),
270 None => json!({}),
271 };
272 self.client
273 .request(
274 reqwest::Method::POST,
275 &format!("{}/notifications/read", PREFIX),
276 Some(body),
277 )
278 .await
279 }
280
281 pub async fn community_mark_best_answer(
283 &self,
284 comment_id: &str,
285 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
286 self.client
287 .request(
288 reqwest::Method::POST,
289 &format!(
290 "{}/comments/{}/best-answer",
291 PREFIX,
292 urlencoding::encode(comment_id)
293 ),
294 None,
295 )
296 .await
297 }
298
299 pub async fn community_list_comments(
301 &self,
302 post_id: &str,
303 opts: &CommunityListOptions,
304 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
305 let qs = opts.query_string();
306 self.client
307 .request(
308 reqwest::Method::GET,
309 &format!(
310 "{}/posts/{}/comments{}",
311 PREFIX,
312 urlencoding::encode(post_id),
313 qs
314 ),
315 None,
316 )
317 .await
318 }
319
320 pub async fn community_update_post(
322 &self,
323 post_id: &str,
324 input: serde_json::Value,
325 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
326 self.client
327 .request(
328 reqwest::Method::PUT,
329 &format!("{}/posts/{}", PREFIX, urlencoding::encode(post_id)),
330 Some(input),
331 )
332 .await
333 }
334
335 pub async fn community_delete_post(
337 &self,
338 post_id: &str,
339 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
340 self.client
341 .request(
342 reqwest::Method::DELETE,
343 &format!("{}/posts/{}", PREFIX, urlencoding::encode(post_id)),
344 None,
345 )
346 .await
347 }
348
349 pub async fn community_update_comment(
351 &self,
352 comment_id: &str,
353 input: serde_json::Value,
354 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
355 self.client
356 .request(
357 reqwest::Method::PUT,
358 &format!("{}/comments/{}", PREFIX, urlencoding::encode(comment_id)),
359 Some(input),
360 )
361 .await
362 }
363
364 pub async fn community_delete_comment(
366 &self,
367 comment_id: &str,
368 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
369 self.client
370 .request(
371 reqwest::Method::DELETE,
372 &format!("{}/comments/{}", PREFIX, urlencoding::encode(comment_id)),
373 None,
374 )
375 .await
376 }
377
378 pub async fn community_get_stats(
380 &self,
381 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
382 self.client
383 .request(reqwest::Method::GET, &format!("{}/stats", PREFIX), None)
384 .await
385 }
386
387 pub async fn community_get_trending_tags(
389 &self,
390 limit: Option<u32>,
391 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
392 let qs = match limit {
393 Some(l) => format!("?limit={}", l),
394 None => String::new(),
395 };
396 self.client
397 .request(
398 reqwest::Method::GET,
399 &format!("{}/tags/trending{}", PREFIX, qs),
400 None,
401 )
402 .await
403 }
404
405 pub async fn community_create_battle_report(
407 &self,
408 input: serde_json::Value,
409 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
410 let mut body = input;
411 if let Some(obj) = body.as_object_mut() {
412 obj.entry("boardId").or_insert(json!("showcase"));
413 obj.entry("postType").or_insert(json!("battleReport"));
414 }
415 self.client
416 .request(reqwest::Method::POST, &format!("{}/posts", PREFIX), Some(body))
417 .await
418 }
419
420 pub async fn community_create_milestone(
422 &self,
423 input: serde_json::Value,
424 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
425 let mut body = input;
426 if let Some(obj) = body.as_object_mut() {
427 obj.entry("boardId").or_insert(json!("showcase"));
428 obj.entry("postType").or_insert(json!("milestone"));
429 }
430 self.client
431 .request(reqwest::Method::POST, &format!("{}/posts", PREFIX), Some(body))
432 .await
433 }
434
435 pub async fn community_create_gene_release(
437 &self,
438 input: serde_json::Value,
439 ) -> Result<ApiResponse<serde_json::Value>, PrismerError> {
440 let mut body = input;
441 if let Some(obj) = body.as_object_mut() {
442 obj.entry("boardId").or_insert(json!("showcase"));
443 obj.entry("postType").or_insert(json!("geneRelease"));
444 }
445 self.client
446 .request(reqwest::Method::POST, &format!("{}/posts", PREFIX), Some(body))
447 .await
448 }
449}