Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save lism/8bc3610d0a4aeabe57e81c24f76d0e87 to your computer and use it in GitHub Desktop.

Select an option

Save lism/8bc3610d0a4aeabe57e81c24f76d0e87 to your computer and use it in GitHub Desktop.
Binance API Document Watch Dog
mkdir binance-changelog-watcher && cd $_
npm init -y
npm i cheerio dotenv
node watcher.js
// watcher.js
// 每秒抓取 https://developers.binance.com/docs/derivatives/change-log
// 发现正文变化则把“新增/变更的行”发到 Telegram
import fs from 'fs';
import path from 'path';
import process from 'process';
import dotenv from 'dotenv';
import cheerio from 'cheerio';
dotenv.config();
const URL = 'https://developers.binance.com/docs/derivatives/change-log';
const BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN; // 必填
const CHAT_ID = process.env.TELEGRAM_CHAT_ID; // 必填
if (!BOT_TOKEN || !CHAT_ID) {
console.error('请设置 TELEGRAM_BOT_TOKEN 和 TELEGRAM_CHAT_ID');
process.exit(1);
}
// Node 18+ 自带 fetch
const SNAPSHOT_FILE = path.join(process.cwd(), 'last_snapshot.txt');
// 读取上次快照(逐行集合)
function loadPrevLines() {
try {
const txt = fs.readFileSync(SNAPSHOT_FILE, 'utf8');
return new Set(txt.split('\n'));
} catch {
return new Set();
}
}
// 保存本次快照
function saveLines(linesArr) {
fs.writeFileSync(SNAPSHOT_FILE, linesArr.join('\n'), 'utf8');
}
// 提取目标 div 的“可读文本”并做基础规范化(去多空格、trim)
function extractNormalizedLines(html) {
const $ = cheerio.load(html);
const $div = $('div.theme-doc-markdown.markdown').first();
if ($div.length === 0) return [];
// 把标题与列表/段落拆成行,尽量保留语义
const lines = [];
$div.find('h1, h2, h3, p, li, code, pre, ul, ol, hr').each((_, el) => {
const tag = el.tagName?.toLowerCase() || '';
if (tag === 'hr') {
lines.push('---');
return;
}
// 代码块/行内代码尽量直接取文本
const text = $(el).text().replace(/\s+/g, ' ').trim();
if (text) {
// 给标题行加点前缀,有助于 diff 时定位
if (tag === 'h1' || tag === 'h2' || tag === 'h3') {
lines.push(`# ${text}`);
} else if (tag === 'li') {
lines.push(`- ${text}`);
} else {
lines.push(text);
}
}
});
return lines;
}
// 计算“新增/变更行”
// 简单策略:把本次所有行与上次集合比对,不在上次集合内的就是新增(或改动后的新行)
function diffNewLines(prevSet, currLines) {
const out = [];
for (const line of currLines) {
if (!prevSet.has(line)) out.push(line);
}
return out;
}
async function sendToTelegram(text) {
// Telegram 单条消息长度有限,做分段
const MAX = 3500; // 留些余量
const chunks = [];
let buf = '';
for (const line of text.split('\n')) {
if ((buf + line + '\n').length > MAX) {
chunks.push(buf);
buf = '';
}
buf += line + '\n';
}
if (buf) chunks.push(buf);
for (const chunk of chunks) {
const url = `https://api.telegram.org/bot${BOT_TOKEN}/sendMessage` +
`?chat_id=${encodeURIComponent(CHAT_ID)}` +
`&text=${encodeURIComponent(chunk)}`;
const resp = await fetch(url);
if (!resp.ok) {
const errTxt = await resp.text().catch(() => '');
console.error('Telegram 发送失败', resp.status, errTxt);
}
// 避免消息过快触发限频
await new Promise(r => setTimeout(r, 300));
}
}
let prevSet = loadPrevLines();
let isFetching = false;
async function tick() {
if (isFetching) return; // 防止重入
isFetching = true;
try {
const res = await fetch(URL, {
headers: {
'User-Agent': 'Mozilla/5.0 (ChangeLogWatcher/1.0)',
'Cache-Control': 'no-cache',
'Pragma': 'no-cache',
},
});
if (!res.ok) {
console.error('抓取失败:', res.status, await res.text().catch(() => ''));
return;
}
const html = await res.text();
const currLines = extractNormalizedLines(html);
if (currLines.length === 0) {
console.warn('未提取到正文,可能页面结构变化');
return;
}
const delta = diffNewLines(prevSet, currLines);
if (delta.length > 0) {
// 组装消息:把新增内容发到 Telegram
const header = `Binance Derivatives Change Log 发现更新:\n${URL}\n\n`;
const body = delta.join('\n');
await sendToTelegram(header + body);
// 刷新快照
saveLines(currLines);
prevSet = new Set(currLines);
console.log(new Date().toISOString(), `发现 ${delta.length} 行变更,已推送`);
} else {
// 无更新
// console.log(new Date().toISOString(), '无变化');
}
} catch (e) {
console.error('tick 异常:', e);
} finally {
isFetching = false;
}
}
// 每秒轮询(注意:1s 很激进,若被限频可改为 5s/10s)
tick();
setInterval(tick, 1000);
TELEGRAM_BOT_TOKEN=123456:ABCDEF...
TELEGRAM_CHAT_ID=-1001234567890
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment