diff --git a/.next/trace b/.next/trace
deleted file mode 100644
index de78b322..00000000
--- a/.next/trace
+++ /dev/null
@@ -1 +0,0 @@
-[{"name":"generate-buildid","duration":145,"timestamp":29782277938,"id":4,"parentId":1,"tags":{},"startTime":1779266763571,"traceId":"3a8c4178be1354d5"},{"name":"load-custom-routes","duration":254,"timestamp":29782278150,"id":5,"parentId":1,"tags":{},"startTime":1779266763572,"traceId":"3a8c4178be1354d5"},{"name":"create-dist-dir","duration":423,"timestamp":29782278422,"id":6,"parentId":1,"tags":{},"startTime":1779266763572,"traceId":"3a8c4178be1354d5"},{"name":"clean","duration":708,"timestamp":29782279361,"id":7,"parentId":1,"tags":{},"startTime":1779266763573,"traceId":"3a8c4178be1354d5"},{"name":"next-build","duration":25156,"timestamp":29782255096,"id":1,"tags":{"buildMode":"default","version":"16.2.6","bundler":"turbopack","failed":true},"startTime":1779266763549,"traceId":"3a8c4178be1354d5"}]
diff --git a/.next/trace-build b/.next/trace-build
deleted file mode 100644
index 33aa9265..00000000
--- a/.next/trace-build
+++ /dev/null
@@ -1 +0,0 @@
-[{"name":"next-build","duration":25156,"timestamp":29782255096,"id":1,"tags":{"buildMode":"default","version":"16.2.6","bundler":"turbopack","failed":true},"startTime":1779266763549,"traceId":"3a8c4178be1354d5"}]
diff --git a/bot/.gitignore b/bot/.gitignore
deleted file mode 100644
index 2d7ec5ce..00000000
--- a/bot/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-.env
-node_modules/
diff --git a/bot/README.md b/bot/README.md
deleted file mode 100644
index 9d56cf62..00000000
--- a/bot/README.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# WriteBot
-
-纯 Node.js,零 npm 依赖。后台管理 `write` 开发服务,提供 Web 控制面板 + Telegram 机器人。
-
-## 快速开始
-
-### 1. 配置
-
-```powershell
-cd bot
-copy .env.example .env
-```
-
-### 2. 启动
-
-```powershell
-node index.js
-```
-
-或双击 `start.bat`。
-
-### 3. 控制面板
-
-浏览器打开:`http://127.0.0.1:8017/`
-
-一键启动/停止 Write、查看状态、复制局域网地址。
-
-### 4. Telegram 机器人
-
-1. 联系 [@BotFather](https://t.me/BotFather),`/newbot` 获得 token。
-2. 联系 [@userinfobot](https://t.me/userinfobot) 获得 chat id。
-3. 填入 `.env`。
-
-命令:`/start` `/stop` `/status` `/ip` `/help`
-
-## 开机自启
-
-已配好启动文件夹的快捷方式(WriteBot.lnk)。开机登录后自动启动。
-
-## 防火墙
-
-首次手机访问不了要放行 8016:
-
-```powershell
-New-NetFirewallRule -DisplayName "Write Dev 8016" -Direction Inbound -Protocol TCP -LocalPort 8016 -Action Allow
-```
diff --git a/bot/index.js b/bot/index.js
deleted file mode 100644
index e5222783..00000000
--- a/bot/index.js
+++ /dev/null
@@ -1,75 +0,0 @@
-// writebot - Telegram bot + HTTP API + Web Admin
-// Zero npm dependencies. Node 18+ built-in everything.
-
-const config = require("./src/config");
-const { getLanIP } = require("./src/helpers");
-const { startWriteServer, stopWriteServer } = require("./src/write-server");
-const { createControlServer } = require("./src/admin-panel");
-const { tg, allowed } = require("./src/tg-core");
-const { tgPoll } = require("./src/tg-poll");
-
-async function main() {
- const ip = getLanIP();
- console.log("");
- console.log(" \\u250C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2510");
- console.log(" \\u2502 WriteBot \\u2502");
- console.log(" \\u251C\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2524");
- console.log(" \\u2502 Admin: http://127.0.0.1:" + config.CONTROL_PORT + "/ \\u2502");
- console.log(" \\u2502 Write: http://" + ip + ":" + config.WRITE_PORT + " \\u2502");
- console.log(" \\u2514\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2518");
- console.log("");
-
- // Start HTTP control server
- const controlServer = createControlServer();
-
- // Connect Telegram
- if (config.TG_TOKEN) {
- try {
- const me = await tg("getMe", {});
- if (me.ok) {
- console.log("[bot] Telegram: @" + me.result.username);
-
- try {
- await tg("setMyCommands", {
- commands: [
- { command: "start", description: "\\u542F\\u52A8\\u5199\\u4F5C\\u540E\\u53F0" },
- { command: "new", description: "\\u5F00\\u59CB\\u5199\\u4E00\\u7BC7" },
- { command: "edit", description: "\\u7F16\\u8F91\\u5DF2\\u6709\\u6587\\u7AE0" },
- { command: "list", description: "\\u6587\\u7AE0\\u5217\\u8868" },
- { command: "stats", description: "\\u7F51\\u7AD9\\u6570\\u636E\\u7EDF\\u8BA1" },
- { command: "publish", description: "\\u53D1\\u5E03\\u5230\\u7F51\\u7AD9" },
- { command: "draft", description: "\\u4FDD\\u5B58\\u4E3A\\u8349\\u7A3F" },
- { command: "preview", description: "\\u9884\\u89C8\\u5168\\u6587" },
- { command: "links", description: "\\u53CB\\u94FE\\u5217\\u8868" },
- { command: "feeds", description: "\\u8BA2\\u9605\\u6E90\\u5217\\u8868" },
- { command: "help", description: "\\u5168\\u90E8\\u547D\\u4EE4" },
- ],
- });
- console.log("[bot] commands registered");
- } catch (e) {
- console.error("[bot] failed to register commands:", e.message);
- }
-
- tgPoll();
- } else {
- console.log("[bot] TG token invalid - HTTP-only mode");
- }
- } catch {
- console.log("[bot] TG connection failed - HTTP-only mode");
- }
- } else {
- console.log("[bot] no TG_TOKEN - HTTP-only mode");
- }
-
- // Graceful shutdown
- for (const sig of ["SIGINT", "SIGTERM"]) {
- process.on(sig, () => {
- console.log("\\n[bot] " + sig + ", stopping...");
- stopWriteServer();
- controlServer.close();
- process.exit(0);
- });
- }
-}
-
-main();
diff --git a/bot/package-lock.json b/bot/package-lock.json
deleted file mode 100644
index 8df4b821..00000000
--- a/bot/package-lock.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "name": "writebot",
- "version": "1.0.0",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "writebot",
- "version": "1.0.0"
- }
- }
-}
diff --git a/bot/package.json b/bot/package.json
deleted file mode 100644
index d3beb48e..00000000
--- a/bot/package.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "name": "writebot",
- "version": "1.0.0",
- "private": true,
- "description": "Telegram bot to start the write dev server on LAN",
- "main": "index.js",
- "scripts": {
- "start": "node index.js"
- }
-}
diff --git a/bot/src/admin-panel.js b/bot/src/admin-panel.js
deleted file mode 100644
index 2443621f..00000000
--- a/bot/src/admin-panel.js
+++ /dev/null
@@ -1,125 +0,0 @@
-// admin-panel.js - Minimal web admin panel + HTTP control API
-const http = require("http");
-const config = require("./config");
-const { getLanIP } = require("./helpers");
-const { startWriteServer, stopWriteServer, getWriteStatus } = require("./write-server");
-
-const ADMIN_HTML = `
-
-
-
-
-WriteBot
-
-
-
-
-
WriteBot
-
Ctrl+D bookmark this page
-
-
Loading...
-
-
-
-
-
-
-
-
-
-
-
-`.replace("var port=8016", "var port=${config.WRITE_PORT}");
-
-function createControlServer() {
- const server = http.createServer((req, res) => {
- const url = new URL(req.url, "http://127.0.0.1:" + config.CONTROL_PORT);
- const path = url.pathname;
- res.setHeader("Access-Control-Allow-Origin", "*");
-
- if (path === "/" && req.method === "GET") {
- res.setHeader("Content-Type", "text/html; charset=utf-8");
- res.end(ADMIN_HTML);
- } else if (path === "/status" && req.method === "GET") {
- res.setHeader("Content-Type", "application/json");
- const s = getWriteStatus();
- s.ip = getLanIP();
- s.port = config.WRITE_PORT;
- res.end(JSON.stringify(s));
- } else if (path === "/start" && req.method === "POST") {
- res.setHeader("Content-Type", "application/json");
- res.end(JSON.stringify(startWriteServer()));
- } else if (path === "/stop" && req.method === "POST") {
- res.setHeader("Content-Type", "application/json");
- res.end(JSON.stringify({ ok: stopWriteServer() }));
- } else if (path === "/ip" && req.method === "GET") {
- res.setHeader("Content-Type", "application/json");
- res.end(JSON.stringify({ ip: getLanIP(), port: config.WRITE_PORT }));
- } else if (path === "/health" && req.method === "GET") {
- res.setHeader("Content-Type", "application/json");
- res.end(JSON.stringify({ ok: true }));
- } else {
- res.statusCode = 404;
- res.setHeader("Content-Type", "application/json");
- res.end(JSON.stringify({ error: "not found" }));
- }
- });
-
- server.listen(config.CONTROL_PORT, "127.0.0.1", () => {
- console.log("[control] http://127.0.0.1:" + config.CONTROL_PORT);
- });
-
- return server;
-}
-
-module.exports = { createControlServer };
diff --git a/bot/src/config.js b/bot/src/config.js
deleted file mode 100644
index a96bf358..00000000
--- a/bot/src/config.js
+++ /dev/null
@@ -1,21 +0,0 @@
-// config.js - Environment and constants
-const { existsSync, readFileSync } = require("fs");
-const { resolve } = require("path");
-
-const envPath = resolve(__dirname, ".env");
-if (existsSync(envPath)) {
- for (const line of readFileSync(envPath, "utf-8").split("\n")) {
- const m = line.match(/^\s*([A-Z_][A-Z0-9_]*)\s*=\s*(.+?)\s*$/);
- if (m && !process.env[m[1]]) process.env[m[1]] = m[2];
- }
-}
-
-module.exports = {
- TG_TOKEN: process.env.TG_BOT_TOKEN || "",
- ALLOWED_IDS: (process.env.TG_ALLOWED_CHAT_IDS || "")
- .split(",").map((s) => s.trim()).filter(Boolean).map(Number),
- WRITE_PORT: parseInt(process.env.WRITE_PORT || "8016", 10),
- CONTROL_PORT: parseInt(process.env.BOT_PORT || "8017", 10),
- PREFER_IFACE: process.env.PREFER_IFACE || "WLAN",
- WRITE_DIR: resolve(__dirname, "..", "write"),
-};
diff --git a/bot/src/helpers.js b/bot/src/helpers.js
deleted file mode 100644
index 44a25541..00000000
--- a/bot/src/helpers.js
+++ /dev/null
@@ -1,76 +0,0 @@
-// helpers.js - Utility functions
-const os = require("os");
-const net = require("net");
-const config = require("./config");
-
-function getLanIP() {
- const now = Date.now();
- if (getLanIP._cache && getLanIP._cache.ip && now - getLanIP._cache.ts < 30000)
- return getLanIP._cache.ip;
- let ip = "0.0.0.0";
- try {
- const ifs = os.networkInterfaces();
- const candidates = [];
- for (const [name, addrs] of Object.entries(ifs)) {
- if (!addrs) continue;
- for (const a of addrs) {
- if (a.family !== "IPv4" || a.internal) continue;
- if (a.address.startsWith("169.254.")) continue;
- candidates.push({ name, address: a.address });
- }
- }
- const pref = candidates.find((c) =>
- c.name.toLowerCase().includes(config.PREFER_IFACE.toLowerCase())
- );
- if (pref) ip = pref.address;
- else {
- const real = candidates.find((c) => {
- const n = c.name.toLowerCase();
- return !n.includes("vmware") && !n.includes("virtual") && !n.includes("vethernet");
- });
- if (real) ip = real.address;
- else if (candidates[0]) ip = candidates[0].address;
- }
- } catch {}
- getLanIP._cache = { ip, ts: now };
- return ip;
-}
-
-function checkPort(port) {
- return new Promise((resolve) => {
- const s = net.createConnection(port, "127.0.0.1", () => { s.destroy(); resolve(true); });
- s.on("error", () => { s.destroy(); resolve(false); });
- s.setTimeout(1500, () => { s.destroy(); resolve(false); });
- });
-}
-
-async function waitForReady(port, timeoutMs) {
- const start = Date.now();
- while (Date.now() - start < (timeoutMs || 30000)) {
- if (await checkPort(port)) return true;
- await new Promise((r) => setTimeout(r, 1500));
- }
- return false;
-}
-
-function makeSlug(title) {
- const dateStr = new Date().toISOString().slice(0, 10);
- const slugPart = (title || "untitled")
- .replace(/[^\w\u4e00-\u9fff]+/g, "-")
- .replace(/^-+|-+$/g, "")
- .toLowerCase() || "post";
- return dateStr + "-" + slugPart;
-}
-
-function sessionSummary(s) {
- const lines = s.content ? s.content.split("\\n").filter(Boolean).length : 0;
- const chars = s.content.length;
- return [
- "\\uD83D\\uDCDD " + (s.title || "\\u672A\\u547D\\u540D") + "",
- s.categories.length ? "\\uD83D\\uDCC2 " + s.categories.join("\\u3001") : "",
- s.tags.length ? "\\uD83C\\uDFF7 " + s.tags.join("\\u3001") : "",
- chars ? "\\u270D " + lines + " \\u884C " + chars + " \\u5B57" : "",
- ].filter(Boolean).join(" \\u00B7 ");
-}
-
-module.exports = { getLanIP, checkPort, waitForReady, makeSlug, sessionSummary };
diff --git a/bot/src/sessions.js b/bot/src/sessions.js
deleted file mode 100644
index c5a2f99c..00000000
--- a/bot/src/sessions.js
+++ /dev/null
@@ -1,59 +0,0 @@
-// sessions.js - Per-chat writing sessions
-const config = require("./config");
-const { makeSlug } = require("./helpers");
-
-const sessions = new Map();
-
-function getSession(chatId) {
- if (!sessions.has(chatId)) {
- sessions.set(chatId, {
- title: "",
- slug: "",
- content: "",
- categories: [],
- tags: [],
- author: "qunlin",
- draft: true,
- });
- }
- return sessions.get(chatId);
-}
-
-function clearSession(chatId) {
- sessions.delete(chatId);
-}
-
-// Save session via Write API
-async function saveViaAPI(s, isDraft) {
- const body = JSON.stringify({
- frontMatter: {
- title: s.title,
- slug: s.slug,
- date: new Date().toISOString().slice(0, 10),
- draft: isDraft,
- categories: s.categories,
- tags: s.tags,
- author: s.author,
- },
- content: s.content,
- });
-
- const checkRes = await fetch("http://127.0.0.1:" + config.WRITE_PORT + "/api/posts/" + s.slug);
- if (checkRes.ok) {
- const res = await fetch("http://127.0.0.1:" + config.WRITE_PORT + "/api/posts/" + s.slug, {
- method: "PUT",
- headers: { "Content-Type": "application/json" },
- body,
- });
- if (!res.ok) throw new Error((await res.json()).error || "API error (PUT)");
- } else {
- const res = await fetch("http://127.0.0.1:" + config.WRITE_PORT + "/api/posts", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body,
- });
- if (!res.ok) throw new Error((await res.json()).error || "API error (POST)");
- }
-}
-
-module.exports = { sessions, getSession, clearSession, saveViaAPI };
diff --git a/bot/src/tg-core.js b/bot/src/tg-core.js
deleted file mode 100644
index 4151ee22..00000000
--- a/bot/src/tg-core.js
+++ /dev/null
@@ -1,54 +0,0 @@
-// tg-core.js - Telegram API helpers
-const config = require("./config");
-
-const API = config.TG_TOKEN ? "https://api.telegram.org/bot" + config.TG_TOKEN : "";
-
-async function tg(method, body) {
- const res = await fetch(API + "/" + method, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(body),
- });
- return res.json();
-}
-
-function allowed(chatId) {
- if (config.ALLOWED_IDS.length === 0) return true;
- return config.ALLOWED_IDS.includes(chatId);
-}
-
-async function sendMessage(chatId, text, opts) {
- const safeText = String(text || "");
- try {
- return await tg("sendMessage", Object.assign(
- { chat_id: chatId, text: safeText, parse_mode: "HTML", disable_web_page_preview: true },
- opts || {}
- ));
- } catch (e) {
- if (e.message && (e.message.includes("parse") || e.message.includes("HTML"))) {
- return await tg("sendMessage", Object.assign(
- { chat_id: chatId, text: safeText },
- opts || {}
- ));
- }
- throw e;
- }
-}
-
-async function handleCallback(chatId, queryId, data, handlers) {
- if (!data) return;
- const parts = data.split("_");
- const cmd = parts[0];
- const page = parseInt(parts[1]) || 1;
-
- if (cmd === "list" && handlers.list) {
- await handlers.list(chatId, String(page));
- } else if (cmd === "links" && handlers.links) {
- await handlers.links(chatId, String(page));
- } else if (cmd === "feeds" && handlers.feeds) {
- await handlers.feeds(chatId, String(page));
- }
- try { await tg("answerCallbackQuery", { callback_query_id: queryId }); } catch {}
-}
-
-module.exports = { tg, allowed, sendMessage, handleCallback };
diff --git a/bot/src/tg-handlers.js b/bot/src/tg-handlers.js
deleted file mode 100644
index 5a838a75..00000000
--- a/bot/src/tg-handlers.js
+++ /dev/null
@@ -1,413 +0,0 @@
-// tg-handlers.js - All Telegram /command handlers
-const config = require("./config");
-const { getLanIP, makeSlug, sessionSummary } = require("./helpers");
-const { getSession, clearSession, sessions, saveViaAPI } = require("./sessions");
-const { getWriteStatus, ensureWriteReady } = require("./write-server");
-const { sendMessage } = require("./tg-core");
-
-const CMD_HANDLERS = {};
-
-// ---------- System ----------
-CMD_HANDLERS.start = async function (chatId) {
- const ip = getLanIP();
- const ready = await ensureWriteReady(chatId, sendMessage);
- if (ready) {
- return sendMessage(chatId,
- "\\u2705 Write \\u5DF2\\u542F\\u52A8\\n\\n" +
- "\\uD83D\\uDCF1 \\u624B\\u673A: http://" + ip + ":8016/\\n" +
- "\\uD83D\\uDCDD \\u6587\\u7AE0: http://" + ip + ":1313/posts/\\n" +
- "\\uD83D\\uDCBB \\u7535\\u8111: http://localhost:8016/\\n\\n" +
- "/help \\u67E5\\u770B\\u5168\\u90E8\\u547D\\u4EE4"
- );
- }
- return sendMessage(chatId, "\\u274C \\u542F\\u52A8\\u8D85\\u65F6\\uFF0C\\u67E5\\u770B\\u65E5\\u5FD7 http://127.0.0.1:" + config.CONTROL_PORT + "/log");
-};
-
-CMD_HANDLERS.stop = async function (chatId) {
- const { stopWriteServer } = require("./write-server");
- if (stopWriteServer()) {
- return sendMessage(chatId, "\\u26A1 Write stopped.");
- }
- return sendMessage(chatId, "\\u23F8 Write is not running.");
-};
-
-CMD_HANDLERS.status = async function (chatId) {
- const s = getWriteStatus();
- const status = s.running ? "\\u2705 Running" : s.starting ? "\\u23F3 Starting..." : "\\u23F8 Stopped";
- const lines = ["Status: " + status];
- if (s.running) {
- const ip = getLanIP();
- lines.push("Local: http://localhost:" + config.WRITE_PORT);
- lines.push("Phone: http://" + ip + ":" + config.WRITE_PORT);
- }
- return sendMessage(chatId, lines.join("\\n"));
-};
-
-CMD_HANDLERS.ip = async function (chatId) {
- const ip = getLanIP();
- return sendMessage(chatId, "Phone: http://" + ip + ":" + config.WRITE_PORT + " (" + config.PREFER_IFACE + ")");
-};
-
-CMD_HANDLERS.help = async function (chatId) {
- if (sessions.has(chatId)) {
- return sendMessage(chatId,
- "\\u270D \\u6B63\\u5728\\u5199\\u4F5C\\u4E2D\\n\\n" +
- "\\u26A1 /publish \\u53D1\\u5E03 \\uD83D\\uDCC4 /draft \\u8349\\u7A3F \\uD83D\\uDD0D /preview \\u9884\\u89C8\\n" +
- "\\u270F /title /categories /tags /author\\n" +
- "\\u274C /cancel \\u4E22\\u5F03"
- );
- }
- return sendMessage(chatId,
- "\\uD83E\\uDD16 WriteBot \\u547D\\u4EE4\\n\\n" +
- "\\uD83D\\uDCDD \\u5199\\u4F5C:\\n" +
- " /new \\u6807\\u9898 - \\u5F00\\u59CB\\u5199\\u4E00\\u7BC7\\n" +
- " /edit slug - \\u7F16\\u8F91\\u5DF2\\u6709\\u6587\\u7AE0\\n" +
- " /delete slug - \\u5220\\u9664\\u6587\\u7AE0\\n" +
- " /list - \\u6587\\u7AE0\\u5217\\u8868\\n\\n" +
- "\\uD83D\\uDD17 \\u53CB\\u94FE:\\n" +
- " /links - \\u67E5\\u770B\\u53CB\\u94FE\\n" +
- " /link_add url name - \\u6DFB\\u52A0\\u53CB\\u94FE\\n" +
- " /link_del url - \\u5220\\u9664\\u53CB\\u94FE\\n" +
- " /link_toggle url - \\u663E\\u793A/\\u9690\\u85CF\\n\\n" +
- "\\uD83D\\uDCE1 \\u8BA2\\u9605:\\n" +
- " /feeds - \\u67E5\\u770B\\u8BA2\\u9605\\u6E90\\n" +
- " /feed_add url - \\u6DFB\\u52A0\\u8BA2\\u9605\\u6E90\\n" +
- " /feed_del url - \\u5220\\u9664\\u8BA2\\u9605\\u6E90\\n\\n" +
- "\\uD83D\\uDCCA /stats \\u7F51\\u7AD9\\u6570\\u636E"
- );
-};
-
-// ---------- Articles ----------
-CMD_HANDLERS.new = async function (chatId, title) {
- if (!title) return sendMessage(chatId, "\\u683C\\u5F0F\\uFF1A\\u5199\\u4E00\\u7BC7 \\u6807\\u9898");
- const s = getSession(chatId);
- s.title = title;
- s.slug = makeSlug(title);
- s.content = "";
- s.categories = [];
- s.tags = [];
- s.draft = true;
- s.author = "qunlin";
- return sendMessage(chatId,
- "\\uD83D\\uDCDD \\u5F00\\u59CB\\u5199\\uFF1A" + title + "\\n\\n" +
- "\\u76F4\\u63A5\\u53D1\\u6D88\\u606F\\u5199\\u4F5C\\uFF0C\\u5B8C\\u6210\\u540E /publish\\n" +
- "\\u968F\\u65F6 /preview \\u67E5\\u770B\\uFF0C/help \\u5168\\u90E8\\u547D\\u4EE4"
- );
-};
-
-CMD_HANDLERS.edit = async function (chatId, slug) {
- if (!slug) return sendMessage(chatId, "\\u683C\\u5F0F\\uFF1A/edit slug\\n\\u5148\\u7528 /list \\u67E5\\u770B\\u6587\\u7AE0 slug\\u3002");
- try {
- const res = await fetch("http://127.0.0.1:" + config.WRITE_PORT + "/api/posts/" + slug);
- if (!res.ok) throw new Error("\\u672A\\u627E\\u5230\\u8BE5\\u6587\\u7AE0");
- const post = await res.json();
- const s = getSession(chatId);
- s.title = post.frontMatter.title;
- s.slug = post.frontMatter.slug;
- s.content = post.content || "";
- s.categories = post.frontMatter.categories || [];
- s.tags = post.frontMatter.tags || [];
- s.author = post.frontMatter.author || "qunlin";
- s.draft = post.frontMatter.draft;
- return sendMessage(chatId, "\\u270F \\u7F16\\u8F91\\uFF1A" + s.title + "\\n\\n" + sessionSummary(s) + "\\n\\n\\u76F4\\u63A5\\u53D1\\u6D88\\u606F\\u8FFD\\u52A0\\u5185\\u5BB9\\uFF0C\\u5199\\u5B8C\\u53D1\\u300C\\u53D1\\u5E03\\u300D\\u3002");
- } catch (e) {
- return sendMessage(chatId, "\\u274C " + e.message);
- }
-};
-
-CMD_HANDLERS.delete = async function (chatId, slug) {
- if (!slug) return sendMessage(chatId, "Usage: /delete \\nFind slugs with /list\\n\\nArticles are moved to recycle bin (30-day retention).");
- try {
- const res = await fetch("http://127.0.0.1:" + config.WRITE_PORT + "/api/posts/" + slug, { method: "DELETE" });
- if (!res.ok) throw new Error((await res.json()).error || "Delete failed");
- return sendMessage(chatId, "\\u2702 Moved to recycle bin: " + slug + "\\nRecoverable within 30 days.");
- } catch (e) {
- return sendMessage(chatId, "\\u274C " + e.message);
- }
-};
-
-CMD_HANDLERS.view = async function (chatId, slug) {
- if (!slug) return sendMessage(chatId, "Usage: /view \\nFind slugs with /list");
- const ip = getLanIP();
- const url = "http://" + ip + ":1313/" + slug + ".html";
- return sendMessage(chatId, "\\uD83D\\uDCC9 Preview\\n\\n" + url + "\\n\\n\\uD83D\\uDCBB http://localhost:1313/" + slug + ".html");
-};
-
-CMD_HANDLERS.title = async function (chatId, newTitle) {
- const s = getSession(chatId);
- if (!newTitle) return sendMessage(chatId, "\\u683C\\u5F0F\\uFF1A\\u6807\\u9898 \\u65B0\\u6807\\u9898");
- s.title = newTitle;
- s.slug = makeSlug(newTitle);
- return sendMessage(chatId, "\\u270F \\u6807\\u9898\\u5DF2\\u66F4\\u65B0\\uFF1A" + newTitle + "");
-};
-
-CMD_HANDLERS.categories = async function (chatId, cats) {
- const s = getSession(chatId);
- s.categories = cats ? cats.split(/[,;\"\\",\\"\\uFF0C\\u3001]+/).map(function(c){return c.trim();}).filter(Boolean) : [];
- return sendMessage(chatId, "\\uD83D\\uDCC2 \\u5206\\u7C7B\\uFF1A" + (s.categories.join("\\u3001") || "\\u5DF2\\u6E05\\u7A7A"));
-};
-
-CMD_HANDLERS.tags = async function (chatId, tags) {
- const s = getSession(chatId);
- s.tags = tags ? tags.split(/[,;\"\\",\\"\\uFF0C\\u3001]+/).map(function(t){return t.trim();}).filter(Boolean) : [];
- return sendMessage(chatId, "\\uD83C\\uDFF7 \\u6807\\u7B7E\\uFF1A" + (s.tags.join("\\u3001") || "\\u5DF2\\u6E05\\u7A7A"));
-};
-
-CMD_HANDLERS.author = async function (chatId, author) {
- const s = getSession(chatId);
- s.author = author || "qunlin";
- return sendMessage(chatId, "\\u270D \\u4F5C\\u8005\\uFF1A" + s.author);
-};
-
-CMD_HANDLERS.preview = async function (chatId) {
- const s = sessions.get(chatId);
- if (!s || !s.title) return sendMessage(chatId, "\\u8FD8\\u6CA1\\u5F00\\u59CB\\u5199\\u3002\\u53D1\\u300C\\u5199\\u4E00\\u7BC7\\u300D\\u5F00\\u59CB\\u3002");
- if (!s.content) return sendMessage(chatId, "\\u6B63\\u6587\\u4E3A\\u7A7A\\u3002");
- const maxLen = 3800;
- const preview = s.content.slice(0, maxLen);
- return sendMessage(chatId, "\\uD83D\\uDCDD " + s.title + "\\n\\n" + preview + (s.content.length > maxLen ? "\\n\\n... \\u524D " + maxLen + " \\u5B57" : ""));
-};
-
-CMD_HANDLERS.draft = async function (chatId) {
- const s = sessions.get(chatId);
- if (!s || !s.title) return sendMessage(chatId, "\\u8FD8\\u6CA1\\u5F00\\u59CB\\u5199\\u3002\\u53D1\\u300C\\u5199\\u4E00\\u7BC7\\u300D\\u5F00\\u59CB\\u3002");
- if (!s.content) return sendMessage(chatId, "\\u6B63\\u6587\\u4E3A\\u7A7A\\uFF0C\\u5148\\u5199\\u70B9\\u5185\\u5BB9\\u518D\\u4FDD\\u5B58\\u3002");
- try {
- await saveViaAPI(s, true);
- clearSession(chatId);
- return sendMessage(chatId, "\\uD83D\\uDCC4 \\u8349\\u7A3F\\u5DF2\\u4FDD\\u5B58\\uFF1A" + s.title + "");
- } catch (e) {
- return sendMessage(chatId, "\\u274C \\u4FDD\\u5B58\\u5931\\u8D25\\uFF1A" + e.message + "\\n\\n\\u8BF7\\u5148 /start \\u542F\\u52A8\\u540E\\u53F0\\u670D\\u52A1\\u3002");
- }
-};
-
-CMD_HANDLERS.publish = async function (chatId) {
- const s = sessions.get(chatId);
- if (!s || !s.title) return sendMessage(chatId, "\\u8FD8\\u6CA1\\u5F00\\u59CB\\u5199\\u3002\\u53D1\\u300C\\u5199\\u4E00\\u7BC7\\u300D\\u5F00\\u59CB\\u3002");
- if (!s.content) return sendMessage(chatId, "\\u6B63\\u6587\\u4E3A\\u7A7A\\uFF0C\\u5148\\u5199\\u70B9\\u5185\\u5BB9\\u518D\\u53D1\\u5E03\\u3002");
- try {
- await saveViaAPI(s, false);
- const slug = s.slug;
- clearSession(chatId);
- const ip = getLanIP();
- return sendMessage(chatId, "\\u2705 \\u5DF2\\u53D1\\u5E03\\uFF01\\n\\n\\uD83D\\uDCDD " + s.title + "\\n\\u9884\\u89C8\\uFF1Ahttp://localhost:1313/" + slug + ".html\\n\\u7EBF\\u4E0A\\uFF1Ahttps://usj.cc/" + slug + ".html");
- } catch (e) {
- return sendMessage(chatId, "\\u274C \\u53D1\\u5E03\\u5931\\u8D25\\uFF1A" + e.message + "\\n\\n\\u8BF7\\u5148 /start \\u542F\\u52A8\\u540E\\u53F0\\u670D\\u52A1\\u3002");
- }
-};
-
-CMD_HANDLERS.cancel = async function (chatId) {
- const s = sessions.get(chatId);
- if (!s || !s.title) return sendMessage(chatId, "\\u6CA1\\u6709\\u6D3B\\u52A8\\u7684\\u8349\\u7A3F\\u3002");
- clearSession(chatId);
- return sendMessage(chatId, "\\u2702 \\u8349\\u7A3F\\u5DF2\\u4E22\\u5F03\\u3002");
-};
-
-CMD_HANDLERS.list = async function (chatId, pageStr) {
- const ip = getLanIP();
- return sendMessage(chatId,
- "\\uD83D\\uDCD1 \\u6587\\u7AE0\\u5217\\u8868\\n\\n" +
- "\\uD83D\\uDCF1 \\u624B\\u673A\\u6253\\u5F00: http://" + ip + ":1313/posts/\\n" +
- "\\uD83D\\uDCBB \\u7535\\u8111\\u6253\\u5F00: http://localhost:1313/posts/\\n\\n" +
- "\\u2705 \\u65B0\\u5EFA: /new \\u300A\\u6807\\u9898\\u300B\\n" +
- "\\uD83D\\uDCD1 \\u7F16\\u8F91: /edit slug\\n" +
- "\\uD83D\\uDCC4 \\u8349\\u7A3F: /draft\\n" +
- "\\uD83D\\uDE80 \\u53D1\\u5E03: /publish"
- );
-};
-
-// ---------- Links (友链) ----------
-CMD_HANDLERS.links = async function (chatId, pageStr) {
- const page = parseInt(pageStr) || 1;
- const perPage = 10;
- try {
- const res = await fetch("http://127.0.0.1:" + config.WRITE_PORT + "/api/rss/links?all=1");
- const data = await res.json();
- const allLinks = data.links || [];
- if (allLinks.length === 0) return sendMessage(chatId, "No links found.");
-
- const totalPages = Math.ceil(allLinks.length / perPage);
- const p = page > totalPages ? totalPages : page;
- const start = (p - 1) * perPage;
- const pageLinks = allLinks.slice(start, start + perPage);
-
- const lines = ["\\uD83D\\uDD17 \\u53CB\\u94FE \\u5171" + allLinks.length + " \\u4E2A \\u00B7 \\u7B2C" + p + "/" + totalPages + " \\u9875"];
- pageLinks.forEach(function(l) {
- const icon = l.hidden ? "\\uD83D\\uDD12" : "\\uD83C\\uDF10";
- const name = (l.name || l.url).slice(0, 40);
- lines.push(icon + " " + name + " " + l.url);
- });
-
- const keyboard = { inline_keyboard: [] };
- const navRow = [];
- if (p > 1) navRow.push({ text: "\\u25C0 \\u4E0A\\u4E00\\u9875", callback_data: "links_" + (p - 1) });
- if (p < totalPages) navRow.push({ text: "\\u4E0B\\u4E00\\u9875 \\u25B6", callback_data: "links_" + (p + 1) });
- if (navRow.length) keyboard.inline_keyboard.push(navRow);
-
- const payload = {
- chat_id: chatId,
- text: lines.join("\\n"),
- parse_mode: "HTML",
- reply_markup: navRow.length ? keyboard : undefined,
- disable_web_page_preview: true,
- };
- const { tg } = require("./tg-core");
- return tg("sendMessage", payload);
- } catch (e) {
- return sendMessage(chatId, "Please /start Write first.");
- }
-};
-
-CMD_HANDLERS.link_add = async function (chatId, arg) {
- if (!arg) return sendMessage(chatId, "Usage: /link_add \\nExample: /link_add https://example.com My Blog");
- const parts = arg.split(/\\s+/);
- let url = parts[0];
- const name = parts.slice(1).join(" ") || url;
- if (!url.startsWith("http")) url = "https://" + url;
- try {
- const res = await fetch("http://127.0.0.1:" + config.WRITE_PORT + "/api/rss/links", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ url, name }),
- });
- if (!res.ok) throw new Error((await res.json()).error || "Add failed");
- return sendMessage(chatId, "\\u2705 Link added: " + name + "\\n" + url);
- } catch (e) {
- return sendMessage(chatId, "\\u274C " + e.message);
- }
-};
-
-CMD_HANDLERS.link_del = async function (chatId, url) {
- if (!url) return sendMessage(chatId, "Usage: /link_del \\nUse /links to find the URL.");
- try {
- const res = await fetch("http://127.0.0.1:" + config.WRITE_PORT + "/api/rss/links?url=" + encodeURIComponent(url), {
- method: "DELETE",
- });
- if (!res.ok) throw new Error((await res.json()).error || "Delete failed");
- return sendMessage(chatId, "\\u2705 Link deleted: " + url);
- } catch (e) {
- return sendMessage(chatId, "\\u274C " + e.message);
- }
-};
-
-CMD_HANDLERS.link_toggle = async function (chatId, url) {
- if (!url) return sendMessage(chatId, "Usage: /link_toggle \\nToggle visibility of a link.");
- try {
- const listRes = await fetch("http://127.0.0.1:" + config.WRITE_PORT + "/api/rss/links?all=1");
- const listData = await listRes.json();
- const link = (listData.links || []).find(function(l) { return l.url === url; });
- if (!link) return sendMessage(chatId, "\\u274C Link not found: " + url);
- const newHidden = !link.hidden;
- const res = await fetch("http://127.0.0.1:" + config.WRITE_PORT + "/api/rss/links", {
- method: "PUT",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ oldUrl: url, hidden: newHidden }),
- });
- if (!res.ok) throw new Error((await res.json()).error || "Toggle failed");
- const label = newHidden ? "\\uD83D\\uDD12 Hidden" : "\\uD83C\\uDF10 Visible";
- return sendMessage(chatId, label + ": " + url);
- } catch (e) {
- return sendMessage(chatId, "\\u274C " + e.message);
- }
-};
-
-// ---------- Feeds (订阅) ----------
-CMD_HANDLERS.feeds = async function (chatId, pageStr) {
- const page = parseInt(pageStr) || 1;
- const perPage = 10;
- try {
- const res = await fetch("http://127.0.0.1:" + config.WRITE_PORT + "/api/rss/feeds");
- const data = await res.json();
- const allFeeds = data.feeds || [];
- if (allFeeds.length === 0) return sendMessage(chatId, "No feeds configured.");
-
- const totalPages = Math.ceil(allFeeds.length / perPage);
- const p = page > totalPages ? totalPages : page;
- const start = (p - 1) * perPage;
- const pageFeeds = allFeeds.slice(start, start + perPage);
-
- const lines = ["\\uD83D\\uDCE1 \\u8BA2\\u9605\\u6E90 \\u5171" + allFeeds.length + " \\u4E2A \\u00B7 \\u7B2C" + p + "/" + totalPages + " \\u9875"];
- pageFeeds.forEach(function(f) {
- const title = (f.feedTitle || f.url).slice(0, 50);
- lines.push("\\uD83D\\uDCDC " + title + "\\n " + f.url);
- });
-
- const keyboard = { inline_keyboard: [] };
- const navRow = [];
- if (p > 1) navRow.push({ text: "\\u25C0 \\u4E0A\\u4E00\\u9875", callback_data: "feeds_" + (p - 1) });
- if (p < totalPages) navRow.push({ text: "\\u4E0B\\u4E00\\u9875 \\u25B6", callback_data: "feeds_" + (p + 1) });
- if (navRow.length) keyboard.inline_keyboard.push(navRow);
-
- const { tg } = require("./tg-core");
- return tg("sendMessage", {
- chat_id: chatId,
- text: lines.join("\\n"),
- parse_mode: "HTML",
- reply_markup: navRow.length ? keyboard : undefined,
- disable_web_page_preview: true,
- });
- } catch (e) {
- return sendMessage(chatId, "Please /start Write first.");
- }
-};
-
-CMD_HANDLERS.feed_add = async function (chatId, arg) {
- if (!arg) return sendMessage(chatId, "Usage: /feed_add \\nExample: /feed_add https://example.com/rss.xml");
- const parts = arg.split(/\\s+/);
- const url = parts[0];
- const feedTitle = parts.slice(1).join(" ") || "";
- try {
- const res = await fetch("http://127.0.0.1:" + config.WRITE_PORT + "/api/rss/feeds", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ url, feedTitle }),
- });
- if (!res.ok) throw new Error((await res.json()).error || "Add failed");
- return sendMessage(chatId, "\\u2705 Feed added: " + url);
- } catch (e) {
- return sendMessage(chatId, "\\u274C " + e.message);
- }
-};
-
-CMD_HANDLERS.feed_del = async function (chatId, url) {
- if (!url) return sendMessage(chatId, "Usage: /feed_del \\nUse /feeds to find the URL.");
- try {
- const res = await fetch("http://127.0.0.1:" + config.WRITE_PORT + "/api/rss/feeds?url=" + encodeURIComponent(url), {
- method: "DELETE",
- });
- if (!res.ok) throw new Error((await res.json()).error || "Delete failed");
- return sendMessage(chatId, "\\u2705 Feed deleted: " + url);
- } catch (e) {
- return sendMessage(chatId, "\\u274C " + e.message);
- }
-};
-
-// ---------- Stats ----------
-CMD_HANDLERS.stats = async function (chatId) {
- try {
- const [resPosts, resLinks, resFeeds] = await Promise.all([
- fetch("http://127.0.0.1:" + config.WRITE_PORT + "/api/posts?perPage=1"),
- fetch("http://127.0.0.1:" + config.WRITE_PORT + "/api/rss/links?all=1"),
- fetch("http://127.0.0.1:" + config.WRITE_PORT + "/api/rss/feeds"),
- ]);
- const postsData = await resPosts.json();
- const linksData = await resLinks.json();
- const feedsData = await resFeeds.json();
-
- const totalPosts = postsData.totalCount || postsData.total || 0;
- const totalLinks = (linksData.links || []).filter(function(l) { return !l.hidden; }).length;
- const totalFeeds = (feedsData.feeds || []).length;
-
- return sendMessage(chatId,
- "\\uD83D\\uDCCA \\u7F51\\u7AD9\\u6570\\u636E\\n\\n" +
- "\\uD83D\\uDCDD \\u6587\\u7AE0\\uFF1A" + totalPosts + " \\u7BC7\\n" +
- "\\uD83D\\uDD17 \\u53CB\\u94FE\\uFF1A" + totalLinks + " \\u4E2A\\n" +
- "\\uD83D\\uDCE1 \\u8BA2\\u9605\\u6E90\\uFF1A" + totalFeeds + " \\u4E2A\\n\\n" +
- "\\uD83C\\uDF10 usj.cc"
- );
- } catch (e) {
- return sendMessage(chatId, "\\u8BF7\\u5148 /start \\u542F\\u52A8\\u540E\\u53F0\\u670D\\u52A1\\u3002");
- }
-};
-
-module.exports = CMD_HANDLERS;
diff --git a/bot/src/tg-poll.js b/bot/src/tg-poll.js
deleted file mode 100644
index 2989b339..00000000
--- a/bot/src/tg-poll.js
+++ /dev/null
@@ -1,103 +0,0 @@
-// tg-poll.js - Telegram polling loop and message dispatch
-const config = require("./config");
-const { tg, allowed, sendMessage, handleCallback } = require("./tg-core");
-const { sessions, getSession } = require("./sessions");
-const CMD_HANDLERS = require("./tg-handlers");
-
-let lastOffset = 0;
-
-// Commands that accept an argument
-const ARG_COMMANDS = new Set([
- "new", "title", "categories", "tags", "author",
- "edit", "view", "delete",
- "link_add", "link_del", "link_toggle",
- "feed_add", "feed_del",
- "list", "links", "feeds",
-]);
-
-async function tgPoll() {
- if (!config.TG_TOKEN) return;
- try {
- const data = await tg("getUpdates", {
- offset: lastOffset + 1,
- timeout: 30,
- allowed_updates: ["message", "callback_query"],
- });
- if (data.ok && data.result) {
- for (const update of data.result) {
- lastOffset = update.update_id;
-
- // Handle callback queries (inline buttons)
- if (update.callback_query) {
- const cb = update.callback_query;
- const cbChatId = cb.message ? cb.message.chat.id : (cb.from ? cb.from.id : 0);
- handleCallback(cbChatId, cb.id, cb.data, CMD_HANDLERS);
- continue;
- }
-
- const msg = update.message;
- const chatId = msg.chat.id;
- if (!allowed(chatId)) continue;
-
- // Handle photo in writing session - append as Markdown image
- if (msg.photo && sessions.has(chatId)) {
- try {
- const s = getSession(chatId);
- const largestPhoto = msg.photo[msg.photo.length - 1];
- const fileRes = await tg("getFile", { file_id: largestPhoto.file_id });
- if (fileRes.ok) {
- const filePath = fileRes.result.file_path;
- const caption = msg.caption || "";
- const imgUrl = "https://api.telegram.org/file/bot" + config.TG_TOKEN + "/" + filePath;
- s.content += "\\n\\n";
- }
- } catch (e) {
- console.error("[photo]", e.message);
- }
- continue;
- }
-
- if (!msg?.text) continue;
-
- const fullText = msg.text ? msg.text.trim() : (msg.caption || "").trim();
- const parts = fullText.split("@")[0].split(/\\s+/);
- const slashCmd = parts[0].toLowerCase().slice(1);
- const slashArg = parts.slice(1).join(" ");
- const hasSession = sessions.has(chatId);
-
- // 1) If in writing session and NOT a slash command, append text silently
- if (hasSession && !fullText.startsWith("/")) {
- try {
- const s = getSession(chatId);
- s.content += fullText + "\\n";
- } catch (e) {
- console.error("[append]", e.message);
- }
- continue;
- }
-
- // 2) Slash commands - each wrapped in try/catch
- try {
- if (ARG_COMMANDS.has(slashCmd)) {
- const handler = CMD_HANDLERS[slashCmd];
- if (handler) await handler(chatId, slashArg);
- else if (slashCmd) await sendMessage(chatId, "\\u274C Unknown: /" + slashCmd);
- } else {
- const handler = CMD_HANDLERS[slashCmd];
- if (handler) await handler(chatId);
- else if (slashCmd) await sendMessage(chatId, "\\u274C Unknown: /" + slashCmd);
- }
- } catch (e) {
- console.error("[handler " + slashCmd + "]", e.message);
- try { await sendMessage(chatId, "\\u274C " + (e.message || "error").slice(0, 200)); } catch {}
- }
- }
- }
- } catch (e) {
- console.error("[tg poll]", e.message);
- await new Promise((r) => setTimeout(r, 3000));
- }
- setImmediate(tgPoll);
-}
-
-module.exports = { tgPoll };
diff --git a/bot/src/write-server.js b/bot/src/write-server.js
deleted file mode 100644
index 1e02401e..00000000
--- a/bot/src/write-server.js
+++ /dev/null
@@ -1,118 +0,0 @@
-// write-server.js - Manage the Write (Next.js) and Hugo processes
-const { spawn, execSync } = require("child_process");
-const config = require("./config");
-const { waitForReady } = require("./helpers");
-
-let writeProcess = null;
-let writeReady = false;
-let hugoProcess = null;
-
-function startWriteServer() {
- if (writeProcess) {
- return { started: false, reason: writeReady ? "already_running" : "starting" };
- }
- writeReady = false;
- const isWin = process.platform === "win32";
- writeProcess = spawn(
- isWin ? "npm.cmd" : "npm",
- ["run", "dev", "--", "-p", String(config.WRITE_PORT), "-H", "0.0.0.0"],
- { cwd: config.WRITE_DIR, shell: isWin, stdio: "pipe", windowsHide: true }
- );
- writeProcess.stdout?.on("data", (d) => process.stdout.write(d));
- writeProcess.stderr?.on("data", (d) => process.stderr.write(d));
- writeProcess.on("exit", (code) => {
- console.log("[write] exited " + code);
- writeProcess = null;
- writeReady = false;
- });
- return { started: true };
-}
-
-function stopWriteServer() {
- if (hugoProcess) {
- try {
- const hpid = hugoProcess.pid;
- if (process.platform === "win32" && hpid) {
- execSync("taskkill /PID " + hpid + " /T /F", { stdio: "ignore" });
- } else {
- hugoProcess.kill();
- }
- } catch {}
- hugoProcess = null;
- }
- if (!writeProcess) return false;
- const pid = writeProcess.pid;
- try {
- if (process.platform === "win32" && pid) {
- execSync("taskkill /PID " + pid + " /T /F", { stdio: "ignore" });
- } else {
- writeProcess.kill();
- }
- } catch {}
- writeProcess = null;
- writeReady = false;
- return true;
-}
-
-function startHugoIfNeeded() {
- if (hugoProcess) return;
- try {
- hugoProcess = spawn("hugo", ["server", "--bind", "0.0.0.0", "--port", "1313", "--disableFastRender"], {
- cwd: config.WRITE_DIR + "/..",
- shell: true,
- stdio: "pipe",
- windowsHide: true,
- });
- hugoProcess.on("exit", (code) => {
- console.log("[hugo] exited " + code);
- hugoProcess = null;
- });
- console.log("[hugo] started on 0.0.0.0:1313");
- } catch (e) {
- console.error("[hugo]", e.message);
- }
-}
-
-function getWriteStatus() {
- return {
- running: !!(writeProcess && writeReady),
- starting: !!(writeProcess && !writeReady),
- };
-}
-
-async function ensureWriteReady(chatId, sendMessage) {
- if (writeProcess && writeReady) {
- startHugoIfNeeded();
- return true;
- }
- if (writeProcess && !writeReady) {
- await sendMessage(chatId, "\\u231B Write \\u6B63\\u5728\\u542F\\u52A8\\u4E2D...");
- const ready = await waitForReady(config.WRITE_PORT);
- if (ready) {
- writeReady = true;
- startHugoIfNeeded();
- return true;
- }
- return false;
- }
- const result = startWriteServer();
- if (!result.started) return false;
- await sendMessage(chatId, "\\u231B \\u6B63\\u5728\\u542F\\u52A8 Write...");
- const ready = await waitForReady(config.WRITE_PORT);
- if (ready) {
- writeReady = true;
- startHugoIfNeeded();
- return true;
- }
- return false;
-}
-
-module.exports = {
- get writeReady() { return writeReady; },
- get writePort() { return config.WRITE_PORT; },
- startWriteServer,
- stopWriteServer,
- startHugoIfNeeded,
- getWriteStatus,
- ensureWriteReady,
-};
diff --git a/bot/start.bat b/bot/start.bat
deleted file mode 100644
index 5ed68ea4..00000000
--- a/bot/start.bat
+++ /dev/null
@@ -1,5 +0,0 @@
-@echo off
-chcp 65001 >nul
-cd /d "%~dp0"
-node index.js
-pause
diff --git a/bot/writebot.ico b/bot/writebot.ico
deleted file mode 100644
index 8a595e74..00000000
Binary files a/bot/writebot.ico and /dev/null differ