fix(core): 🐛 优化数据库结构并添加用户状态标记
在 Telegram Bot 核心逻辑中,为消息表增加了 topic_message_id 字段以支持 话题模式。重构了 Telegram API 请求封装逻辑,增强了错误处理能力。 同时在文档中增加了关于“用户屏蔽 Bot”的常见问题说明。系统现在可以自动 检测用户屏蔽状态,并在管理界面展示屏蔽标记,当用户重新互动时会自动清 除该标记。
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
# Telegram Bot Worker
|
yi'xia# Telegram Bot Worker
|
||||||
|
|
||||||
`tg-bot.js` 是一个部署在 Cloudflare Workers 上的 Telegram 双向私聊中继 Bot(版本 v3.68)。它会把用户私聊消息转发到管理员论坛群的独立 topic 中,管理员在对应 topic 内回复即可把消息发回用户,同时提供验证、过滤、封禁、备注、自动回复、备份和 Telegram 内联管理面板。
|
`tg-bot.js` 是一个部署在 Cloudflare Workers 上的 Telegram 双向私聊中继 Bot(版本 v3.68)。它会把用户私聊消息转发到管理员论坛群的独立 topic 中,管理员在对应 topic 内回复即可把消息发回用户,同时提供验证、过滤、封禁、备注、自动回复、备份和 Telegram 内联管理面板。
|
||||||
|
|
||||||
@@ -240,6 +240,36 @@ curl -X POST "https://api.telegram.org/bot<BOT_TOKEN>/setWebhook" \
|
|||||||
- **引用块**:以 `>`、`》` 或 `>` 开头的文本会被渲染为 HTML 引用块
|
- **引用块**:以 `>`、`》` 或 `>` 开头的文本会被渲染为 HTML 引用块
|
||||||
- **媒体欢迎语**:管理员可以在面板中上传图片/视频/GIF 作为欢迎语
|
- **媒体欢迎语**:管理员可以在面板中上传图片/视频/GIF 作为欢迎语
|
||||||
|
|
||||||
|
### ⚠️ 常见问题:用户屏蔽 Bot
|
||||||
|
|
||||||
|
如果管理员回复时收到错误提示:**"⚠️ 用户已屏蔽 Bot"**,说明该用户已在 Telegram 中屏蔽了机器人。
|
||||||
|
|
||||||
|
**症状:**
|
||||||
|
- 管理员可以正常接收用户消息
|
||||||
|
- 管理员回复时显示 "用户已屏蔽 Bot" 错误
|
||||||
|
- 用户收不到管理员的回复
|
||||||
|
|
||||||
|
**原因:**
|
||||||
|
用户在 Telegram 中点击了"屏蔽机器人"(Block Bot),导致 Bot 无法再主动发消息给用户。即使用户之前发送过消息,屏蔽后 Bot 也无法回复。
|
||||||
|
|
||||||
|
**自动标记功能:**
|
||||||
|
系统会自动检测并标记被屏蔽的用户:
|
||||||
|
- 当检测到用户屏蔽 Bot 时,会自动在用户卡片上显示屏蔽状态和时间
|
||||||
|
- 用户卡片会显示 `⛔ 用户屏蔽Bot: 是 (时间)`
|
||||||
|
- 当用户重新发送消息或管理员解封时,自动清除屏蔽标记
|
||||||
|
|
||||||
|
**解决方案:**
|
||||||
|
需要通过其他方式联系该用户,让其按以下步骤解除屏蔽:
|
||||||
|
1. 打开与机器人的聊天窗口
|
||||||
|
2. 点击右上角菜单(三个点或机器人名称)
|
||||||
|
3. 选择"解除屏蔽"或"Unblock bot"
|
||||||
|
4. 重新发送 `/start` 命令激活机器人
|
||||||
|
|
||||||
|
**预防措施:**
|
||||||
|
- 在欢迎语中提醒用户不要屏蔽机器人
|
||||||
|
- 定期检查被屏蔽的用户列表(查看用户卡片上的屏蔽状态)
|
||||||
|
- 对于重要用户,建议通过其他渠道保持联系
|
||||||
|
|
||||||
## 安全建议
|
## 安全建议
|
||||||
|
|
||||||
### 🔐 认证与授权
|
### 🔐 认证与授权
|
||||||
|
|||||||
@@ -176,9 +176,8 @@ async function dbInit(env) {
|
|||||||
await env.TG_BOT_DB.batch([
|
await env.TG_BOT_DB.batch([
|
||||||
env.TG_BOT_DB.prepare(`CREATE TABLE IF NOT EXISTS config (key TEXT PRIMARY KEY, value TEXT)`),
|
env.TG_BOT_DB.prepare(`CREATE TABLE IF NOT EXISTS config (key TEXT PRIMARY KEY, value TEXT)`),
|
||||||
env.TG_BOT_DB.prepare(`CREATE TABLE IF NOT EXISTS users (user_id TEXT PRIMARY KEY, user_state TEXT DEFAULT 'new', is_blocked INTEGER DEFAULT 0, block_count INTEGER DEFAULT 0, first_message_sent INTEGER DEFAULT 0, topic_id TEXT, user_info_json TEXT)`),
|
env.TG_BOT_DB.prepare(`CREATE TABLE IF NOT EXISTS users (user_id TEXT PRIMARY KEY, user_state TEXT DEFAULT 'new', is_blocked INTEGER DEFAULT 0, block_count INTEGER DEFAULT 0, first_message_sent INTEGER DEFAULT 0, topic_id TEXT, user_info_json TEXT)`),
|
||||||
env.TG_BOT_DB.prepare(`CREATE TABLE IF NOT EXISTS messages (user_id TEXT, message_id TEXT, text TEXT, date INTEGER, PRIMARY KEY (user_id, message_id))`)
|
env.TG_BOT_DB.prepare(`CREATE TABLE IF NOT EXISTS messages (user_id TEXT, message_id TEXT, text TEXT, date INTEGER, topic_message_id TEXT, PRIMARY KEY (user_id, message_id))`)
|
||||||
]);
|
]);
|
||||||
try { await sql(env, "ALTER TABLE messages ADD COLUMN topic_message_id TEXT"); } catch (e) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 4. 业务逻辑 (核心流) ---
|
// --- 4. 业务逻辑 (核心流) ---
|
||||||
@@ -268,20 +267,20 @@ async function handleUserDelete(msg, u, env) {
|
|||||||
const targetMsgIdRaw = msg.reply_to_message.message_id;
|
const targetMsgIdRaw = msg.reply_to_message.message_id;
|
||||||
const targetMsgId = targetMsgIdRaw.toString();
|
const targetMsgId = targetMsgIdRaw.toString();
|
||||||
console.log(`Delete request: user=${u.user_id}, target_msg_raw=${targetMsgIdRaw} (type: ${typeof targetMsgIdRaw}), target_msg_str=${targetMsgId}`);
|
console.log(`Delete request: user=${u.user_id}, target_msg_raw=${targetMsgIdRaw} (type: ${typeof targetMsgIdRaw}), target_msg_str=${targetMsgId}`);
|
||||||
|
|
||||||
// 查询对应的管理员侧消息ID - 尝试多种可能的格式
|
// 查询对应的管理员侧消息ID - 尝试多种可能的格式
|
||||||
let ref = await sql(env, "SELECT topic_message_id FROM messages WHERE user_id=? AND message_id=?", [u.user_id, targetMsgId], 'first');
|
let ref = await sql(env, "SELECT topic_message_id FROM messages WHERE user_id=? AND message_id=?", [u.user_id, targetMsgId], 'first');
|
||||||
|
|
||||||
// 如果没找到,尝试用整数查询(以防数据库中存的是数字)
|
// 如果没找到,尝试用整数查询(以防数据库中存的是数字)
|
||||||
if (!ref || !ref.topic_message_id) {
|
if (!ref || !ref.topic_message_id) {
|
||||||
console.log(`First query failed, trying with integer...`);
|
console.log(`First query failed, trying with integer...`);
|
||||||
ref = await sql(env, "SELECT topic_message_id FROM messages WHERE user_id=? AND message_id=?", [u.user_id, parseInt(targetMsgId)], 'first');
|
ref = await sql(env, "SELECT topic_message_id FROM messages WHERE user_id=? AND message_id=?", [u.user_id, parseInt(targetMsgId)], 'first');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ref || !ref.topic_message_id) {
|
if (!ref || !ref.topic_message_id) {
|
||||||
console.log(`Delete failed: No mapping found for user=${u.user_id}, msg=${targetMsgId}`);
|
console.log(`Delete failed: No mapping found for user=${u.user_id}, msg=${targetMsgId}`);
|
||||||
console.log(`Tip: Check database records with: SELECT * FROM messages WHERE user_id='${u.user_id}'`);
|
console.log(`Tip: Check database records with: SELECT * FROM messages WHERE user_id='${u.user_id}'`);
|
||||||
|
|
||||||
// 帮助用户排查:列出该用户的最近5条消息记录
|
// 帮助用户排查:列出该用户的最近5条消息记录
|
||||||
try {
|
try {
|
||||||
const recentMsgs = await sql(env, "SELECT message_id, topic_message_id, text FROM messages WHERE user_id=? ORDER BY date DESC LIMIT 5", [u.user_id], 'all');
|
const recentMsgs = await sql(env, "SELECT message_id, topic_message_id, text FROM messages WHERE user_id=? ORDER BY date DESC LIMIT 5", [u.user_id], 'all');
|
||||||
@@ -291,7 +290,7 @@ async function handleUserDelete(msg, u, env) {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(`Failed to fetch recent messages:`, e.message);
|
console.log(`Failed to fetch recent messages:`, e.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return api(env.BOT_TOKEN, "sendMessage", {
|
return api(env.BOT_TOKEN, "sendMessage", {
|
||||||
chat_id: u.user_id,
|
chat_id: u.user_id,
|
||||||
text: "❌ 未找到对应的消息记录,可能该消息未被转发或已被删除",
|
text: "❌ 未找到对应的消息记录,可能该消息未被转发或已被删除",
|
||||||
@@ -300,14 +299,14 @@ async function handleUserDelete(msg, u, env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Delete success: Found mapping topic_msg=${ref.topic_message_id}`);
|
console.log(`Delete success: Found mapping topic_msg=${ref.topic_message_id}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. 删除被回复的目标消息(先删目标,再删命令)
|
// 1. 删除被回复的目标消息(先删目标,再删命令)
|
||||||
await api(env.BOT_TOKEN, "deleteMessage", {
|
await api(env.BOT_TOKEN, "deleteMessage", {
|
||||||
chat_id: u.user_id,
|
chat_id: u.user_id,
|
||||||
message_id: parseInt(targetMsgId)
|
message_id: parseInt(targetMsgId)
|
||||||
}).catch((e) => console.log("Failed to delete target msg:", e.message));
|
}).catch((e) => console.log("Failed to delete target msg:", e.message));
|
||||||
|
|
||||||
// 2. 删除用户侧的 /del 命令消息
|
// 2. 删除用户侧的 /del 命令消息
|
||||||
await api(env.BOT_TOKEN, "deleteMessage", {
|
await api(env.BOT_TOKEN, "deleteMessage", {
|
||||||
chat_id: u.user_id,
|
chat_id: u.user_id,
|
||||||
@@ -326,7 +325,7 @@ async function handleUserDelete(msg, u, env) {
|
|||||||
// 4. 清理数据库记录
|
// 4. 清理数据库记录
|
||||||
await sql(env, "DELETE FROM messages WHERE user_id=? AND message_id=?", [u.user_id, targetMsgId]);
|
await sql(env, "DELETE FROM messages WHERE user_id=? AND message_id=?", [u.user_id, targetMsgId]);
|
||||||
console.log(`Delete completed: Cleaned up database record`);
|
console.log(`Delete completed: Cleaned up database record`);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("User Delete Failed:", e);
|
console.error("User Delete Failed:", e);
|
||||||
await api(env.BOT_TOKEN, "sendMessage", {
|
await api(env.BOT_TOKEN, "sendMessage", {
|
||||||
@@ -360,10 +359,10 @@ async function handleAdminDelete(msg, env, delCmd = parseDelCommand(msg.text ||
|
|||||||
}
|
}
|
||||||
|
|
||||||
const targetTopicMsgId = msg.reply_to_message.message_id;
|
const targetTopicMsgId = msg.reply_to_message.message_id;
|
||||||
|
|
||||||
// 查询对应的用户侧消息ID
|
// 查询对应的用户侧消息ID
|
||||||
const ref = await sql(env, "SELECT user_id, message_id FROM messages WHERE topic_message_id=?", targetTopicMsgId.toString(), 'first');
|
const ref = await sql(env, "SELECT user_id, message_id FROM messages WHERE topic_message_id=?", targetTopicMsgId.toString(), 'first');
|
||||||
|
|
||||||
if (!ref) {
|
if (!ref) {
|
||||||
return api(env.BOT_TOKEN, "sendMessage", {
|
return api(env.BOT_TOKEN, "sendMessage", {
|
||||||
chat_id: msg.chat.id,
|
chat_id: msg.chat.id,
|
||||||
@@ -378,7 +377,7 @@ async function handleAdminDelete(msg, env, delCmd = parseDelCommand(msg.text ||
|
|||||||
chat_id: msg.chat.id,
|
chat_id: msg.chat.id,
|
||||||
message_id: msg.message_id
|
message_id: msg.message_id
|
||||||
}).catch(() => {});
|
}).catch(() => {});
|
||||||
|
|
||||||
await api(env.BOT_TOKEN, "deleteMessage", {
|
await api(env.BOT_TOKEN, "deleteMessage", {
|
||||||
chat_id: msg.chat.id,
|
chat_id: msg.chat.id,
|
||||||
message_id: targetTopicMsgId
|
message_id: targetTopicMsgId
|
||||||
@@ -392,7 +391,7 @@ async function handleAdminDelete(msg, env, delCmd = parseDelCommand(msg.text ||
|
|||||||
|
|
||||||
// 3. 清理数据库记录
|
// 3. 清理数据库记录
|
||||||
await sql(env, "DELETE FROM messages WHERE user_id=? AND message_id=?", [ref.user_id, ref.message_id]);
|
await sql(env, "DELETE FROM messages WHERE user_id=? AND message_id=?", [ref.user_id, ref.message_id]);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Admin Delete Failed:", e);
|
console.error("Admin Delete Failed:", e);
|
||||||
await api(env.BOT_TOKEN, "sendMessage", {
|
await api(env.BOT_TOKEN, "sendMessage", {
|
||||||
@@ -649,6 +648,31 @@ async function sendStart(id, msg, env) {
|
|||||||
// 正常态用户消息防线:敏感词与类型拦截器
|
// 正常态用户消息防线:敏感词与类型拦截器
|
||||||
async function handleVerifiedMsg(msg, u, env) {
|
async function handleVerifiedMsg(msg, u, env) {
|
||||||
const id = u.user_id, text = msg.text || "";
|
const id = u.user_id, text = msg.text || "";
|
||||||
|
|
||||||
|
// 如果用户之前被标记为屏蔽 Bot,但现在能发消息,说明已解除屏蔽
|
||||||
|
if (u.user_info && u.user_info.bot_blocked) {
|
||||||
|
u.user_info.bot_blocked = false;
|
||||||
|
delete u.user_info.bot_blocked_ts;
|
||||||
|
await updUser(id, { user_info: u.user_info }, env);
|
||||||
|
console.log(`Cleared bot_blocked mark for user ${id} after receiving message`);
|
||||||
|
|
||||||
|
// 更新用户卡片显示
|
||||||
|
try {
|
||||||
|
if (u.topic_id && u.user_info.card_msg_id) {
|
||||||
|
const mockTgUser = { id: id, username: u.user_info.username || "", first_name: u.user_info.name || "(未获取)", last_name: "" };
|
||||||
|
const newMeta = getUMeta(mockTgUser, u, u.user_info.join_date || (Date.now()/1000));
|
||||||
|
await api(env.BOT_TOKEN, "editMessageCaption", {
|
||||||
|
chat_id: env.ADMIN_GROUP_ID,
|
||||||
|
message_id: u.user_info.card_msg_id,
|
||||||
|
caption: newMeta.card,
|
||||||
|
parse_mode: "HTML",
|
||||||
|
reply_markup: getBtns(id, u.is_blocked, newMeta.username)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Failed to update card after bot_blocked cleared:", e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 敏感词屏蔽预检系统
|
// 敏感词屏蔽预检系统
|
||||||
if (text) {
|
if (text) {
|
||||||
@@ -1072,17 +1096,49 @@ async function handleAdminReply(msg, env) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const uid = (await sql(env, "SELECT user_id FROM users WHERE topic_id = ?", msg.message_thread_id.toString(), 'first'))?.user_id;
|
const topicIdStr = msg.message_thread_id.toString();
|
||||||
if (!uid) return;
|
console.log(`Admin reply debug: topic_id=${topicIdStr}, admin_msg_id=${msg.message_id}`);
|
||||||
|
|
||||||
|
const userRef = await sql(env, "SELECT user_id FROM users WHERE topic_id = ?", topicIdStr, 'first');
|
||||||
|
console.log(`Admin reply debug: userRef=`, userRef);
|
||||||
|
|
||||||
|
const uid = userRef?.user_id;
|
||||||
|
if (!uid) {
|
||||||
|
console.error(`Admin reply failed: No user found for topic_id=${topicIdStr}`);
|
||||||
|
return api(env.BOT_TOKEN, "sendMessage", {
|
||||||
|
chat_id: msg.chat.id,
|
||||||
|
message_thread_id: msg.message_thread_id,
|
||||||
|
text: `❌ 系统错误:未找到关联的用户(topic_id=${topicIdStr})`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查用户状态
|
||||||
|
const u = await getUser(uid, env);
|
||||||
|
console.log(`Admin reply debug: user_state=${u.user_state}, is_blocked=${u.is_blocked}`);
|
||||||
|
|
||||||
|
if (u.is_blocked) {
|
||||||
|
return api(env.BOT_TOKEN, "sendMessage", {
|
||||||
|
chat_id: msg.chat.id,
|
||||||
|
message_thread_id: msg.message_thread_id,
|
||||||
|
text: `❌ 该用户已被屏蔽,无法发送消息`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let replyToMsgId = undefined;
|
let replyToMsgId = undefined;
|
||||||
if (msg.reply_to_message) {
|
if (msg.reply_to_message) {
|
||||||
const ref = await sql(env, "SELECT message_id FROM messages WHERE topic_message_id = ?", msg.reply_to_message.message_id.toString(), 'first');
|
const ref = await sql(env, "SELECT message_id FROM messages WHERE topic_message_id = ?", msg.reply_to_message.message_id.toString(), 'first');
|
||||||
if (ref) replyToMsgId = ref.message_id;
|
if (ref) {
|
||||||
|
replyToMsgId = ref.message_id;
|
||||||
|
console.log(`Admin reply debug: Found reply mapping topic_msg=${msg.reply_to_message.message_id} -> user_msg=${replyToMsgId}`);
|
||||||
|
} else {
|
||||||
|
console.log(`Admin reply debug: No reply mapping found for topic_msg=${msg.reply_to_message.message_id}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log(`Admin reply debug: Delivering message to uid=${uid}, replyToMsgId=${replyToMsgId}`);
|
||||||
const sent = await deliverAdminMessageToUser(msg, uid, replyToMsgId, env);
|
const sent = await deliverAdminMessageToUser(msg, uid, replyToMsgId, env);
|
||||||
|
console.log(`Admin reply debug: Delivery success, sent.message_id=${sent?.message_id}`);
|
||||||
if (sent && sent.message_id) {
|
if (sent && sent.message_id) {
|
||||||
const storeText = msg.text || msg.caption || "[Admin Message]";
|
const storeText = msg.text || msg.caption || "[Admin Message]";
|
||||||
await sql(env, "INSERT OR REPLACE INTO messages (user_id, message_id, text, date, topic_message_id) VALUES (?,?,?,?,?)",
|
await sql(env, "INSERT OR REPLACE INTO messages (user_id, message_id, text, date, topic_message_id) VALUES (?,?,?,?,?)",
|
||||||
@@ -1091,11 +1147,61 @@ async function handleAdminReply(msg, env) {
|
|||||||
// 此处为管理员端给用户下发消息的主逻辑。根据之前的版本,管理员侧发送成功后的回执代码也已经去除
|
// 此处为管理员端给用户下发消息的主逻辑。根据之前的版本,管理员侧发送成功后的回执代码也已经去除
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Admin Delivery Failed:", e);
|
console.error("Admin Delivery Failed:", e);
|
||||||
await api(env.BOT_TOKEN, "sendMessage", {
|
console.error("Admin Delivery Failed - Error details:", {
|
||||||
chat_id: msg.chat.id,
|
message: e.message,
|
||||||
message_thread_id: msg.message_thread_id,
|
stack: e.stack,
|
||||||
text: `❌ 内部投递失败:${e.message || "Unknown error"}`
|
uid: uid,
|
||||||
|
topicId: topicIdStr,
|
||||||
|
replyToMsgId: replyToMsgId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 检测用户是否屏蔽了 Bot
|
||||||
|
const isBlocked = e.message && e.message.includes("bot was blocked by the user");
|
||||||
|
|
||||||
|
if (isBlocked) {
|
||||||
|
// 自动标记用户为屏蔽状态
|
||||||
|
try {
|
||||||
|
const u = await getUser(uid, env);
|
||||||
|
if (!u.user_info.bot_blocked) {
|
||||||
|
u.user_info.bot_blocked = true;
|
||||||
|
u.user_info.bot_blocked_ts = Date.now();
|
||||||
|
await updUser(uid, { user_info: u.user_info }, env);
|
||||||
|
console.log(`Auto-marked user ${uid} as bot_blocked`);
|
||||||
|
|
||||||
|
// 更新用户卡片显示
|
||||||
|
if (u.topic_id && u.user_info.card_msg_id) {
|
||||||
|
const mockTgUser = { id: uid, username: u.user_info.username || "", first_name: u.user_info.name || "(未获取)", last_name: "" };
|
||||||
|
const newMeta = getUMeta(mockTgUser, u, u.user_info.join_date || (Date.now()/1000));
|
||||||
|
try {
|
||||||
|
await api(env.BOT_TOKEN, "editMessageCaption", {
|
||||||
|
chat_id: env.ADMIN_GROUP_ID,
|
||||||
|
message_id: u.user_info.card_msg_id,
|
||||||
|
caption: newMeta.card,
|
||||||
|
parse_mode: "HTML",
|
||||||
|
reply_markup: getBtns(uid, u.is_blocked, newMeta.username)
|
||||||
|
});
|
||||||
|
} catch (editErr) {
|
||||||
|
console.log("Failed to update card after bot_blocked detection:", editErr.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (markErr) {
|
||||||
|
console.error("Failed to mark user as bot_blocked:", markErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
await api(env.BOT_TOKEN, "sendMessage", {
|
||||||
|
chat_id: msg.chat.id,
|
||||||
|
message_thread_id: msg.message_thread_id,
|
||||||
|
text: `⚠️ <b>用户已屏蔽 Bot</b>\n\n该用户已在 Telegram 中屏蔽了本机器人,无法接收消息。\n\n请通过其他方式联系用户,让其:\n1️⃣ 打开与机器人的聊天\n2️⃣ 解除屏蔽\n3️⃣ 重新发送 /start`,
|
||||||
|
parse_mode: "HTML"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await api(env.BOT_TOKEN, "sendMessage", {
|
||||||
|
chat_id: msg.chat.id,
|
||||||
|
message_thread_id: msg.message_thread_id,
|
||||||
|
text: `❌ 内部投递失败:${e.message || "Unknown error"}\n\n调试信息:\n用户ID: ${uid}\n话题ID: ${topicIdStr}`
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1107,7 +1213,7 @@ async function handleEdit(msg, env) {
|
|||||||
const newTxt = msg.text || msg.caption || "[非文本]";
|
const newTxt = msg.text || msg.caption || "[非文本]";
|
||||||
|
|
||||||
const logText = `✏️ 消息修改\n前: ${escape(old?.text||"?")}\n后: ${escape(newTxt)}`;
|
const logText = `✏️ 消息修改\n前: ${escape(old?.text||"?")}\n后: ${escape(newTxt)}`;
|
||||||
|
yixia
|
||||||
await api(env.BOT_TOKEN, "sendMessage", {
|
await api(env.BOT_TOKEN, "sendMessage", {
|
||||||
chat_id: env.ADMIN_GROUP_ID,
|
chat_id: env.ADMIN_GROUP_ID,
|
||||||
message_thread_id: u.topic_id,
|
message_thread_id: u.topic_id,
|
||||||
@@ -1223,6 +1329,15 @@ async function handleCallback(cb, env) {
|
|||||||
else if (['block','unblock'].includes(act)) {
|
else if (['block','unblock'].includes(act)) {
|
||||||
const isB = act === 'block'; const uid = p1; const u = await getUser(uid, env); const bid = await getCfg('blocked_topic_id', env);
|
const isB = act === 'block'; const uid = p1; const u = await getUser(uid, env); const bid = await getCfg('blocked_topic_id', env);
|
||||||
await updUser(uid, { is_blocked: isB, block_count: 0 }, env);
|
await updUser(uid, { is_blocked: isB, block_count: 0 }, env);
|
||||||
|
|
||||||
|
// 如果是解封操作,清除 bot_blocked 标记
|
||||||
|
if (!isB && u.user_info.bot_blocked) {
|
||||||
|
u.user_info.bot_blocked = false;
|
||||||
|
delete u.user_info.bot_blocked_ts;
|
||||||
|
await updUser(uid, { user_info: u.user_info }, env);
|
||||||
|
console.log(`Cleared bot_blocked mark for user ${uid} after admin unblock`);
|
||||||
|
}
|
||||||
|
|
||||||
// 响应变更,刷新目标人员资料卡片上的按钮渲染状态
|
// 响应变更,刷新目标人员资料卡片上的按钮渲染状态
|
||||||
if (u.user_info.card_msg_id) {
|
if (u.user_info.card_msg_id) {
|
||||||
api(env.BOT_TOKEN, "editMessageReplyMarkup", { chat_id: env.ADMIN_GROUP_ID, message_id: u.user_info.card_msg_id, reply_markup: getBtns(uid, isB, u.user_info.username) }).catch(()=>{});
|
api(env.BOT_TOKEN, "editMessageReplyMarkup", { chat_id: env.ADMIN_GROUP_ID, message_id: u.user_info.card_msg_id, reply_markup: getBtns(uid, isB, u.user_info.username) }).catch(()=>{});
|
||||||
@@ -1434,13 +1549,23 @@ const getUMeta = (tgUser, dbUser, d) => {
|
|||||||
const note = dbUser.user_info && dbUser.user_info.note ? `\n📝 <b>附加备注:</b> ${escape(dbUser.user_info.note)}` : "";
|
const note = dbUser.user_info && dbUser.user_info.note ? `\n📝 <b>附加备注:</b> ${escape(dbUser.user_info.note)}` : "";
|
||||||
const labelDisplay = tgUser.username ? `@${tgUser.username}` : "未设公开ID";
|
const labelDisplay = tgUser.username ? `@${tgUser.username}` : "未设公开ID";
|
||||||
const timeStr = new Date(d*1000).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai', hour12: false });
|
const timeStr = new Date(d*1000).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai', hour12: false });
|
||||||
|
|
||||||
|
// 检测并显示屏蔽状态
|
||||||
|
let blockStatus = "";
|
||||||
|
if (dbUser.is_blocked) {
|
||||||
|
blockStatus = `\n🚫 <b>管理员屏蔽:</b> 是`;
|
||||||
|
}
|
||||||
|
if (dbUser.user_info && dbUser.user_info.bot_blocked) {
|
||||||
|
const blockTime = new Date(dbUser.user_info.bot_blocked_ts || d*1000).toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai', hour12: false });
|
||||||
|
blockStatus += `\n⛔ <b>用户屏蔽Bot:</b> 是 (${blockTime})`;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
userId: id,
|
userId: id,
|
||||||
name,
|
name,
|
||||||
username: tgUser.username,
|
username: tgUser.username,
|
||||||
topicName: name.substring(0, 128),
|
topicName: name.substring(0, 128),
|
||||||
card: `<b>🪪 用户身份卡片</b>\n---\n👤: ${safeName}\n🏷️: ${labelDisplay}\n🆔: <code>${id}</code>${note}\n🕒: <code>${timeStr}</code>`
|
card: `<b>🪪 用户身份卡片</b>\n---\n👤: ${safeName}\n🏷️: ${labelDisplay}\n🆔: <code>${id}</code>${note}${blockStatus}\n🕒: <code>${timeStr}</code>`
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1489,4 +1614,4 @@ const getBtns = (id, blk, username) => {
|
|||||||
btns.push([{ text: "✏️ 录入备注", callback_data: `note:set:${id}` }, { text: "📌 提升置顶", callback_data: `pin_card:${id}` }]);
|
btns.push([{ text: "✏️ 录入备注", callback_data: `note:set:${id}` }, { text: "📌 提升置顶", callback_data: `pin_card:${id}` }]);
|
||||||
|
|
||||||
return { inline_keyboard: btns };
|
return { inline_keyboard: btns };
|
||||||
};
|
};
|
||||||
Reference in New Issue
Block a user