feat(rss): 添加RSS订阅功能并优化日期处理
- 在DEPLOYMENT.md中添加RSS_FEED_URL环境变量配置说明 - 修改日期处理函数,统一使用GMT时区格式 - 实现RSS订阅功能,支持通过Feedly等阅读器订阅 - 优化GitHub API调用的Base64编解码处理 - 更新README展示RSS订阅链接和访问方式
This commit is contained in:
11
README.md
11
README.md
@@ -59,8 +59,13 @@
|
||||
|
||||
**在线阅读地址:**
|
||||
|
||||
* 🌐 **主站点(GitHub Pages )**:[website-1](https://justlovemaki.github.io/CloudFlare-AI-Insight-Daily/today/book/)
|
||||
* 📖 **备用站点(Cloudflare)**:[website-2](https://ai-today.justlikemaki.vip/)
|
||||
你可以通过以下任一方式访问每日生成的最新资讯:
|
||||
|
||||
| 访问方式 | 链接 | 状态 |
|
||||
| ----------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- |
|
||||
| 📡 **RSS 订阅** | [https://justlovemaki.github.io/CloudFlare-AI-Insight-Daily/rss.xml](https://justlovemaki.github.io/CloudFlare-AI-Insight-Daily/rss.xml) | 推荐使用 [Feedly](https://feedly.com/), [Folo](https://app.follow.is/) 等阅读器 |
|
||||
| 🌐 **主站点 (GitHub)** | [https://justlovemaki.github.io/CloudFlare-AI-Insight-Daily/today/book/](https://justlovemaki.github.io/CloudFlare-AI-Insight-Daily/today/book/) | ✅ 稳定 🚀 速度 |
|
||||
| 📖 **备用站点 (Cloudflare)** | [https://ai-today.justlikemaki.vip/](https://ai-today.justlikemaki.vip/) | |
|
||||
|
||||
**内容成果展示:**
|
||||
|
||||
@@ -70,7 +75,7 @@
|
||||
|  |  |
|
||||
|
||||
|
||||
**项目截图:**
|
||||
**后台项目截图:**
|
||||
|
||||
| 网站首页 | 日报内容 | 播客脚本 |
|
||||
| -------------------------------------- | -------------------------------------- | -------------------------------------- |
|
||||
|
||||
@@ -172,6 +172,7 @@ TWITTER_FETCH_PAGES = "2"
|
||||
* 在您的 GitHub 仓库页面,进入 `Settings` -> `Secrets and variables` -> `Actions`。
|
||||
* 在 `Variables` 标签页,点击 `New repository variable`。
|
||||
* 创建一个名为 `IMAGE_PROXY_URL` 的变量,值为您的代理服务地址,例如 `https://your-proxy.com/`。
|
||||
* 创建一个名为 `RSS_FEED_URL` 的变量,值为您的后端服务地址,例如 `https://your-backend.com/rss`。
|
||||
|
||||
4. **🚀 触发 Action 并验证**
|
||||
* 手动触发一次 `build-daily-book` 工作流,或等待其定时自动执行。
|
||||
|
||||
17
src/foot.js
17
src/foot.js
@@ -1,15 +1,14 @@
|
||||
export function insertFoot() {
|
||||
return `
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
**收听语音版**
|
||||
|
||||
| 🎙️ **小宇宙** | 📹 **抖音** |
|
||||
| --- | --- |
|
||||
| [来生小酒馆](https://www.xiaoyuzhoufm.com/podcast/683c62b7c1ca9cf575a5030e) | [来生情报站](https://www.douyin.com/user/MS4wLjABAAAAwpwqPQlu38sO38VyWgw9ZjDEnN4bMR5j8x111UxpseHR9DpB6-CveI5KRXOWuFwG)|
|
||||
|  |  |
|
||||
|
||||
**收听语音版**
|
||||
|
||||
| 🎙️ **小宇宙** | 📹 **抖音** |
|
||||
| --- | --- |
|
||||
| [来生小酒馆](https://www.xiaoyuzhoufm.com/podcast/683c62b7c1ca9cf575a5030e) | [来生情报站](https://www.douyin.com/user/MS4wLjABAAAAwpwqPQlu38sO38VyWgw9ZjDEnN4bMR5j8x111UxpseHR9DpB6-CveI5KRXOWuFwG)|
|
||||
|  |  |
|
||||
|
||||
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ export async function getGitHubFileSha(env, filePath) {
|
||||
*/
|
||||
export async function createOrUpdateGitHubFile(env, filePath, content, commitMessage, existingSha = null) {
|
||||
const GITHUB_BRANCH = env.GITHUB_BRANCH || 'main';
|
||||
const base64Content = btoa(String.fromCharCode(...new TextEncoder().encode(content)));
|
||||
const base64Content = b64EncodeUnicode(content);
|
||||
|
||||
const payload = {
|
||||
message: commitMessage,
|
||||
@@ -102,20 +102,41 @@ export async function getDailyReportContent(env, filePath) {
|
||||
throw new Error("GitHub API configuration is missing in environment variables.");
|
||||
}
|
||||
|
||||
const rawUrl = `https://raw.githubusercontent.com/${GITHUB_REPO_OWNER}/${GITHUB_REPO_NAME}/${GITHUB_BRANCH}/${filePath}`;
|
||||
console.log(rawUrl)
|
||||
try {
|
||||
const response = await fetch(rawUrl);
|
||||
if (!response.ok) {
|
||||
if (response.status === 404) {
|
||||
console.log(`File not found: ${filePath} on branch ${GITHUB_BRANCH}`);
|
||||
return null;
|
||||
}
|
||||
throw new Error(`Failed to fetch file from GitHub: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
return await response.text();
|
||||
const data = await callGitHubApi(env, `/contents/${filePath}?ref=${GITHUB_BRANCH}`);
|
||||
return b64DecodeUnicode(data.content);
|
||||
} catch (error) {
|
||||
console.error(`Error fetching daily report content from ${rawUrl}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Base64 encode (UTF-8 safe)
|
||||
function b64EncodeUnicode(str) {
|
||||
// Replacing '+' with '-' and '/' with '_' makes it URL-safe, but GitHub API expects standard Base64
|
||||
// Using btoa directly after encodeURIComponent is standard
|
||||
try {
|
||||
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g,
|
||||
function toSolidBytes(match, p1) {
|
||||
return String.fromCharCode('0x' + p1);
|
||||
}));
|
||||
} catch (e) {
|
||||
console.error("Base64 Encoding Error:", e);
|
||||
showStatus("Error: Could not encode content for GitHub.", true);
|
||||
return null; // Return null on error
|
||||
}
|
||||
}
|
||||
|
||||
// Base64 decode (UTF-8 safe)
|
||||
function b64DecodeUnicode(str) {
|
||||
try {
|
||||
// Standard Base64 decoding
|
||||
return decodeURIComponent(atob(str).split('').map(function(c) {
|
||||
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
||||
}).join(''));
|
||||
} catch(e) {
|
||||
console.error("Base64 Decoding Error:", e);
|
||||
showStatus("Error: Could not decode file content from GitHub.", true);
|
||||
return null; // Return null on error
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// src/handlers/commitToGitHub.js
|
||||
import { getISODate, formatMarkdownText, replaceImageProxy,formatDateToChineseWithTime } from '../helpers.js';
|
||||
import { getISODate, formatMarkdownText, replaceImageProxy,formatDateToGMT0WithTime, sleep } from '../helpers.js';
|
||||
import { getGitHubFileSha, createOrUpdateGitHubFile } from '../github.js';
|
||||
import { storeInKV } from '../kv.js';
|
||||
import { marked } from '../marked.esm.js';
|
||||
@@ -19,7 +19,7 @@ export async function handleCommitToGitHub(request, env) {
|
||||
link: '/daily/'+dateStr+'.html',
|
||||
content_html: null,
|
||||
// 可以添加其他相關欄位,例如作者、來源等
|
||||
published_date: formatDateToChineseWithTime(new Date()) // 記錄保存時間
|
||||
published_date: formatDateToGMT0WithTime(new Date()) // 記錄保存時間
|
||||
}
|
||||
|
||||
const filesToCommit = [];
|
||||
|
||||
@@ -229,7 +229,7 @@ export async function handleGenAIContent(request, env) {
|
||||
if (fullPromptForCall2_User) promptsMarkdownContent += `### User Input (Output of Call 1)\n\`\`\`\n${fullPromptForCall2_User}\n\`\`\`\n\n`;
|
||||
|
||||
let dailySummaryMarkdownContent = `# ${env.DAILY_TITLE} ${formatDateToChinese(dateStr)}\n\n${removeMarkdownCodeBlock(outputOfCall2)}`;
|
||||
if (env.INSERT_FOOT) dailySummaryMarkdownContent += insertFoot() +`\n\n`;
|
||||
if (env.INSERT_FOOT=='true') dailySummaryMarkdownContent += insertFoot() +`\n\n`;
|
||||
|
||||
const successHtml = generateGenAiPageHtml(
|
||||
env,
|
||||
|
||||
@@ -23,7 +23,6 @@ export async function handleRss(request, env) {
|
||||
|
||||
const allData = [];
|
||||
const today = getShanghaiTime(); // 加上東八時區的偏移量
|
||||
console.log(today);
|
||||
|
||||
for (let i = 0; i < days; i++) {
|
||||
const date = new Date(today);
|
||||
@@ -58,7 +57,7 @@ export async function handleRss(request, env) {
|
||||
const finalData = Object.values(filteredData);
|
||||
|
||||
finalData.forEach(item => {
|
||||
const pubDate = item.published_date ? formatRssDate(new Date(item.published_date)) : formatRssDate(new Date());
|
||||
const pubDate = formatRssDate(new Date(item.published_date));
|
||||
const content = minifyHTML(item.content_html);
|
||||
const title = item.title || '无标题';
|
||||
const link = env.BOOK_LINK+item.link || '#';
|
||||
@@ -84,7 +83,7 @@ export async function handleRss(request, env) {
|
||||
<link>${env.BOOK_LINK}</link>
|
||||
<description> 近 ${days} 天的AI日报</description>
|
||||
<language>zh-cn</language>
|
||||
<lastBuildDate>${formatRssDate(new Date())}</lastBuildDate>
|
||||
<lastBuildDate>${formatRssDate()}</lastBuildDate>
|
||||
<atom:link href="${url.origin}/rss" rel="self" type="application/rss+xml" />
|
||||
${rssItems}
|
||||
</channel>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { replaceImageProxy, formatDateToChineseWithTime } from '../helpers.js';
|
||||
import { replaceImageProxy, formatDateToGMT0WithTime } from '../helpers.js';
|
||||
import { getDailyReportContent } from '../github.js';
|
||||
import { storeInKV } from '../kv.js';
|
||||
import { marked } from '../marked.esm.js';
|
||||
@@ -24,16 +24,17 @@ export async function handleWriteRssData(request, env) {
|
||||
link: '/daily/'+dateStr+'.html',
|
||||
content_html: null,
|
||||
// 可以添加其他相關欄位,例如作者、來源等
|
||||
published_date: formatDateToChineseWithTime(new Date()) // 記錄保存時間
|
||||
published_date: formatDateToGMT0WithTime(new Date()) // 記錄保存時間
|
||||
}
|
||||
report.content_html = marked.parse(replaceImageProxy(env.IMG_PROXY, content));
|
||||
storeInKV(env.DATA_KV, `${dateStr}-report`, report);
|
||||
|
||||
return new Response(JSON.stringify({ message: `Successfully fetched and stored daily report for ${dateStr}`}), {
|
||||
return new Response(JSON.stringify(report), {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
status: 200
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error handling daily report:', error);
|
||||
console.error('Error handling daily report:', error.message);
|
||||
return new Response(`Error handling daily report: ${error.message}`, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -231,20 +231,29 @@ export function formatDateToChineseWithTime(isoDateString) {
|
||||
* @returns {string} 格式化後的日期字串
|
||||
*/
|
||||
export function formatRssDate(date) {
|
||||
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
|
||||
const dayOfWeek = days[date.getUTCDay()];
|
||||
const dayOfMonth = date.getUTCDate();
|
||||
const month = months[date.getUTCMonth()];
|
||||
const year = date.getUTCFullYear();
|
||||
const hours = String(date.getUTCHours()).padStart(2, '0');
|
||||
const minutes = String(date.getUTCMinutes()).padStart(2, '0');
|
||||
const seconds = String(date.getUTCSeconds()).padStart(2, '0');
|
||||
|
||||
return `${dayOfWeek}, ${dayOfMonth} ${month} ${year} ${hours}:${minutes}:${seconds} GMT`;
|
||||
if (!date) return new Date().toUTCString();
|
||||
|
||||
return date.toUTCString();
|
||||
}
|
||||
|
||||
|
||||
export function formatDateToGMT0WithTime(isoDateString) {
|
||||
if (!isoDateString) return '';
|
||||
const date = new Date(isoDateString);
|
||||
const options = {
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false, // 使用24小时制
|
||||
timeZone: 'GMT'
|
||||
};
|
||||
// 使用 'zh-CN' 语言环境以确保中文格式
|
||||
return new Intl.DateTimeFormat('zh-CN', options).format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts English double quotes (") to Chinese double quotes (“”).
|
||||
* @param {string} text - The input string.
|
||||
|
||||
Reference in New Issue
Block a user