Initialize project structure with Bun, Elysia, and AI-powered article summarization
This commit is contained in:
175
.gitignore
vendored
Normal file
175
.gitignore
vendored
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
|
||||||
|
logs
|
||||||
|
_.log
|
||||||
|
npm-debug.log_
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Caches
|
||||||
|
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
|
||||||
|
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
|
||||||
|
pids
|
||||||
|
_.pid
|
||||||
|
_.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
|
||||||
|
.temp
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
# IntelliJ based IDEs
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Finder (MacOS) folder config
|
||||||
|
.DS_Store
|
||||||
13
2025-2-10.md
Normal file
13
2025-2-10.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# 总结
|
||||||
|
|
||||||
|
**核心观点:**
|
||||||
|
|
||||||
|
* **指数层面:** 虽然今天收小阳线,但存在隐患,主要问题是成交量大幅萎缩,即便突破3322点(60日线)问题可能更大。下午的上攻伴随量能萎缩,呈现量价背离,需要警惕。
|
||||||
|
* **操作建议:** 如果明天高开个股出现进攻衰竭信号,可适当减仓,以防量能持续萎缩导致市场降温。但由于今天只是第一天量价不匹配,若未来量能能再次放大,则个股仍有操作空间。
|
||||||
|
* **仓位:** 个人维持5-5.5成仓位,消费和医药持仓为主,不考虑加仓。目前位置不适合中线布局,除非是长线布局。
|
||||||
|
* **DS题材(可能指数字经济或算力概念):** 目前已进入深度博傻阶段,等待接盘侠。三大运营商(电信、联通)必须持续冲新高,否则可能面临负反馈。资金不会立刻撤出DS,仍会在科技股中流动,直到更大的利好出现。
|
||||||
|
* **风险提示:** 不建议追高DS题材,现在入场已经太晚,如果要参与只能小仓位短线投机,并设置止损。不要盲目相信科技信仰,避免高位接盘。
|
||||||
|
|
||||||
|
**总结:**
|
||||||
|
|
||||||
|
作者认为当前市场存在量价背离的隐患,建议谨慎操作,注意控制仓位。对于近期火热的DS题材,作者持谨慎态度,认为已进入博傻阶段,不建议追高。
|
||||||
15
README.md
Normal file
15
README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# info_flow
|
||||||
|
|
||||||
|
To install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
To run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run index.js
|
||||||
|
```
|
||||||
|
|
||||||
|
This project was created using `bun init` in bun v1.1.37. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
|
||||||
15
bak/ai.js
Normal file
15
bak/ai.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// ai.js
|
||||||
|
export async function analyzeContent(content) {
|
||||||
|
// 假设DeepSeek API的调用方式
|
||||||
|
const response = await fetch("https://api.deepseek.com/v1/analyze", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": "Bearer sk-75972dd6431e4440a0428fa8922ed6b1",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ content }),
|
||||||
|
});
|
||||||
|
const result = await response.json();
|
||||||
|
console.log('Result:', result);
|
||||||
|
return result.summary; // 假设返回的是Markdown格式的总结
|
||||||
|
}
|
||||||
21
bak/rss.js
Normal file
21
bak/rss.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// ... existing code ...
|
||||||
|
export async function fetchRSS(url, maxRetries = 3) {
|
||||||
|
let retries = 0;
|
||||||
|
while (retries < maxRetries) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, { verbose: true, keepalive: true, timeout: 10000, compress: false });
|
||||||
|
// console.log('Response:', response);
|
||||||
|
const text = await response.text();
|
||||||
|
// console.log('Response Text:', text);
|
||||||
|
return text;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Fetch Error (Attempt ${retries + 1}):`, error);
|
||||||
|
retries++;
|
||||||
|
// 等待一段时间再重试,避免过于频繁的请求
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.error('Fetch failed after multiple retries.');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// ... existing code ...
|
||||||
29
bak/server.js
Normal file
29
bak/server.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// server.js
|
||||||
|
import { serve } from "bun";
|
||||||
|
import { fetchRSS } from "./rss.js";
|
||||||
|
import { analyzeContent } from "./ai.js";
|
||||||
|
import { getWeChatArticleLinks } from "./wechat.js";
|
||||||
|
const PORT = 3000;
|
||||||
|
|
||||||
|
async function handleRequest(request) {
|
||||||
|
// 从环境变量中获取 RSS URL,如果没有则使用默认值
|
||||||
|
const rssUrl = "https://mp.weixin.qq.com/s/vQgsMuxXffpFZkNFj89wUQ";
|
||||||
|
try {
|
||||||
|
const rssContent = await fetchRSS(rssUrl);
|
||||||
|
const analyzedContent = await analyzeContent(rssContent);
|
||||||
|
|
||||||
|
return new Response(analyzedContent, {
|
||||||
|
headers: { "Content-Type": "text/markdown" },
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error processing request:", error);
|
||||||
|
return new Response("Error processing request", { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serve({
|
||||||
|
port: PORT,
|
||||||
|
fetch: handleRequest,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Server running at http://localhost:${PORT}`);
|
||||||
24
bak/wechat.js
Normal file
24
bak/wechat.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { JSDOM } from 'jsdom';
|
||||||
|
|
||||||
|
async function getWeChatArticleLinks(publicAccountName) {
|
||||||
|
// const searchUrl = `https://weixin.sogou.com/weixin?p=01030402&query=${encodeURIComponent(publicAccountName)}&type=2&ie=utf8`;
|
||||||
|
// const response = await fetch(searchUrl);
|
||||||
|
console.log(`https://mp.weixin.qq.com/mp/profile_ext?action=getmsg&__biz=${publicAccountName}`);
|
||||||
|
const response = await fetch(`https://mp.weixin.qq.com/mp/profile_ext?action=getmsg&__biz=${publicAccountName}`, {
|
||||||
|
headers: {
|
||||||
|
'Referer': `https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=${publicAccountName}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const html = await response.text();
|
||||||
|
|
||||||
|
const { document } = new JSDOM(html).window;
|
||||||
|
const articleLinks = [];
|
||||||
|
const articleElements = document.querySelectorAll('.news-box .news-list li a');
|
||||||
|
articleElements.forEach((element) => {
|
||||||
|
const link = element.href;
|
||||||
|
articleLinks.push(link);
|
||||||
|
});
|
||||||
|
return articleLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { getWeChatArticleLinks };
|
||||||
0
config.json
Normal file
0
config.json
Normal file
1
global.d.ts
vendored
Normal file
1
global.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
declare module '@elysiajs/html';
|
||||||
78
history/2025/2/10.md
Normal file
78
history/2025/2/10.md
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
好的,我已经收到了你提供的三篇文章。现在我将对它们进行分析,并为你设计短、中、长线的投资策略。
|
||||||
|
|
||||||
|
**一、文章核心内容总结:**
|
||||||
|
|
||||||
|
* **文章1(2.10日收盘总结):**
|
||||||
|
* **市场隐患:** 量能大幅缩量,下午出现量价背离,进攻衰竭信号。
|
||||||
|
* **操作建议:** 尾盘/明早高开出现进攻衰竭信号的个股减仓。如果后续量能放大或维持常态,则个股仍可操作,不轻易大减仓。
|
||||||
|
* **仓位建议:** 维持5-5.5成仓位。消费和医药持仓为主,不加仓。
|
||||||
|
* **投资策略:** 中线布局意义不大,除非是半年以上不动的大长线布局。
|
||||||
|
* **Deepseek (DS) 风险:** 深度博傻阶段,等接盘侠。三大运营商必须继续冲新高,否则负反馈可能开始。不建议追高,手痒玩玩投机仓短线带好止损。资金仍在科技股里流动,等待更大层面的重磅利好。
|
||||||
|
* **文章2 (阳气乍露,日之初升):**
|
||||||
|
* **市场定性:** 情绪周期为上升。
|
||||||
|
* **操作建议:** 关注拓维信息、华胜天成,给机会可考虑上车。
|
||||||
|
* **Deepseek (DS) 策略:** DS是市场最强主线,等待DS大分歧时上车补票。前排给机会大胆上车。
|
||||||
|
* **文章3 (重要提示):**
|
||||||
|
* **应对策略:** 在节奏感不好的时候,耐心等待一个看得懂的极致最票的模式内机会。
|
||||||
|
* **龙头战法:** 行情超预期走强后,重新改变行情定性,出现龙头强分机会。
|
||||||
|
* **案例:** 每日互动 (DS概念),行情级别在确定性扩大加强,作为市场最强票,绕不过去。
|
||||||
|
* **强趋势股梳理:** 列举了云服务、DS、哪吒、AI医疗、端侧、智驾、机器人等方向的个股,作为一段时间内的观察标的。
|
||||||
|
* **主要题材梳理:** DS概念、中兵系重组。
|
||||||
|
|
||||||
|
**二、投资策略设计:**
|
||||||
|
|
||||||
|
综合以上三篇文章的信息,我可以为你设计一个短中长线的投资策略,请注意,这只是根据提供的资料做出的建议,实际投资需要结合你的风险承受能力、资金情况和市场变化灵活调整。
|
||||||
|
|
||||||
|
**1. 短线策略(1-3周):**
|
||||||
|
|
||||||
|
* **核心:** 把握Deepseek (DS) 的短线机会,但控制风险。
|
||||||
|
* **选股:**
|
||||||
|
* **激进型:** 关注DS概念中的龙头个股,如文章3提到的每日互动,或者其他文中提及的DS概念股。
|
||||||
|
* **稳健型:** 等待DS概念出现大分歧时,选择调整充分、基本面较好的个股逢低介入。关注文章2提到的拓维信息、华胜天成,若调整到位可考虑。
|
||||||
|
* **操作:**
|
||||||
|
* 严格止损:因为DS概念处于博傻阶段,一旦出现亏损,立即止损。文章1建议手痒玩玩投机仓短线带好止损。
|
||||||
|
* 快进快出:不恋战,赚取快钱,避免成为接盘侠。
|
||||||
|
* 关注量价关系:文章1提到量价背离是风险信号,要警惕。
|
||||||
|
* **仓位:** 短线仓位控制在总仓位的10-20%。
|
||||||
|
|
||||||
|
**2. 中线策略(3个月-1年):**
|
||||||
|
|
||||||
|
* **核心:** 围绕科技主线,选择有业绩支撑、成长性较好的个股,适当关注消费和医药。
|
||||||
|
* **选股:**
|
||||||
|
* **科技:** 从云服务、AI医疗、端侧、智驾、机器人等方向中,选择细分行业的龙头企业。
|
||||||
|
* **消费/医药:** 持有原有仓位,不加仓。
|
||||||
|
* **操作:**
|
||||||
|
* 逢低布局:选择在回调时逐步建仓。
|
||||||
|
* 价值投资:关注公司的基本面,长期持有。
|
||||||
|
* **仓位:** 中线仓位控制在总仓位的30-40%。
|
||||||
|
|
||||||
|
**3. 长线策略(1年以上):**
|
||||||
|
|
||||||
|
* **核心:** 低位布局,长期持有,耐心等待。
|
||||||
|
* **选股:**
|
||||||
|
* **低估值潜力股:** 寻找被市场低估的、有长期发展潜力的个股。
|
||||||
|
* **行业龙头:** 选择具有长期竞争优势的行业龙头企业。
|
||||||
|
* **操作:**
|
||||||
|
* 分批建仓:在低位分批买入,降低成本。
|
||||||
|
* 长期持有:不轻易交易,享受企业成长带来的收益。
|
||||||
|
* **仓位:** 长线仓位控制在总仓位的30-40%。
|
||||||
|
|
||||||
|
**4. 整体仓位控制:**
|
||||||
|
|
||||||
|
* 根据你的风险承受能力,将总仓位控制在50-70%。
|
||||||
|
* 短、中、长线仓位的比例可以根据市场情况进行调整。
|
||||||
|
* 保留一定的现金,以备不时之需。
|
||||||
|
|
||||||
|
**5. 风险提示:**
|
||||||
|
|
||||||
|
* 股市有风险,投资需谨慎。
|
||||||
|
* 密切关注市场动态,及时调整投资策略。
|
||||||
|
* 不要盲目跟风,要有自己的判断。
|
||||||
|
* 控制好仓位,避免过度投资。
|
||||||
|
|
||||||
|
**6. 需要进一步考虑的问题:**
|
||||||
|
|
||||||
|
* **文章1中提到“其他更大更高层面的重磅利好”是什么?** 这个利好出现时,可能会打破DS的局面,需要密切关注。
|
||||||
|
* **中兵系重组的机会如何?** 文章3提到了中兵系重组,可以进一步研究相关标的。
|
||||||
|
|
||||||
|
希望这个策略能给你一些参考。记住,投资需要不断学习和实践,才能找到最适合自己的方法。祝你投资顺利!
|
||||||
143
index.ts
Normal file
143
index.ts
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
import { Elysia } from "elysia";
|
||||||
|
import { html } from '@elysiajs/html'
|
||||||
|
import { JSDOM } from 'jsdom';
|
||||||
|
import { GoogleGenerativeAI } from "@google/generative-ai";
|
||||||
|
|
||||||
|
const genAI = new GoogleGenerativeAI(process.env.API_KEY || "");
|
||||||
|
|
||||||
|
const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash-lite-preview-02-05" });
|
||||||
|
|
||||||
|
async function callGeminiAPI(articleContent: string): Promise<any> {
|
||||||
|
// articleContent = `You are a helpful assistant that summarizes WeChat articles.use Chinese: ${articleContent}`;
|
||||||
|
// const response = (await model.generateContent(articleContent)).response;
|
||||||
|
// return response.text();
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
GoogleGenerativeAI,
|
||||||
|
HarmCategory,
|
||||||
|
HarmBlockThreshold,
|
||||||
|
} = require("@google/generative-ai");
|
||||||
|
|
||||||
|
const apiKey = process.env.API_KEY;
|
||||||
|
const genAI = new GoogleGenerativeAI(apiKey);
|
||||||
|
|
||||||
|
const model = genAI.getGenerativeModel({
|
||||||
|
model: "gemini-2.0-flash",
|
||||||
|
});
|
||||||
|
|
||||||
|
const generationConfig = {
|
||||||
|
temperature: 1,
|
||||||
|
topP: 0.95,
|
||||||
|
topK: 40,
|
||||||
|
maxOutputTokens: 8192,
|
||||||
|
responseMimeType: "text/plain",
|
||||||
|
};
|
||||||
|
|
||||||
|
async function run(articleContent: string) {
|
||||||
|
const chatSession = model.startChat({
|
||||||
|
generationConfig,
|
||||||
|
history: [
|
||||||
|
{
|
||||||
|
role: "user",
|
||||||
|
parts: [
|
||||||
|
{ text: "接下来我会给你提供几篇文章,用---和换行分割。如果是投资主题,我希望你能帮我总结和设计出一个短中长线的投资策略:" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "model",
|
||||||
|
parts: [
|
||||||
|
{ text: "请提供你说的文章,我会根据内容总结并设计投资策略。你需要将几篇文章用`---`和换行分割,像你例子里那样。" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "user",
|
||||||
|
parts: [
|
||||||
|
{ text: articleContent },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await chatSession.sendMessage("INSERT_INPUT_HERE");
|
||||||
|
console.log(result.response.text());
|
||||||
|
return result.response.text();
|
||||||
|
}
|
||||||
|
|
||||||
|
return run(articleContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Elysia()
|
||||||
|
.use(html())
|
||||||
|
.get("/", () => `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>文章总结</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>输入微信文章链接</h1>
|
||||||
|
<form action="/summarize" method="post">
|
||||||
|
<label for="articleUrl">文章链接:</label><br>
|
||||||
|
<textarea id="articleUrl" name="articleUrl" rows="4" cols="50"></textarea><br><br>
|
||||||
|
<input type="submit" value="提交">
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`)
|
||||||
|
.post("/summarize", async ({ body }) => {
|
||||||
|
try {
|
||||||
|
const { articleUrl } = body as { articleUrl: string };
|
||||||
|
const urls = articleUrl.split('\n').filter(url => url.trim() !== '');
|
||||||
|
|
||||||
|
async function fetchArticle(url: string): Promise<string> {
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to fetch article: Status Code ${response.status}`);
|
||||||
|
}
|
||||||
|
return await response.text();
|
||||||
|
}
|
||||||
|
|
||||||
|
let articleText = '';
|
||||||
|
for (const url of urls) {
|
||||||
|
try {
|
||||||
|
const articleHTML = await fetchArticle(url);
|
||||||
|
const dom = new JSDOM(articleHTML);
|
||||||
|
const jsContent = dom.window.document.querySelector("#js_content");
|
||||||
|
const content = jsContent ? jsContent.textContent : "";
|
||||||
|
articleText += content + '\n---\n';
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to process article from ${url}:`, error);
|
||||||
|
// Optionally, you might want to handle the error more gracefully,
|
||||||
|
// such as skipping the article or returning a default value.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return articleText;
|
||||||
|
const geminiResponse = await callGeminiAPI(articleText);
|
||||||
|
const today = new Date();
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const fileName = `history/${today.getFullYear()}/${today.getMonth() + 1}/${today.getDate()}.md`;
|
||||||
|
const dir = path.dirname(fileName);
|
||||||
|
|
||||||
|
if (!fs.existsSync(dir)) {
|
||||||
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(fileName, geminiResponse);
|
||||||
|
return new Response(geminiResponse, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "text/markdown; charset=utf-8",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error("Error:", error);
|
||||||
|
return { error: error.message };
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.listen({ port: 3000 });
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`
|
||||||
|
);
|
||||||
27
jsconfig.json
Normal file
27
jsconfig.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// Enable latest features
|
||||||
|
"lib": ["ESNext", "DOM"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"allowJs": true,
|
||||||
|
|
||||||
|
// Bundler mode
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false
|
||||||
|
}
|
||||||
|
}
|
||||||
20
package.json
Normal file
20
package.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "info_flow",
|
||||||
|
"module": "index.js",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.0.0"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"dependencies": {
|
||||||
|
"@elysiajs/html": "^1.2.0",
|
||||||
|
"@google/generative-ai": "^0.21.0",
|
||||||
|
"cheerio": "^1.0.0",
|
||||||
|
"dotenv": "^16.4.7",
|
||||||
|
"elysia": "^1.2.12",
|
||||||
|
"jsdom": "^26.0.0",
|
||||||
|
"openai": "^4.83.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
11
style.css
Normal file
11
style.css
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/* style.css */
|
||||||
|
body {
|
||||||
|
max-width: 768px;
|
||||||
|
margin: 0 auto;
|
||||||
|
font-family: system-ui;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
.timestamp {
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.8em
|
||||||
|
}
|
||||||
16
wechat.js
Normal file
16
wechat.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { JSDOM } from 'jsdom';
|
||||||
|
|
||||||
|
async function getWeChatArticleLinks(publicAccountName) {
|
||||||
|
const html = await response.text();
|
||||||
|
|
||||||
|
const { document } = new JSDOM(html).window;
|
||||||
|
const articleLinks = [];
|
||||||
|
const articleElements = document.querySelectorAll('.news-box .news-list li a');
|
||||||
|
articleElements.forEach((element) => {
|
||||||
|
const link = element.href;
|
||||||
|
articleLinks.push(link);
|
||||||
|
});
|
||||||
|
return articleLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { getWeChatArticleLinks };
|
||||||
Reference in New Issue
Block a user