1use serde::{ Deserialize, Serialize };
2
3use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
4
5#[derive(Debug, Clone, Deserialize, Serialize)]
10pub struct DanmakuPostData {
11 pub colorful_src: Option<serde_json::Value>, pub dmid: u64,
13 pub dmid_str: String,
14}
15
16impl BpiClient {
17 pub async fn danmaku_send(
36 &self,
37 oid: u64,
38 msg: &str,
39 avid: Option<u64>,
40 bvid: Option<&str>,
41 mode: Option<u8>,
42 typ: Option<u8>,
43 progress: Option<u32>,
44 color: Option<u32>,
45 fontsize: Option<u8>,
46 pool: Option<u8>
47 ) -> Result<BpiResponse<DanmakuPostData>, BpiError> {
48 let csrf = self.csrf()?;
49
50 let mut form = vec![
51 ("oid", oid.to_string()),
52 ("msg", msg.to_string()),
53 ("mode", "1".to_string()),
54 ("fontsize", "25".to_string()),
55 ("color", "16777215".to_string()),
56 ("pool", "0".to_string()),
57 ("progress", "1878".to_string()),
58 ("rnd", "2".to_string()),
59 ("plat", "1".to_string()),
60 ("csrf", csrf),
61 ("checkbox_type", "0".to_string()),
62 ("colorful", "".to_string()),
63 ("gaiasource", "main_web".to_string()),
64 ("polaris_app_id", "100".to_string()),
65 ("polaris_platform", "5".to_string()),
66 ("spmid", "333.788.0.0".to_string()),
67 ("from_spmid", "333.788.0.0".to_string())
68 ];
69
70 if let Some(m) = mode {
71 form.push(("mode", m.to_string()));
72 }
73 if let Some(t) = typ {
74 form.push(("type", t.to_string()));
75 }
76 if let Some(p) = progress {
77 form.push(("progress", p.to_string()));
78 }
79 if let Some(c) = color {
80 form.push(("color", c.to_string()));
81 }
82 if let Some(f) = fontsize {
83 form.push(("fontsize", f.to_string()));
84 }
85 if let Some(p) = pool {
86 form.push(("pool", p.to_string()));
87 }
88 if let Some(b) = bvid {
89 form.push(("bvid", b.to_string()));
90 }
91 if let Some(a) = avid {
92 form.push(("avid", a.to_string()));
93 }
94
95 let signed_params = self.get_wbi_sign2(form.clone()).await?;
97
98 self
99 .post("https://api.bilibili.com/x/v2/dm/post")
100 .form(&signed_params)
101 .send_bpi("发送视频弹幕").await
102 }
103
104 pub async fn danmaku_send_default(
117 &self,
118 oid: u64,
119 msg: &str,
120 avid: Option<u64>,
121 bvid: Option<&str>
122 ) -> Result<BpiResponse<DanmakuPostData>, BpiError> {
123 let csrf = self.csrf()?;
124
125 let mut form = vec![
126 ("type", "1".to_string()),
127 ("oid", oid.to_string()),
128 ("msg", msg.to_string()),
129 ("mode", "1".to_string()),
130 ("csrf", csrf)
131 ];
132
133 if let Some(b) = bvid {
134 form.push(("bvid", b.to_string()));
135 }
136 if let Some(a) = avid {
137 form.push(("avid", a.to_string()));
138 }
139
140 let signed_form = self.get_wbi_sign2(form).await?;
142
143 self
144 .post("https://api.bilibili.com/x/v2/dm/post")
145 .form(&signed_form)
146 .send_bpi("发送视频弹幕").await
147 }
148}
149
150impl BpiClient {
155 pub async fn danmaku_recall(
168 &self,
169 cid: u64,
170 dmid: u64
171 ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
172 let csrf = self.csrf()?;
173 self
174 .post("https://api.bilibili.com/x/dm/recall")
175 .form(
176 &[
177 ("cid", &cid.to_string()),
178 ("dmid", &dmid.to_string()),
179 ("type", &"1".to_string()),
180 ("csrf", &csrf),
181 ]
182 )
183 .send_bpi("撤回弹幕").await
184 }
185}
186
187impl BpiClient {
192 pub async fn danmaku_buy_adv(
202 &self,
203 cid: u64
204 ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
205 let csrf = self.csrf()?;
206 self
207 .post("https://api.bilibili.com/x/dm/adv/buy")
208 .form(
209 &[
210 ("cid", cid.to_string()),
211 ("mode", "sp".to_string()),
212 ("csrf", csrf),
213 ]
214 )
215 .send_bpi("购买高级弹幕发送权限").await
216 }
217}
218
219#[derive(Debug, Clone, Deserialize)]
224#[serde(rename_all = "camelCase")]
225pub struct DanmakuAdvState {
226 pub coins: u8,
227 pub confirm: u8,
228 pub accept: bool,
229 pub has_buy: bool,
230}
231
232impl BpiClient {
233 pub async fn danmaku_adv_state(
243 &self,
244 cid: u64
245 ) -> Result<BpiResponse<DanmakuAdvState>, BpiError> {
246 self
247 .get("https://api.bilibili.com/x/dm/adv/state")
248 .query(
249 &[
250 ("cid", cid.to_string()),
251 ("mode", "sp".to_string()),
252 ]
253 )
254 .send_bpi("检测高级弹幕发送权限").await
255 }
256}
257
258impl BpiClient {
263 pub async fn danmaku_thumbup(
275 &self,
276 oid: u64,
277 dmid: u64,
278 op: u8
279 ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
280 let csrf = self.csrf()?;
281 let mut form = vec![
282 ("oid", oid.to_string()),
283 ("dmid", dmid.to_string()),
284 ("op", op.to_string()),
285 ("csrf", csrf)
286 ];
287
288 form.push(("platform", "web_player".to_string()));
289
290 self
291 .post("https://api.bilibili.com/x/v2/dm/thumbup/add")
292 .form(&form)
293 .send_bpi("点赞弹幕").await
294 }
295}
296
297impl BpiClient {
302 pub async fn danmaku_report(
315 &self,
316 cid: u64,
317 dmid: u64,
318 reason: u8,
319 content: Option<&str>
320 ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
321 let csrf = self.csrf()?;
322 let mut form = vec![
323 ("cid", cid.to_string()),
324 ("dmid", dmid.to_string()),
325 ("reason", reason.to_string()),
326 ("csrf", csrf)
327 ];
328
329 if let Some(c) = content {
330 form.push(("content", c.to_string()));
331 }
332
333 self.post("https://api.bilibili.com/x/dm/report/add").form(&form).send_bpi("举报弹幕").await
334 }
335}
336
337impl BpiClient {
342 pub async fn danmaku_edit_state(
354 &self,
355 oid: u64,
356 dmids: &[u64],
357 state: u8
358 ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
359 let csrf = self.csrf()?;
360 let dmids_str = dmids
361 .iter()
362 .map(|id| id.to_string())
363 .collect::<Vec<_>>()
364 .join(",");
365
366 self
367 .post("https://api.bilibili.com/x/v2/dm/edit/state")
368 .form(
369 &[
370 ("type", "1"),
371 ("oid", &oid.to_string()),
372 ("dmids", &dmids_str),
373 ("state", &state.to_string()),
374 ("csrf", &csrf),
375 ]
376 )
377 .send_bpi("保护&删除弹幕").await
378 }
379}
380
381impl BpiClient {
386 pub async fn danmaku_edit_pool(
398 &self,
399 oid: u64,
400 dmids: &[u64],
401 pool: u8
402 ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
403 let csrf = self.csrf()?;
404 let dmids_str = dmids
405 .iter()
406 .map(|id| id.to_string())
407 .collect::<Vec<_>>()
408 .join(",");
409
410 self
411 .post("https://api.bilibili.com/x/v2/dm/edit/pool")
412 .form(
413 &[
414 ("type", "1"),
415 ("oid", &oid.to_string()),
416 ("dmids", &dmids_str),
417 ("pool", &pool.to_string()),
418 ("csrf", &csrf),
419 ]
420 )
421 .send_bpi("修改字幕池").await
422 }
423}
424
425#[cfg(test)]
426mod tests {
427 use super::*;
428 use tracing::info;
429
430 #[tokio::test]
431 #[ignore]
432 async fn test_danmaku_post() {
433 let bpi = BpiClient::new();
434
435 let resp = bpi.danmaku_send(
436 413195701,
437 "测试22",
438 Some(590635620),
439 None,
440 None,
441 None,
442 None,
443 None,
444 None,
445 None
446 ).await;
447 info!("{:#?}", resp);
448 assert!(resp.is_ok());
449 info!("dmid{}", resp.unwrap().data.unwrap().dmid);
450 }
451
452 #[tokio::test]
453 #[ignore]
454 async fn test_danmaku_recall() {
455 let bpi = BpiClient::new();
456
457 let resp = bpi.danmaku_recall(413195701, 1932013422544416768).await;
458 info!("{:#?}", resp);
459 assert!(resp.is_ok());
460 }
461
462 #[tokio::test]
463 #[ignore]
464 async fn test_danmaku_buy_adv() {
465 let bpi = BpiClient::new();
466
467 let resp = bpi.danmaku_buy_adv(413195701).await;
468 info!("{:#?}", resp);
469 assert!(resp.is_ok());
470 }
471
472 #[tokio::test]
473 #[ignore]
474 async fn test_danmaku_get_adv_state() {
475 let bpi = BpiClient::new();
476
477 let resp = bpi.danmaku_adv_state(413195701).await;
478 info!("{:#?}", resp);
479 assert!(resp.is_ok());
480 }
481
482 #[tokio::test]
483 #[ignore]
484 async fn test_danmaku_thumbup() {
485 let bpi = BpiClient::new();
486
487 let resp = bpi.danmaku_thumbup(413195701, 1932011031958944000, 1).await;
488 info!("{:#?}", resp);
489 assert!(resp.is_ok());
490 }
491
492 #[tokio::test]
493 #[ignore]
494 async fn test_danmaku_edit_state() {
495 let bpi = BpiClient::new();
496
497 let dmids = vec![1932011031958944000];
498 let resp = bpi.danmaku_edit_state(413195701, &dmids, 1).await;
499 info!("{:#?}", resp);
500 assert!(resp.is_ok());
501 }
502
503 #[tokio::test]
504 #[ignore]
505 async fn test_danmaku_edit_pool() {
506 let bpi = BpiClient::new();
507
508 let dmids = vec![1932011031958944000];
509 let resp = bpi.danmaku_edit_pool(413195701, &dmids, 1).await;
510 info!("{:#?}", resp);
511 assert!(resp.is_ok());
512 }
513}