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 ("id", id.to_string()),
37 ("type", r#type.to_string()),
38 ("csrf", csrf),
39 ])
40 .send_bpi("点赞文章")
41 .await?;
42
43 Ok(result)
44 }
45
46 pub async fn article_coin(
58 &self,
59 aid: u64,
60 upid: u64,
61 multiply: u32,
62 ) -> Result<BpiResponse<CoinResponseData>, BpiError> {
63 let multiply = multiply.min(2);
64 let csrf = self.csrf()?;
65
66 let result = self
67 .post("https://api.bilibili.com/x/web-interface/coin/add")
68 .form(&[
69 ("aid", aid.to_string()),
70 ("upid", upid.to_string()),
71 ("multiply", multiply.to_string()),
72 ("avtype", "2".to_string()),
73 ("csrf", csrf),
74 ])
75 .send_bpi("投币文章")
76 .await?;
77
78 Ok(result)
79 }
80
81 pub async fn article_favorite(
91 &self,
92 id: u64,
93 ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
94 let csrf = self.csrf()?;
95
96 let result = self
97 .post("https://api.bilibili.com/x/article/favorites/add")
98 .form(&[("id", id.to_string()), ("csrf", csrf)])
99 .send_bpi("收藏文章")
100 .await?;
101
102 Ok(result)
103 }
104
105 pub async fn article_unfavorite(
115 &self,
116 id: i64,
117 ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
118 let csrf = self.csrf()?;
119
120 let result = self
121 .post("https://api.bilibili.com/x/article/favorites/del")
122 .form(&[("id", id.to_string()), ("csrf", csrf)])
123 .send_bpi("收藏文章")
124 .await?;
125
126 Ok(result)
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 const TEST_CVID: u64 = 1;
135 const TEST_UID: u64 = 91221505;
136 #[tokio::test]
137 async fn test_like_article() -> Result<(), Box<BpiError>> {
138 let bpi = BpiClient::new();
139 bpi.article_like(TEST_CVID, true)
140 .await
141 .map(|_| ())
142 .or_else(|e| {
143 if e.code() == Some(65006) {
144 Ok(())
145 } else {
146 Err(Box::new(e))
147 }
148 })
149 }
150
151 #[tokio::test]
152 async fn test_coin_article() -> Result<(), Box<BpiError>> {
153 let bpi = BpiClient::new();
154 let multiply = 1;
155
156 bpi.article_coin(TEST_CVID, TEST_UID, multiply)
157 .await
158 .map(|_| ())
159 .or_else(|e| {
160 if e.code() == Some(34005) {
161 Ok(())
162 } else {
163 Err(Box::new(e))
164 }
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}