mirror of
https://github.com/joeseesun/wechat-radar.git
synced 2026-09-09 17:18:31 +09:00
Publish updated WeChat Radar
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { db } from '@/lib/db';
|
||||
import { wxSessions } from '@/lib/wx';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
type SearchResult = {
|
||||
id: string;
|
||||
type: 'group' | 'topic' | 'person' | 'message' | 'link';
|
||||
title: string;
|
||||
subtitle: string;
|
||||
href: string;
|
||||
external?: boolean;
|
||||
};
|
||||
|
||||
type MessageRow = {
|
||||
chatroom_id: string;
|
||||
sender: string;
|
||||
content: string;
|
||||
date: string;
|
||||
time: string;
|
||||
};
|
||||
|
||||
type TopicRow = {
|
||||
id: number;
|
||||
date: string;
|
||||
title: string;
|
||||
summary: string | null;
|
||||
message_count: number;
|
||||
group_count: number;
|
||||
};
|
||||
|
||||
type LinkRow = {
|
||||
canonical_url: string;
|
||||
title: string | null;
|
||||
domain: string;
|
||||
date: string;
|
||||
};
|
||||
|
||||
type PersonRow = {
|
||||
sender: string;
|
||||
hits: number;
|
||||
groups: number;
|
||||
latest: string;
|
||||
};
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const q = (new URL(req.url).searchParams.get('q') ?? '').trim();
|
||||
if (q.length < 2) return NextResponse.json({ ok: true, results: [] });
|
||||
|
||||
const like = `%${q}%`;
|
||||
const nameMap = await loadGroupNames();
|
||||
const results: SearchResult[] = [];
|
||||
|
||||
for (const [chatroomId, name] of nameMap) {
|
||||
if (!name.toLowerCase().includes(q.toLowerCase())) continue;
|
||||
results.push({
|
||||
id: `group:${chatroomId}`,
|
||||
type: 'group',
|
||||
title: name,
|
||||
subtitle: chatroomId,
|
||||
href: `/groups/${encodeURIComponent(chatroomId)}`,
|
||||
});
|
||||
if (results.length >= 8) break;
|
||||
}
|
||||
|
||||
const topics = db()
|
||||
.prepare(
|
||||
`SELECT id, date, title, summary, message_count, group_count
|
||||
FROM topics
|
||||
WHERE title LIKE ? OR COALESCE(summary, '') LIKE ?
|
||||
ORDER BY date DESC, message_count DESC
|
||||
LIMIT 8`,
|
||||
)
|
||||
.all(like, like) as TopicRow[];
|
||||
for (const t of topics) {
|
||||
results.push({
|
||||
id: `topic:${t.id}`,
|
||||
type: 'topic',
|
||||
title: t.title,
|
||||
subtitle: `${t.date} · ${t.message_count} 条 · ${t.group_count} 群${t.summary ? ` · ${t.summary}` : ''}`,
|
||||
href: `/topics?date=${t.date}`,
|
||||
});
|
||||
}
|
||||
|
||||
const people = db()
|
||||
.prepare(
|
||||
`SELECT sender, COUNT(*) AS hits, COUNT(DISTINCT chatroom_id) AS groups, MAX(date) AS latest
|
||||
FROM messages
|
||||
WHERE sender LIKE ?
|
||||
GROUP BY sender
|
||||
ORDER BY hits DESC
|
||||
LIMIT 8`,
|
||||
)
|
||||
.all(like) as PersonRow[];
|
||||
for (const p of people) {
|
||||
results.push({
|
||||
id: `person:${p.sender}`,
|
||||
type: 'person',
|
||||
title: p.sender,
|
||||
subtitle: `${p.hits} 条消息 · ${p.groups} 个群 · 最近 ${p.latest}`,
|
||||
href: `/signals?q=${encodeURIComponent(p.sender)}`,
|
||||
});
|
||||
}
|
||||
|
||||
const messages = db()
|
||||
.prepare(
|
||||
`SELECT chatroom_id, sender, content, date, time
|
||||
FROM messages
|
||||
WHERE content LIKE ? OR sender LIKE ?
|
||||
ORDER BY timestamp DESC
|
||||
LIMIT 10`,
|
||||
)
|
||||
.all(like, like) as MessageRow[];
|
||||
for (const m of messages) {
|
||||
results.push({
|
||||
id: `message:${m.chatroom_id}:${m.time}:${m.sender}`,
|
||||
type: 'message',
|
||||
title: compact(m.content || m.sender, 80),
|
||||
subtitle: `${nameMap.get(m.chatroom_id) ?? m.chatroom_id} · ${m.sender} · ${m.time}`,
|
||||
href: `/groups/${encodeURIComponent(m.chatroom_id)}?date=${m.date}`,
|
||||
});
|
||||
}
|
||||
|
||||
const links = db()
|
||||
.prepare(
|
||||
`SELECT canonical_url, title, domain, MAX(date) AS date
|
||||
FROM message_links
|
||||
WHERE canonical_url LIKE ? OR COALESCE(title, '') LIKE ? OR domain LIKE ?
|
||||
GROUP BY canonical_url
|
||||
ORDER BY MAX(timestamp) DESC
|
||||
LIMIT 8`,
|
||||
)
|
||||
.all(like, like, like) as LinkRow[];
|
||||
for (const l of links) {
|
||||
results.push({
|
||||
id: `link:${l.canonical_url}`,
|
||||
type: 'link',
|
||||
title: l.title || l.canonical_url,
|
||||
subtitle: `${l.domain} · ${l.date}`,
|
||||
href: l.canonical_url,
|
||||
external: true,
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json({ ok: true, results: results.slice(0, 32) });
|
||||
}
|
||||
|
||||
async function loadGroupNames(): Promise<Map<string, string>> {
|
||||
const names = new Map<string, string>();
|
||||
try {
|
||||
const sessions = await wxSessions(500);
|
||||
for (const s of sessions) {
|
||||
if (s.is_group) names.set(s.username, s.chat);
|
||||
}
|
||||
} catch {}
|
||||
|
||||
const local = db()
|
||||
.prepare(
|
||||
`SELECT chatroom_id, COUNT(*) AS n
|
||||
FROM messages
|
||||
GROUP BY chatroom_id
|
||||
ORDER BY n DESC
|
||||
LIMIT 500`,
|
||||
)
|
||||
.all() as Array<{ chatroom_id: string }>;
|
||||
for (const row of local) {
|
||||
if (!names.has(row.chatroom_id)) names.set(row.chatroom_id, row.chatroom_id);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
function compact(s: string, max: number): string {
|
||||
const text = s.replace(/\s+/g, ' ').trim();
|
||||
if (text.length <= max) return text;
|
||||
return `${text.slice(0, max - 1)}…`;
|
||||
}
|
||||
Reference in New Issue
Block a user