bilibili_msg_history/
lib.rs1use chrono::Local;
2use models::message::{ImageMessage, Message, ShareMessage, TextMessage, ViewerKind};
3use models::response::{BilibiliResponse, ResponseData};
4use reqwest::blocking::Client;
5use reqwest::header::HeaderMap;
6use std::fs;
7use std::io::Write;
8use std::path::PathBuf;
9use std::time::Duration;
10
11pub mod api;
12pub mod models;
13
14pub fn format_message(message: &Message) -> Result<String, String> {
19 match message.msg_type {
20 1 => {
22 let deserialized = serde_json::from_str::<TextMessage>(&message.content)
23 .map_err(|err| format!("反序列化 [文本消息 (TextMessage)] 时发生错误: {}", err))?;
24
25 Ok(deserialized.into())
26 }
27 2 => {
29 let deserialized = serde_json::from_str::<ImageMessage>(&message.content)
30 .map_err(|err| format!("反序列化 [文本消息 (TextMessage)] 时发生错误: {}", err))?;
31
32 Ok(deserialized.into())
33 }
34 7 => {
36 let deserialized = serde_json::from_str::<ShareMessage>(&message.content)
37 .map_err(|err| format!("反序列化 [分享消息 (ShareMessage)] 时发生错误: {}", err))?;
38
39 Ok(deserialized.into())
40 }
41 _ => Ok("[其他消息]".to_owned()),
43 }
44}
45
46pub fn person_nickname<'a>(
47 viewer: ViewerKind,
48 message: &'a Message,
49 nickname: &'a str,
50 talker_uid: u64,
51) -> &'a str {
52 match viewer {
53 ViewerKind::FirstPerson => {
54 if message.sender_uid == talker_uid {
55 return nickname;
56 }
57
58 return "我";
59 }
60 ViewerKind::ThirdPerson => {
61 if message.receiver_id == talker_uid {
62 return nickname;
63 }
64
65 return "我";
66 }
67 }
68}
69
70#[deprecated(since = "0.0.2", note = "下个版本不在使用,将会被 Args 结构体代替")]
71#[derive(Debug, Default)]
72pub struct Config {
73 pub cookie: String,
74 pub talker_uid: u64,
75 pub size: u32,
76 pub viewer_kind: ViewerKind,
77 pub talker_nickname: String,
78 pub save_path: String,
79}
80
81pub fn run(config: Option<Config>) -> Result<(), String> {
82 let config = config.ok_or("缺省配置")?;
83
84 let size = config.size;
85 let talker_uid = config.talker_uid;
86 let cookie = config.cookie;
88 let viewer = config.viewer_kind;
89 let talker_nickname = config.talker_nickname;
90
91 let mut headers = HeaderMap::new();
92 headers.insert(
93 "Cookie",
94 cookie
95 .parse()
96 .map_err(|err| format!("解析 Cookie 时发生错误: {}", err))?,
97 );
98
99 let bilibili_response = api::get_latest_msg_api(headers.clone(), talker_uid)
101 .map_err(|err| format!("反序列化时发生错误: {}", err))?;
102 let mut mutable_end_seqno = bilibili_response.data.max_seqno;
104 #[allow(unused)]
106 let timestamp = bilibili_response
107 .data
108 .messages
109 .map(|messages| {
110 messages
111 .iter()
112 .next()
113 .map_or(0, |message| message.timestamp)
114 })
115 .unwrap_or_default();
116
117 let datetime = Local::now().format("%Y-%m-%d").to_string();
119 let filepath = format!("{}/{}.txt", config.save_path, datetime);
120 let path_buf = PathBuf::from(filepath);
121
122 if fs::exists(&path_buf).map_err(|err| format!("检查文件是否存在时发生错误: {}", err))?
123 {
124 fs::remove_file(&path_buf).map_err(|err| format!("删除文件时发生错误: {}", err))?;
125 }
126
127 let mut file =
128 fs::File::create(&path_buf).map_err(|err| format!("创建文件时发生错误: {}", err))?;
129
130 println!("Start getting the message data");
131
132 loop {
133 let bilibili_response =
134 api::get_message_collect_api(headers.clone(), talker_uid, size, mutable_end_seqno)
135 .map_err(|err| format!("反序列化时发生错误: {}", err))?;
136
137 if bilibili_response.code != 0 {
138 break;
139 }
140
141 if bilibili_response.data.has_more == 0 {
144 if let Some(messages) = bilibili_response.data.messages {
145 messages.iter().for_each(|message| {
146 let nickname = person_nickname(viewer, message, &talker_nickname, talker_uid);
148 let formatted_message = format_message(&message).unwrap();
149 let message_text = format!(
150 "[{}]{}: {}\n",
151 message.datetime(),
152 nickname,
153 formatted_message,
154 );
155
156 if let Err(err) = file.write(message_text.as_bytes()) {
157 eprintln!("消息写入时发生错误: {}", err);
158 }
159 });
160 }
161
162 break;
163 }
164
165 if let Some(messages) = bilibili_response.data.messages {
166 messages.iter().for_each(|message| {
167 let nickname = person_nickname(viewer, message, &talker_nickname, talker_uid);
169 let message_text = format!(
170 "[{}]{}: {}\n",
171 message.datetime(),
172 nickname,
173 format_message(&message).unwrap()
174 );
175
176 if let Err(err) = file.write(message_text.as_bytes()) {
177 eprintln!("消息写入时发生错误: {}", err);
178 }
179 });
180 }
181
182 mutable_end_seqno = bilibili_response.data.min_seqno - 1;
183
184 std::thread::sleep(Duration::from_millis(500));
185 }
186
187 println!("Done.");
188
189 Ok(())
190}