1use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
6
7#[derive(Debug, Clone, serde::Deserialize)]
9pub struct CoinResponseData {
10 pub like: bool,
12}
13
14impl BpiClient {
15 pub async fn article_like(
26 &self,
27 id: u64,
28 like: bool
29 ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
30 let csrf = self.csrf()?;
31 let r#type = if like { 1 } else { 2 };
32
33 let result = self
34 .post("https://api.bilibili.com/x/article/like")
35 .form(
36 &[
37 ("id", id.to_string()),
38 ("type", r#type.to_string()),
39 ("csrf", csrf),
40 ]
41 )
42 .send_bpi("点赞文章").await?;
43
44 Ok(result)
45 }
46
47 pub async fn article_coin(
59 &self,
60 aid: u64,
61 upid: u64,
62 multiply: u32
63 ) -> Result<BpiResponse<CoinResponseData>, BpiError> {
64 let multiply = multiply.min(2);
65 let csrf = self.csrf()?;
66
67 let result = self
68 .post("https://api.bilibili.com/x/web-interface/coin/add")
69 .form(
70 &[
71 ("aid", aid.to_string()),
72 ("upid", upid.to_string()),
73 ("multiply", multiply.to_string()),
74 ("avtype", "2".to_string()),
75 ("csrf", csrf),
76 ]
77 )
78 .send_bpi("投币文章").await?;
79
80 Ok(result)
81 }
82
83 pub async fn article_favorite(
93 &self,
94 id: u64
95 ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
96 let csrf = self.csrf()?;
97
98 let result = self
99 .post("https://api.bilibili.com/x/article/favorites/add")
100 .form(
101 &[
102 ("id", id.to_string()),
103 ("csrf", csrf),
104 ]
105 )
106 .send_bpi("收藏文章").await?;
107
108 Ok(result)
109 }
110
111 pub async fn article_unfavorite(
121 &self,
122 id: i64
123 ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
124 let csrf = self.csrf()?;
125
126 let result = self
127 .post("https://api.bilibili.com/x/article/favorites/del")
128 .form(
129 &[
130 ("id", id.to_string()),
131 ("csrf", csrf),
132 ]
133 )
134 .send_bpi("收藏文章").await?;
135
136 Ok(result)
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 const TEST_CVID: u64 = 1;
145 const TEST_UID: u64 = 91221505;
146 #[tokio::test]
147 async fn test_like_article() -> Result<(), Box<BpiError>> {
148 let bpi = BpiClient::new();
149 bpi.article_like(TEST_CVID, true).await
150 .map(|_| ())
151 .or_else(|e| {
152 if e.code() == Some(65006) { Ok(()) } else { Err(Box::new(e)) }
153 })
154 }
155
156 #[tokio::test]
157 async fn test_coin_article() -> Result<(), Box<BpiError>> {
158 let bpi = BpiClient::new();
159 let multiply = 1;
160
161 bpi.article_coin(TEST_CVID, TEST_UID, multiply).await
162 .map(|_| ())
163 .or_else(|e| {
164 if e.code() == Some(34005) { Ok(()) } else { Err(Box::new(e)) }
165 })
166 }
167
168 #[tokio::test]
169 async fn test_favorite_article() -> Result<(), Box<BpiError>> {
170 let bpi = BpiClient::new();
171
172 bpi.article_favorite(TEST_CVID).await?;
173 Ok(())
174 }
175}