"use client"; import { useEffect, useState } from "react"; import { opsApi } from "@/lib/ops-api"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { RefreshCcw, ShieldAlert, Copy, Check, Search, UserX, Mail, Calendar, AlertTriangle, } from "lucide-react"; interface TelegramAnomaly { telegram_id: number; username: string; chat_id: string; status: string; anomaly_type: "unbound" | "expired" | "trial_only"; reason: string; email: string | null; expires_at: string | null; } export function TelegramAuditPageClient() { const [loading, setLoading] = useState(true); const [data, setData] = useState<{ anomalies: TelegramAnomaly[]; valid_count: number; anomaly_count: number; error?: string; } | null>(null); const [filterType, setFilterType] = useState("all"); const [searchQuery, setSearchQuery] = useState(""); const [copiedId, setCopiedId] = useState(null); const loadData = async () => { setLoading(true); try { const res = await opsApi.telegramAudit(); setData(res); } catch (err: any) { setData({ anomalies: [], valid_count: 0, anomaly_count: 0, error: err.message || "获取审核数据失败", }); } setLoading(false); }; useEffect(() => { void loadData(); }, []); const handleCopy = (id: number) => { void navigator.clipboard.writeText(String(id)); setCopiedId(id); setTimeout(() => setCopiedId(null), 1500); }; if (loading) { return (

正在与电报服务器同步并审计群成员,可能需要几秒钟...

); } const anomalies = data?.anomalies ?? []; const error = data?.error; const filtered = anomalies.filter((item) => { const matchesFilter = filterType === "all" || item.anomaly_type === filterType; const matchesSearch = searchQuery.trim() === "" || String(item.telegram_id).includes(searchQuery) || (item.username && item.username.toLowerCase().includes(searchQuery.toLowerCase())) || (item.email && item.email.toLowerCase().includes(searchQuery.toLowerCase())) || (item.reason && item.reason.toLowerCase().includes(searchQuery.toLowerCase())); return matchesFilter && matchesSearch; }); const countUnbound = anomalies.filter((x) => x.anomaly_type === "unbound").length; const countExpired = anomalies.filter((x) => x.anomaly_type === "expired").length; const countTrial = anomalies.filter((x) => x.anomaly_type === "trial_only").length; return (

电报群清理与异常检测

本页通过核对本地数据库中与 Bot 交互过(或绑定过)的用户,实时查询 Telegram 接口审计群成员资格。

{error && (

检测出发生错误

{error}

)} {/* Overview Cards */}
群内付费用户 (正常)
{data?.valid_count ?? 0} 人

已绑定且在订阅期内的正常付费用户

未绑定网页账号
{countUnbound} 人

未能在网页端找到绑定对应 TG 的记录

订阅已过期
{countExpired} 人

已绑定,但当前没有任何有效订阅

仅拥有试用期
{countTrial} 人

仅注册了免费试用订阅(不具备入群资格)

{/* Filter and Search Bar */}
{/* Filter Buttons */}
{/* Search Input */}
setSearchQuery(e.target.value)} className="pl-9 h-9 bg-slate-950 border-white/10 text-xs rounded-lg text-slate-100 placeholder:text-slate-500" />
{/* Main Table */} 需清理成员列表
{filtered.length > 0 ? ( filtered.map((row) => ( )) ) : ( )}
电报用户名 & ID 异常类型 原因 绑定邮箱 订阅到期日 操作
{row.username.startsWith("@") ? ( {row.username} ) : ( {row.username} )}
{row.telegram_id}
{row.anomaly_type === "unbound" ? ( 未绑定 ) : row.anomaly_type === "expired" ? ( 已到期 ) : ( 试用会员 )} {row.reason} {row.email ? (
{row.email}
) : ( )}
{row.expires_at ? (
{row.expires_at.split("T")[0]}
) : ( )}
{searchQuery ? "未找到符合搜索条件的异常成员" : "当前群内没有检测出任何已过期的异常成员 🎉"}

💡 怎么手动清理?

  1. 在上述列表中点击任意用户的 复制 ID 按钮。
  2. 打开你的 Telegram 客户端,进入对应的群组设置面板。
  3. 点击群成员列表中的“添加成员”或直接搜索该用户的电报 ID 或电报用户名。
  4. 在用户资料卡中选择 Remove from Group (踢出群组) 即可。
); }