เทรนด์ล่าสุดจากแหล่งจริง: Google Trends ไทย, Hacker News, GitHub repo มาแรง, คริปโตมาแรง และ Wikipedia ที่คนอ่านมากสุด

เทรนด์ล่าสุดจากแหล่งข้อมูลจริง: คำค้นมาแรงในไทยจาก Google Trends, ข่าว tech จาก Hacker News, repo ใหม่ที่ได้ดาวเร็วที่สุดบน GitHub, เหรียญคริปโตที่คนค้นมากสุด และบทความ Wikipedia ที่คนอ่านมากสุดเมื่อวาน ติดตามคำที่สนใจแล้วเด้งเตือนเมื่อคำนั้นขึ้นเทรนด์ได้
| คำสั่ง | ทำอะไร | |
|---|---|---|
/trend | สรุปทุกแหล่ง | |
/trend google [ประเทศ] | Google Trends (TH, US, JP ...) | |
/trend hn | Hacker News | |
/trend github [วัน] [ภาษา] | repo ใหม่ดาวพุ่ง | |
/trend crypto | คริปโตมาแรง | |
| `/trend wiki [th\ | en]` | Wikipedia ที่คนอ่านมากสุด |
/trend watch <คำ> / unwatch <คำ> | ติดตามคำ |
/plugin install trend-th --marketplace tksoft11/claude-code-mods-th
ต้องตั้ง CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 ก่อนเปิด claude (ดู README หลัก)
เปิด hooks/register.tsx แล้วอ่านตามบทที่เขียนไว้ในคอมเมนต์:
GitHub API แบบไม่ใช้ token จำกัดจำนวนครั้งต่อชั่วโมง บทความ Wikipedia ที่คนอ่านมากสุดเป็นข้อมูลดิบจากผู้อ่าน อาจมีหัวข้อที่ไม่เหมาะสม Mod กรองหัวข้อสั้นเกินไปออกแล้วแต่ไม่ได้กรองทั้งหมด
hooks/register.tsx 198 lines1/**
2 * ============================================================================
3 * MOD 38: Trend TH - เทรนด์ล่าสุดจากแหล่งข้อมูลจริง (ฉบับสอนทำ)
4 * ============================================================================
5 * แรงบันดาลใจจาก: Google Trends, Hacker News, GitHub Trending
6 *
7 * หน้าที่ของ Mod นี้:
8 * 1. คำค้นมาแรงในไทยวันนี้ (Google Trends RSS) พร้อมจำนวนค้นหาโดยประมาณ
9 * 2. ข่าว tech ที่ Dev ทั่วโลกคุยกัน (Hacker News)
10 * 3. repo ใหม่ที่ได้ดาวเร็วที่สุด (GitHub search API)
11 * 4. เหรียญคริปโตที่คนค้นมากสุด (CoinGecko trending)
12 * 5. บทความ Wikipedia ไทย/อังกฤษที่คนอ่านมากสุดเมื่อวาน
13 * 6. ติดตามคำ (watch): เด้งเตือนเมื่อคำที่สนใจขึ้นเทรนด์ Google
14 */
15
16import type { EngineInterface, Register, Timer } from 'claude-code'
17
18const UA = { 'User-Agent': 'claude-code-trend-th/1.0 (educational mod)' }
19let watches: string[] = []
20let watchTimer: Timer | undefined
21let alerted = new Set<string>()
22
23async function get($: EngineInterface, url: string): Promise<string> {
24 const res = await $.http.fetch(url, { headers: UA })
25 if (!res.ok) throw new Error(`HTTP ${res.status}`)
26 return res.text
27}
28
29function decode(s: string): string {
30 return s.replace(/<!\[CDATA\[|\]\]>/g, '').replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, "'").replace(/</g, '<').replace(/>/g, '>').trim()
31}
32
33// ==========================================================================
34// บทที่ 1: Google Trends - อ่าน RSS (XML) ด้วย regex
35// ==========================================================================
36interface GoogleTrend {
37 title: string
38 traffic: string
39 news: string
40}
41
42async function googleTrends($: EngineInterface, geo: string): Promise<GoogleTrend[]> {
43 const xml = await get($, `https://trends.google.com/trending/rss?geo=${encodeURIComponent(geo)}`)
44 return [...xml.matchAll(/<item>([\s\S]*?)<\/item>/g)].map(m => {
45 const item = m[1] ?? ''
46 const tag = (name: string) => decode(new RegExp(`<${name}>([\\s\\S]*?)</${name}>`).exec(item)?.[1] ?? '')
47 return { title: tag('title'), traffic: tag('ht:approx_traffic'), news: tag('ht:news_item_title') }
48 })
49}
50
51// ==========================================================================
52// บทที่ 2: Hacker News, GitHub, CoinGecko, Wikipedia (JSON ทั้งหมด)
53// ==========================================================================
54async function hackerNews($: EngineInterface, n: number): Promise<string[]> {
55 const ids = (JSON.parse(await get($, 'https://hacker-news.firebaseio.com/v0/topstories.json')) as number[]).slice(0, n)
56 const items = await Promise.all(ids.map(id => get($, `https://hacker-news.firebaseio.com/v0/item/${id}.json`).then(t => JSON.parse(t) as { title?: string; score?: number; descendants?: number; url?: string }).catch(() => undefined)))
57 return items.filter(Boolean).map(i => `▲${i?.score ?? 0} 💬${i?.descendants ?? 0} ${i?.title ?? ''}${i?.url ? `\n ${i.url}` : ''}`)
58}
59
60async function githubRising($: EngineInterface, days: number, language?: string): Promise<string[]> {
61 const since = new Date((await $.clock.now()) - days * 86_400_000).toISOString().slice(0, 10)
62 const q = `created:>${since}${language ? ` language:${language}` : ''}`
63 const body = JSON.parse(await get($, `https://api.github.com/search/repositories?q=${encodeURIComponent(q)}&sort=stars&order=desc&per_page=10`)) as { items?: { full_name: string; stargazers_count: number; description?: string | null; language?: string | null }[] }
64 return (body.items ?? []).map(r => `⭐${r.stargazers_count.toLocaleString('en-US')} ${r.full_name}${r.language ? ` [${r.language}]` : ''}${r.description ? `\n ${r.description.slice(0, 90)}` : ''}`)
65}
66
67async function cryptoTrending($: EngineInterface): Promise<string[]> {
68 const body = JSON.parse(await get($, 'https://api.coingecko.com/api/v3/search/trending')) as { coins?: { item: { name: string; symbol: string; market_cap_rank?: number | null } }[] }
69 return (body.coins ?? []).slice(0, 10).map(c => `${c.item.symbol.padEnd(8)} ${c.item.name}${c.item.market_cap_rank ? ` (อันดับ ${c.item.market_cap_rank})` : ''}`)
70}
71
72async function wikiMostRead($: EngineInterface, lang: string): Promise<string[]> {
73 const y = new Date((await $.clock.now()) - 86_400_000) // ข้อมูลของเมื่อวาน (วันนี้ยังนับไม่จบ)
74 const path = `${y.getFullYear()}/${String(y.getMonth() + 1).padStart(2, '0')}/${String(y.getDate()).padStart(2, '0')}`
75 const body = JSON.parse(await get($, `https://api.wikimedia.org/feed/v1/wikipedia/${lang}/featured/${path}`)) as { mostread?: { articles?: { normalizedtitle: string; views: number; description?: string }[] } }
76 return (body.mostread?.articles ?? [])
77 // ตัดหน้าระบบ และหัวข้อสั้นเกินไป (มักเป็นคำหยาบหรือหน้าที่ไม่มีเนื้อหา)
78 .filter(a => !/^(Main Page|หน้าหลัก|Special:|พิเศษ:)/.test(a.normalizedtitle) && [...a.normalizedtitle].length > 2)
79 .slice(0, 10)
80 .map(a => `👁️${a.views.toLocaleString('en-US')} ${a.normalizedtitle}${a.description ? ` - ${a.description.slice(0, 60)}` : ''}`)
81}
82
83// ==========================================================================
84// บทที่ 3: ติดตามคำ - เช็ค Google Trends ทุก 1 ชม. ขณะเปิด Claude Code
85// ==========================================================================
86async function checkWatches($: EngineInterface): Promise<void> {
87 if (watches.length === 0) return
88 const trends = await googleTrends($, 'TH')
89 for (const w of watches) {
90 const hit = trends.find(t => t.title.toLowerCase().includes(w.toLowerCase()) || t.news.toLowerCase().includes(w.toLowerCase()))
91 const key = `${w}|${hit?.title}`
92 if (hit && !alerted.has(key)) {
93 alerted.add(key)
94 $.ui.toast(`📈 "${w}" ขึ้นเทรนด์ Google: ${hit.title} (${hit.traffic})`)
95 }
96 }
97}
98
99function startWatch($: EngineInterface): void {
100 watchTimer?.cancel()
101 watchTimer = watches.length ? $.clock.every(60 * 60_000, () => void checkWatches($).catch(() => undefined)) : undefined
102}
103
104const section = (title: string, lines: string[]) => [title, ...lines.map((l, i) => ` ${i + 1}. ${l}`)].join('\n')
105
106const HELP = `📈 Trend TH (Mod 38)
107========================================
108/trend สรุปทุกแหล่ง (สั้น)
109/trend google [ประเทศ] คำค้นมาแรง (ค่าเริ่มต้น TH เช่น US, JP)
110/trend hn Hacker News
111/trend github [วัน] [ภาษา] repo ใหม่ดาวพุ่ง เช่น /trend github 7 typescript
112/trend crypto เหรียญที่คนค้นมากสุด
113/trend wiki [th|en] บทความที่คนอ่านมากสุดเมื่อวาน
114/trend watch <คำ> | unwatch <คำ> | watch เตือนเมื่อคำนี้ขึ้นเทรนด์`
115
116export const register: Register = on => {
117 on('session.start', async ($, e, next) => {
118 const saved = await $.store.get('watches')
119 watches = Array.isArray(saved) ? (saved as string[]) : []
120 startWatch($)
121 await $.command.register({ name: 'trend', description: 'เทรนด์ล่าสุด: Google ไทย, Hacker News, GitHub, คริปโต, Wikipedia (/trend help)', argumentHint: 'google|hn|github|crypto|wiki|watch' })
122 return next(e)
123 })
124
125 // ==========================================================================
126 // บทที่ 4: คำสั่ง /trend
127 // ==========================================================================
128 on('command.run', { command: 'trend' }, async ($, e) => {
129 const [sub = '', a1, a2] = e.args.trim().split(/\s+/).filter(Boolean)
130 try {
131 switch (sub) {
132 case '': {
133 // ดึงพร้อมกัน แหล่งไหนพังก็ยังแสดงแหล่งอื่น
134 const [g, h, gh, c] = await Promise.all([
135 googleTrends($, 'TH').catch(() => [] as GoogleTrend[]),
136 hackerNews($, 5).catch(() => ['(ดึงไม่ได้)']),
137 githubRising($, 7).then(l => l.slice(0, 5)).catch(() => ['(ดึงไม่ได้)']),
138 cryptoTrending($).then(l => l.slice(0, 5)).catch(() => ['(ดึงไม่ได้)']),
139 ])
140 return {
141 text: [
142 section('🇹🇭 Google ไทยวันนี้', g.slice(0, 8).map(t => `${t.title} (${t.traffic})`)),
143 section('💻 Hacker News', h.map(l => l.split('\n')[0] ?? l)),
144 section('⭐ GitHub repo ใหม่ดาวพุ่ง (7 วัน)', gh.map(l => l.split('\n')[0] ?? l)),
145 section('🪙 คริปโตมาแรง', c),
146 '',
147 'ดูละเอียดแต่ละแหล่ง: /trend help',
148 ].join('\n\n'),
149 }
150 }
151 case 'google': {
152 const geo = (a1 ?? 'TH').toUpperCase()
153 const list = await googleTrends($, geo)
154 return { text: section(`🔎 Google Trends ${geo}`, list.slice(0, 20).map(t => `${t.title} (${t.traffic})${t.news ? `\n 📰 ${t.news.slice(0, 90)}` : ''}`)) }
155 }
156 case 'hn':
157 return { text: section('💻 Hacker News ตอนนี้', await hackerNews($, 15)) }
158 case 'github': {
159 const days = Math.min(30, Math.max(1, Number(a1) || 7))
160 const language = a1 && !Number(a1) ? a1 : a2
161 return { text: section(`⭐ GitHub repo ใหม่ (${days} วัน)${language ? ` ${language}` : ''}`, await githubRising($, days, language)) }
162 }
163 case 'crypto':
164 return { text: section('🪙 CoinGecko trending', await cryptoTrending($)) }
165 case 'wiki': {
166 const lang = a1 === 'en' ? 'en' : 'th'
167 return { text: section(`📚 Wikipedia ${lang} อ่านมากสุดเมื่อวาน`, await wikiMostRead($, lang)) }
168 }
169 case 'watch':
170 if (!a1) return { text: watches.length ? `👀 ติดตาม: ${watches.join(', ')}` : 'ยังไม่ได้ติดตามคำไหน /trend watch <คำ>' }
171 watches = [...new Set([...watches, [a1, a2].filter(Boolean).join(' ')])]
172 await $.store.set('watches', watches)
173 startWatch($)
174 await checkWatches($)
175 return { text: `👀 ติดตาม: ${watches.join(', ')} (เช็คทุก 1 ชม. ขณะเปิด Claude Code)` }
176 case 'unwatch':
177 watches = watches.filter(w => w !== [a1, a2].filter(Boolean).join(' '))
178 await $.store.set('watches', watches)
179 startWatch($)
180 return { text: `👀 ติดตาม: ${watches.join(', ') || '-'}` }
181 default:
182 return { text: HELP }
183 }
184 } catch (err) {
185 return { text: `❌ ดึงข้อมูลไม่สำเร็จ: ${err instanceof Error ? err.message : String(err)}` }
186 }
187 })
188}
189
190/**
191 * ========================================================================
192 * การบ้าน / ต่อยอด
193 * ========================================================================
194 * 1. ให้ Claude สรุปว่าเทรนด์ไหนเกี่ยวกับธุรกิจ/โปรเจกต์ของคุณ ($.model.complete)
195 * 2. เก็บประวัติเทรนด์รายวันใน $.store แล้วดูว่าคำไหนอยู่นาน
196 * 3. ส่งสรุปเทรนด์ตอนเช้าไปมือถือ (ต่อกับ Mod 22 + Mod 24)
197 */
198