Skip to content

Commit 346e88e

Browse files
committed
feat: bot add del command
1 parent ac84e7e commit 346e88e

5 files changed

Lines changed: 180 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2222
- 2023-10-13 完成开始功能选择中的「快搜教师」和「快搜评论」功能
2323
- 2023-10-20 修正下标溢出 bug
2424
- 2024-09-23 优化使用时的帮助说明
25+
- 2025-10-12 增加了 /del 命令,可以删除自己的评论
2526
- 正在进行:web 端开发重构,更多功能加入

doc/usage.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,11 @@
2626
- [@SAFC_bak_bot](https://t.me/SAFC_bak_bot)
2727
- ~~[@SAFC_bot](https://t.me/SAFC_bot)~~
2828

29+
### 机器人常用命令
30+
31+
- `/start`:启动会话并进入功能菜单。
32+
- `/find 客体 <关键字...>``/find 评价 <关键字...>`:快速检索导师或评价。
33+
- `/comment <客体ID>`:对指定客体提交新评价。
34+
- `/del <评价ID> <发布人 OTP>`:删除自己发布的评价;通过 OTP 签名校验后需回复“确认删除”完成操作。
35+
- `/downloaddb`:下载当前公开的数据库快照。
36+
- `/cancel`:随时终止当前会话。

src/db.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,28 @@ impl SAFCdb {
383383
Ok(rows.collect::<Result<Vec<_>, _>>()?)
384384
}
385385

386+
/// 根据评价 id 定位单条评价
387+
pub fn find_comment_by_id(&self, comment_id: &String) -> HandlerResult<Option<ObjComment>> {
388+
let conn = self.pool.clone().get()?;
389+
390+
let mut stmt = conn.prepare("SELECT * FROM comments WHERE id = ?")?;
391+
let mut rows = stmt.query([comment_id])?;
392+
393+
if let Some(row) = rows.next()? {
394+
Ok(Some(ObjComment {
395+
object: row.get::<_, String>(0)?,
396+
description: row.get::<_, String>(1)?,
397+
date: row.get::<_, String>(2)?,
398+
source_cate: SourceCate::from_str(row.get::<_, String>(3)?.as_str()).unwrap(),
399+
comment_type: CommentType::from_str(row.get::<_, String>(4)?.as_str()).unwrap(),
400+
author_sign: row.get::<_, String>(5).ok(),
401+
id: row.get::<_, String>(6)?,
402+
}))
403+
} else {
404+
Ok(None)
405+
}
406+
}
407+
386408
/// 查找评价 - like 方式
387409
pub fn find_comment_like(&self, s: &String) -> HandlerResult<Vec<ObjComment>> {
388410
let conn = self.pool.clone().get()?;
@@ -425,6 +447,13 @@ impl SAFCdb {
425447
Ok(())
426448
}
427449

450+
/// 删除评价
451+
pub fn delete_comment(&self, comment_id: &String) -> HandlerResult<bool> {
452+
let conn = self.pool.clone().get()?;
453+
let affected = conn.execute("DELETE FROM comments WHERE id = ?", [comment_id])?;
454+
Ok(affected > 0)
455+
}
456+
428457
/// 增加评价
429458
pub fn add_comment(&self, obj_comment: &ObjComment) -> HandlerResult<()> {
430459
let conn = self.pool.clone().get()?;

src/main.rs

Lines changed: 139 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ enum Command {
4040
Comment(String),
4141
#[command(description = "搜索")]
4242
Find(String),
43+
#[command(description = "删除评价")]
44+
Del(String),
4345
}
4446

4547
#[tokio::main]
@@ -52,7 +54,7 @@ async fn main() {
5254
bot.set_my_commands(Command::bot_commands()) // 向 telegram 注册命令
5355
.await
5456
.expect("Failed to set bot commands to telegram");
55-
57+
5658
log::info!("Bot commands have been set");
5759

5860
Dispatcher::builder(bot, schema())
@@ -78,6 +80,7 @@ fn schema() -> UpdateHandler<Box<dyn std::error::Error + Send + Sync + 'static>>
7880
.branch(case![Command::DownloadDb].endpoint(download_db_command))
7981
.branch(case![Command::Find(arg)].endpoint(find_command))
8082
.branch(case![Command::Comment(arg)].endpoint(comment_command))
83+
.branch(case![Command::Del(arg)].endpoint(delete_comment_command))
8184
.branch(dptree::endpoint(invalid_command));
8285

8386
// 文本消息
@@ -90,7 +93,8 @@ fn schema() -> UpdateHandler<Box<dyn std::error::Error + Send + Sync + 'static>>
9093
.branch(case![State::Department { school_cate, university }].endpoint(choose_supervisor))
9194
.branch(case![State::Supervisor { school_cate, university, department }].endpoint(read_or_comment))
9295
.branch(case![State::Comment { object_id, comment_type }].endpoint(add_comment))
93-
.branch(case![State::Publish { object_id, comment, comment_type }].endpoint(publish_comment));
96+
.branch(case![State::Publish { object_id, comment, comment_type }].endpoint(publish_comment))
97+
.branch(case![State::DeleteConfirm { comment_id }].endpoint(confirm_delete_comment));
9498

9599
// 消息
96100
let message_handler = Update::filter_message()
@@ -197,6 +201,90 @@ async fn find_command(bot: Bot, dialogue: MyDialogue, arg: String, msg: Message)
197201
Ok(())
198202
}
199203

204+
/// 删除评价命令
205+
async fn delete_comment_command(
206+
bot: Bot,
207+
dialogue: MyDialogue,
208+
arg: String,
209+
msg: Message,
210+
) -> HandlerResult {
211+
let parts: Vec<&str> = arg.split_whitespace().collect();
212+
213+
if parts.len() != 2 {
214+
bot.send_message(
215+
msg.chat.id,
216+
"使用方法:\n/del <评价 ID> <发布人 OTP>\n示例:/del ab12cd34 xxxxxxxx",
217+
)
218+
.reply_to_message_id(msg.id)
219+
.await?;
220+
return Ok(());
221+
}
222+
223+
let comment_id = parts[0].to_string();
224+
let otp = parts[1].to_string();
225+
226+
let comment = match SAFC_DB.find_comment_by_id(&comment_id)? {
227+
Some(c) => c,
228+
None => {
229+
bot.send_message(
230+
msg.chat.id,
231+
format!("未找到评价 `{}`。请检查评价 ID。", comment_id),
232+
)
233+
.reply_to_message_id(msg.id)
234+
.await?;
235+
return Ok(());
236+
}
237+
};
238+
239+
let author_sign = match comment.author_sign.as_ref() {
240+
Some(sign) => sign,
241+
None => {
242+
bot.send_message(
243+
msg.chat.id,
244+
"该评价未设置 OTP,暂不支持自行删除。",
245+
)
246+
.reply_to_message_id(msg.id)
247+
.await?;
248+
return Ok(());
249+
}
250+
};
251+
252+
let expected_sign = hash_author_sign(&comment_id, &otp);
253+
if author_sign != &expected_sign {
254+
bot.send_message(
255+
msg.chat.id,
256+
"签名验证失败。请确认评价 ID 与 OTP 是否正确。",
257+
)
258+
.reply_to_message_id(msg.id)
259+
.await?;
260+
return Ok(());
261+
}
262+
263+
let object_info = match SAFC_DB.find_objteacher_with_id(comment.object.as_str())? {
264+
Some(obj) => obj.display_path(),
265+
None => format!("客体 ID {}", comment.object),
266+
};
267+
268+
let comment_body = escape(comment.description.as_str());
269+
let comment_type_label = comment.comment_type.to_string();
270+
let confirm_message = format!(
271+
"评价 ID `{}`\n对象:{}\n日期:{}\n类型:{}\n\n```text\n{}\n```\n\n签名验证成功。\n请回复「确认删除」以继续,或发送 /cancel 终止此操作。",
272+
escape(comment_id.as_str()),
273+
escape(object_info.as_str()),
274+
escape(comment.date.as_str()),
275+
escape(comment_type_label.as_str()),
276+
comment_body
277+
);
278+
279+
bot.send_message(msg.chat.id, confirm_message)
280+
.reply_to_message_id(msg.id)
281+
.parse_mode(MarkdownV2)
282+
.await?;
283+
284+
dialogue.update(State::DeleteConfirm { comment_id }).await?;
285+
Ok(())
286+
}
287+
200288
/// 直接评价命令处理函数
201289
async fn comment_command(
202290
bot: Bot,
@@ -993,6 +1081,55 @@ async fn publish_comment(
9931081
Ok(())
9941082
}
9951083

1084+
/// 删除评价确认处理
1085+
async fn confirm_delete_comment(
1086+
bot: Bot,
1087+
dialogue: MyDialogue,
1088+
comment_id: String,
1089+
msg: Message,
1090+
) -> HandlerResult {
1091+
let text = match msg.text() {
1092+
Some(t) => t.trim(),
1093+
None => {
1094+
bot.send_message(
1095+
msg.chat.id,
1096+
"未识别的输入。请回复「确认删除」,或使用 /cancel 取消操作。",
1097+
)
1098+
.await?;
1099+
return Ok(());
1100+
}
1101+
};
1102+
1103+
if text != "确认删除" {
1104+
bot.send_message(
1105+
msg.chat.id,
1106+
"如需删除,请回复「确认删除」。若不再需要删除,可使用 /cancel 结束操作。",
1107+
)
1108+
.await?;
1109+
return Ok(());
1110+
}
1111+
1112+
match SAFC_DB.delete_comment(&comment_id)? {
1113+
true => {
1114+
bot.send_message(
1115+
msg.chat.id,
1116+
format!("评价 `{}` 已删除。感谢您的更新!", comment_id),
1117+
)
1118+
.await?;
1119+
}
1120+
false => {
1121+
bot.send_message(
1122+
msg.chat.id,
1123+
"评价不存在或已被删除。",
1124+
)
1125+
.await?;
1126+
}
1127+
}
1128+
1129+
dialogue.update(State::Start).await?;
1130+
Ok(())
1131+
}
1132+
9961133
/// 一维向量转换为 n 列纵向键盘
9971134
fn convert_to_n_columns_keyboard(data: Vec<String>, n: usize) -> Vec<Vec<KeyboardButton>> {
9981135
data.chunks(n)

src/msg.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ pub enum State {
9797
comment: String,
9898
comment_type: CommentType,
9999
},
100+
DeleteConfirm {
101+
comment_id: String,
102+
},
100103
/// 分页显示回调状态
101104
PagingCb {
102105
data: PagingCbData,

0 commit comments

Comments
 (0)