feat: add chat message to spam rules

This commit is contained in:
ngfchl
2026-07-07 15:37:53 +08:00
parent 886cd0d301
commit c48300cb55
4 changed files with 685 additions and 7 deletions
+28 -6
View File
@@ -2,7 +2,7 @@
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref } from 'vue';
import { SButton, SButtonLoading, SCard, SConfigProvider, SInput, STag } from '@soybeanjs/ui';
type ChatItem = Record<string, any> & { _deleting?: boolean };
type ChatItem = Record<string, any> & { _deleting?: boolean; _ruling?: boolean };
type BanItem = Record<string, any> & { _loading?: boolean };
type LogItem = { time?: string; level?: string; message?: string };
type GroupItem = { chat_id: number; name: string };
@@ -414,6 +414,27 @@ async function sendMessage() {
sending.value = false;
}
}
async function addChatMessageToRules(item: ChatItem) {
const text = String(item.text || '').trim();
if (!text) return showToast('当前消息没有可写入的文本');
if (text.length > 500) return showToast('当前消息超过 500 字,请到规则测试页提取关键词或正则');
if (!await askConfirm('写入拦截库', '确认将当前聊天内容作为关键词写入拦截规则库?', { confirmText: '写入', danger: false })) return;
item._ruling = true;
try {
const result = await fetchJson('/api/spam-rules', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ type: 'keyword', pattern: text, score: 90 })
});
showToast(`已写入拦截库,关键词 ${result.rule_counts?.keywords || '-'} / 正则 ${result.rule_counts?.regex || '-'}`);
} catch (e: any) {
showToast(`写入失败:${e.message}`);
} finally {
item._ruling = false;
}
}
async function deleteChatMessage(item: ChatItem, ban = false) {
const action = ban ? '删除并封禁' : '删除';
if (!await askConfirm(action, `确认${action}这条消息?`, { confirmText: action, danger: true })) return;
@@ -555,7 +576,7 @@ onBeforeUnmount(() => {
<SCard id="chat" class="panel chat-panel" title="聊天记录" :description="`${chatCount} 条`">
<div class="scroll-area chat-scroll">
<div v-if="!chatLogs.length" class="empty">等待消息...</div>
<article v-for="(item, i) in chatLogs" :key="item.id || i" :class="[chatItemClass(item), { deleting: item._deleting }]">
<article v-for="(item, i) in chatLogs" :key="item.id || i" :class="[chatItemClass(item), { deleting: item._deleting || item._ruling }]">
<div class="chat-head">
<div class="chat-user-line">
<STag size="xs" :color="tagColor(item)" variant="soft">{{ chatStatusText(item) }}</STag>
@@ -572,16 +593,17 @@ onBeforeUnmount(() => {
<STag v-if="item.spam" class="reason-tag" size="xs" color="destructive" variant="soft" :title="item.reason || item.spam_reason">{{ item.reason || item.spam_reason }}</STag>
<span v-else />
<div class="chat-actions">
<SButton size="xs" variant="link" color="primary" :disabled="item._deleting || item._ruling" @click="addChatMessageToRules(item)">{{ item._ruling ? '入库中...' : '入库' }}</SButton>
<STag v-if="item.deleted" size="xs" color="secondary" variant="soft">已删 · {{ formatTime(item.deleted_at) }}</STag>
<template v-else>
<SButton size="xs" variant="link" color="destructive" :disabled="item._deleting" @click="deleteChatMessage(item, false)">{{ item._deleting ? '删除中...' : '删除' }}</SButton>
<SButton v-if="item.user_id" size="xs" variant="link" color="warning" :disabled="item._deleting" @click="deleteChatMessage(item, true)">{{ item._deleting ? '处理中...' : '删封' }}</SButton>
<SButton size="xs" variant="link" color="destructive" :disabled="item._deleting || item._ruling" @click="deleteChatMessage(item, false)">{{ item._deleting ? '删除中...' : '删除' }}</SButton>
<SButton v-if="item.user_id" size="xs" variant="link" color="warning" :disabled="item._deleting || item._ruling" @click="deleteChatMessage(item, true)">{{ item._deleting ? '处理中...' : '删封' }}</SButton>
</template>
</div>
</div>
<div v-if="item._deleting" class="chat-loading-mask">
<div v-if="item._deleting || item._ruling" class="chat-loading-mask">
<span class="mini-spinner"></span>
<strong>正在处理当前消息...</strong>
<strong>{{ item._ruling ? '正在写入拦截库...' : '正在处理当前消息...' }}</strong>
</div>
</article>
</div>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="/static/icon.svg" />
<title>TG Spam Guard</title>
<script type="module" crossorigin src="/static/assets/index-C6kPOPdz.js"></script>
<script type="module" crossorigin src="/static/assets/index-h7vFW1gU.js"></script>
<link rel="stylesheet" crossorigin href="/static/assets/index-DfsWb80Q.css">
</head>
<body>