Skip to content

Daily Commit Summary & Telegram Notification #4

Daily Commit Summary & Telegram Notification

Daily Commit Summary & Telegram Notification #4

name: Daily Commit Summary & Telegram Notification
on:
schedule:
# 每天晚上 23:00 (UTC+8 15:00) 运行
- cron: '0 15 * * *'
workflow_dispatch:
inputs:
date:
description: '指定日期 (YYYY-MM-DD,默认为今天)'
required: false
type: string
jobs:
daily-summary:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout TeleBox_Plugins repository
uses: actions/checkout@v4
with:
path: TeleBox_Plugins
fetch-depth: 0
- name: Checkout TeleBox repository
id: checkout-telebox
uses: actions/checkout@v4
with:
repository: TeleBoxOrg/TeleBox
path: TeleBox
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
continue-on-error: true
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Generate commit summary and send to Telegram
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
TARGET_DATE: ${{ github.event.inputs.date }}
CHECKOUT_SUCCESS: ${{ steps.checkout-telebox.outcome == 'success' }}
run: |
cat > commit-summary.js << 'EOF'
const { execSync } = require('child_process');
const https = require('https');
const querystring = require('querystring');
// 配置
const BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
const CHAT_ID = process.env.TELEGRAM_CHAT_ID;
const TARGET_DATE = process.env.TARGET_DATE || new Date().toISOString().split('T')[0];
const CHECKOUT_SUCCESS = process.env.CHECKOUT_SUCCESS === 'true';
// 验证环境变量
if (!BOT_TOKEN || !CHAT_ID) {
console.error('❌ 缺少必要的环境变量: TELEGRAM_BOT_TOKEN 或 TELEGRAM_CHAT_ID');
process.exit(1);
}
console.log(`📅 生成 ${TARGET_DATE} 的提交摘要`);
// 获取指定日期的提交
function getCommitsForDate(repoPath, repoName, date) {
try {
const since = `${date} 00:00:00`;
const until = `${date} 23:59:59`;
const gitLog = execSync(
`cd ${repoPath} && git log --since="${since}" --until="${until}" --pretty=format:"%h|%s|%an|%ad" --date=format:"%H:%M"`,
{ encoding: 'utf8' }
).trim();
if (!gitLog) {
return [];
}
return gitLog.split('\n').map(line => {
const [hash, message, author, time] = line.split('|');
return {
hash: hash.trim(),
message: message.trim(),
author: author.trim(),
time: time.trim(),
repo: repoName
};
});
} catch (error) {
console.warn(`⚠️ 获取 ${repoName} 提交记录失败:`, error.message);
return [];
}
}
// 获取两个仓库的提交
const teleboxCommits = CHECKOUT_SUCCESS ? getCommitsForDate('TeleBox', 'TeleBox', TARGET_DATE) : [];
const pluginsCommits = getCommitsForDate('TeleBox_Plugins', 'TeleBox_Plugins', TARGET_DATE);
if (!CHECKOUT_SUCCESS) {
console.warn('⚠️ TeleBox 仓库访问失败,仅统计 TeleBox_Plugins 提交');
}
// 去重和过滤提交信息
function deduplicateCommits(commits) {
const seen = new Set();
const filtered = [];
for (const commit of commits) {
// 跳过自动化提交
if (commit.message.includes('🤖 自动更新插件列表') ||
commit.message.includes('Merge pull request') ||
commit.message.match(/^Update \w+\.(json|yml|md)$/)) {
continue;
}
// 基于消息内容去重
const key = commit.message.toLowerCase().trim();
if (!seen.has(key)) {
seen.add(key);
filtered.push(commit);
}
}
return filtered;
}
const dedupedTeleboxCommits = deduplicateCommits(teleboxCommits);
const dedupedPluginsCommits = deduplicateCommits(pluginsCommits);
const allCommits = [...dedupedTeleboxCommits, ...dedupedPluginsCommits];
if (allCommits.length === 0) {
console.log('📭 今日无提交记录');
// 发送无提交的通知
const noCommitsMessage = `📅 TeleBox 日报 - ${TARGET_DATE}\n\n🌙 今日无代码提交\n\n保持代码整洁,明日再战!`;
sendToTelegram(noCommitsMessage);
return;
}
// 按仓库分组提交
const commitsByRepo = {
'TeleBox': dedupedTeleboxCommits,
'TeleBox_Plugins': dedupedPluginsCommits
};
// 生成摘要消息
let message = `📅 TeleBox 日报 - ${TARGET_DATE}\n\n`;
message += `📊 今日提交统计\n`;
message += `• 总提交数: ${allCommits.length}\n`;
message += `• TeleBox: ${dedupedTeleboxCommits.length} 次提交\n`;
message += `• TeleBox_Plugins: ${dedupedPluginsCommits.length} 次提交\n\n`;
// 添加详细提交信息 (限制显示数量避免消息过长)
for (const [repoName, commits] of Object.entries(commitsByRepo)) {
if (commits.length === 0) continue;
message += `🔧 ${repoName}\n`;
// 限制每个仓库最多显示10条提交
const displayCommits = commits.slice(0, 10);
displayCommits.forEach(commit => {
// 简化提交信息,移除常见前缀
let cleanMessage = commit.message
.replace(/^(feat|fix|docs|style|refactor|test|chore|perf)(\(.+\))?: /, '')
.replace(/^(🎉|🐛|📝|💄|♻️|✅|🔧|⚡|🚀|📦|🔀|⏪|🔖|💚|👷|📈|♿|🍱|🚨|🔇|👥|🚚|📄|⚗️|🏷️|🌐|💫|🗑️|🔊|🔇|🐛|💩|⏪|🔀|📦|👽|🚚|📱|🤡|🥚|🙈|📸|⚗️|🔍|🏷️|🌱|🚩|💥|🍱|♿|💬|🗃️|🔊|📈|⚗️|🔍|🏷️)\s*/, '')
.substring(0, 50);
if (commit.message.length > 50) {
cleanMessage += '...';
}
message += `• \`${commit.hash}\` ${cleanMessage} _${commit.time}_\n`;
});
// 如果有更多提交,显示省略信息
if (commits.length > 10) {
message += `• _... 还有 ${commits.length - 10} 条提交_\n`;
}
message += '\n';
}
// 添加贡献者统计
const contributors = [...new Set(allCommits.map(c => c.author))];
if (contributors.length > 0) {
message += `👥 今日贡献者\n`;
contributors.forEach(author => {
const authorCommits = allCommits.filter(c => c.author === author).length;
message += `• ${author}: ${authorCommits} 次提交\n`;
});
message += '\n';
}
// 添加时间戳
message += `⏰ 报告生成时间: ${new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })}`;
// 检查消息长度,Telegram 限制为 4096 字符
if (message.length > 4000) {
console.warn('⚠️ 消息过长,进行截断处理');
message = message.substring(0, 3900) + '\n\n_... 消息过长已截断_';
}
console.log('📝 生成的消息:');
console.log(message);
// 发送到 Telegram
sendToTelegram(message);
function sendToTelegram(text) {
const data = querystring.stringify({
chat_id: CHAT_ID,
text: text,
disable_web_page_preview: true
});
const options = {
hostname: 'api.telegram.org',
port: 443,
path: `/bot${BOT_TOKEN}/sendMessage`,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(data)
}
};
const req = https.request(options, (res) => {
let responseData = '';
res.on('data', (chunk) => {
responseData += chunk;
});
res.on('end', () => {
try {
const response = JSON.parse(responseData);
if (response.ok) {
console.log('✅ 消息已成功发送到 Telegram');
} else {
console.error('❌ Telegram API 错误:', response.description);
process.exit(1);
}
} catch (error) {
console.error('❌ 解析响应失败:', error.message);
console.error('响应内容:', responseData);
process.exit(1);
}
});
});
req.on('error', (error) => {
console.error('❌ 发送请求失败:', error.message);
process.exit(1);
});
req.write(data);
req.end();
}
EOF
node commit-summary.js