use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
use serde::{ Deserialize, Serialize };
#[derive(Debug, Clone, Copy, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum CommentType {
Video = 1, Article = 12, Dynamic = 17, Unknown = 0,
}
#[derive(Debug, Clone, Copy, Serialize)]
pub enum ReportReason {
Other = 0,
Ad = 1,
Porn = 2,
Spam = 3,
Flame = 4,
Spoiler = 5,
Politics = 6,
Abuse = 7,
Irrelevant = 8,
Illegal = 9,
Vulgar = 10,
Phishing = 11,
Scam = 12,
Rumor = 13,
Incitement = 14,
Privacy = 15,
FloorSnatching = 16,
HarmfulToYouth = 17,
}
#[derive(Debug, Serialize, Clone, Deserialize)]
pub struct CommentData {
pub rpid: u64,
pub rpid_str: String,
pub root: u64,
pub root_str: String,
pub parent: u64,
pub parent_str: String,
pub dialog: u64,
pub dialog_str: String,
pub success_toast: Option<String>,
}
impl BpiClient {
pub async fn comment_add(
&self,
r#type: CommentType,
oid: u64,
message: &str,
root: Option<u64>,
parent: Option<u64>
) -> Result<BpiResponse<CommentData>, BpiError> {
let csrf = self.csrf()?;
let mut params = vec![
("type", (r#type as u32).to_string()),
("oid", oid.to_string()),
("message", message.to_string()),
("plat", "1".to_string()), ("csrf", csrf.to_string())
];
if let Some(r) = root {
params.push(("root", r.to_string()));
}
if let Some(p) = parent {
params.push(("parent", p.to_string()));
}
self
.post("https://api.bilibili.com/x/v2/reply/add")
.form(¶ms)
.send_bpi("发表评论").await
}
pub async fn comment_like(
&self,
r#type: CommentType,
oid: u64,
rpid: u64,
action: u8
) -> Result<BpiResponse<serde_json::Value>, BpiError> {
let csrf = self.csrf()?;
let params = [
("type", (r#type as u32).to_string()),
("oid", oid.to_string()),
("rpid", rpid.to_string()),
("action", action.to_string()), ("csrf", csrf.to_string()),
];
self
.post("https://api.bilibili.com/x/v2/reply/action")
.form(¶ms)
.send_bpi("点赞评论").await
}
pub async fn comment_dislike(
&self,
r#type: CommentType,
oid: u64,
rpid: u64,
action: u8
) -> Result<BpiResponse<serde_json::Value>, BpiError> {
let csrf = self.csrf()?;
let params = [
("type", (r#type as u32).to_string()),
("oid", oid.to_string()),
("rpid", rpid.to_string()),
("action", action.to_string()), ("csrf", csrf.to_string()),
];
self
.post("https://api.bilibili.com/x/v2/reply/hate")
.form(¶ms)
.send_bpi("点踩评论").await
}
pub async fn comment_delete(
&self,
r#type: CommentType,
oid: u64,
rpid: u64
) -> Result<BpiResponse<serde_json::Value>, BpiError> {
let csrf = self.csrf()?;
let params = [
("type", (r#type as u32).to_string()),
("oid", oid.to_string()),
("rpid", rpid.to_string()),
("csrf", csrf.to_string()),
];
self
.post("https://api.bilibili.com/x/v2/reply/del")
.form(¶ms)
.send_bpi("删除评论").await
}
pub async fn comment_top(
&self,
r#type: CommentType,
oid: u64,
rpid: u64,
action: u8
) -> Result<BpiResponse<serde_json::Value>, BpiError> {
let csrf = self.csrf()?;
let params = [
("type", (r#type as u32).to_string()),
("oid", oid.to_string()),
("rpid", rpid.to_string()),
("action", action.to_string()), ("csrf", csrf.to_string()),
];
self
.post("https://api.bilibili.com/x/v2/reply/top")
.form(¶ms)
.send_bpi("置顶评论").await
}
pub async fn comment_report(
&self,
r#type: CommentType,
oid: u64,
rpid: u64,
reason: ReportReason,
content: Option<&str>
) -> Result<BpiResponse<serde_json::Value>, BpiError> {
let csrf = self.csrf()?;
let mut params = vec![
("type", (r#type as u32).to_string()),
("oid", oid.to_string()),
("rpid", rpid.to_string()),
("reason", (reason as u32).to_string()),
("csrf", csrf)
];
if let Some(c) = content {
params.push(("content", c.to_string()));
}
self
.post("https://api.bilibili.com/x/v2/reply/report")
.form(¶ms)
.send_bpi("举报评论").await
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
use std::time::{ SystemTime, UNIX_EPOCH };
use tokio::time;
const TEST_AID: u64 = 851944245;
async fn add_test_comment() -> Result<u64, BpiError> {
let bpi = BpiClient::new();
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
let random_secs = (now % 1_000_000) + 1_600_000_000;
let resp = bpi.comment_add(
CommentType::Video,
TEST_AID,
&random_secs.to_string(),
None,
None
).await?;
let data = resp.into_data()?;
Ok(data.rpid)
}
async fn delete_test_comment(rpid: u64) -> Result<(), BpiError> {
let bpi = BpiClient::new();
bpi.comment_delete(CommentType::Video, TEST_AID, rpid).await?;
Ok(())
}
#[tokio::test]
async fn test_comment_like() -> Result<(), BpiError> {
let rpid = add_test_comment().await?;
time::sleep(Duration::from_secs(3)).await;
let bpi = BpiClient::new();
let resp = bpi.comment_like(CommentType::Video, TEST_AID, rpid, 1).await?;
assert_eq!(resp.code, 0);
time::sleep(Duration::from_secs(3)).await;
delete_test_comment(rpid).await?;
Ok(())
}
#[tokio::test]
async fn test_comment_dislike() -> Result<(), BpiError> {
let rpid = add_test_comment().await?;
time::sleep(Duration::from_secs(3)).await;
let bpi = BpiClient::new();
let resp = bpi.comment_dislike(CommentType::Video, TEST_AID, rpid, 1).await?;
assert_eq!(resp.code, 0);
time::sleep(Duration::from_secs(3)).await;
delete_test_comment(rpid).await?;
Ok(())
}
}