144 lines
4.4 KiB
TypeScript
144 lines
4.4 KiB
TypeScript
|
|
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}`
|
||
|
|
);
|