upd
This commit is contained in:
108
tg_filebrowser_bot.js
Normal file
108
tg_filebrowser_bot.js
Normal file
@@ -0,0 +1,108 @@
|
||||
require("dotenv").config();
|
||||
const { Telegraf } = require("telegraf");
|
||||
const fs = require("fs").promises;
|
||||
const path = require("path");
|
||||
const stream = require("stream");
|
||||
const { pipeline } = require("stream/promises");
|
||||
const BOT_TOKEN = process.env.BOT_TOKEN;
|
||||
const BASE_DIR = process.env.BASE_DIR || "/srv/files";
|
||||
const ALLOWED = (process.env.ALLOWED_TELEGRAM_IDS || "")
|
||||
.split(",")
|
||||
.filter(Boolean)
|
||||
.map((x) => Number(x));
|
||||
if (!BOT_TOKEN) {
|
||||
console.error("Missing BOT_TOKEN in .env");
|
||||
process.exit(1);
|
||||
}
|
||||
const bot = new Telegraf(BOT_TOKEN);
|
||||
function allowed(ctx) {
|
||||
const id = ctx.from && ctx.from.id;
|
||||
if (!id) return false;
|
||||
if (ALLOWED.length === 0) return true;
|
||||
return ALLOWED.includes(id);
|
||||
}
|
||||
function safeJoin(base, target) {
|
||||
const resolved = path.resolve(base, "." + path.sep + target);
|
||||
if (!resolved.startsWith(path.resolve(base)))
|
||||
throw new Error("Access denied");
|
||||
return resolved;
|
||||
}
|
||||
bot.use(async (ctx, next) => {
|
||||
try {
|
||||
if (!allowed(ctx)) return ctx.reply("Access denied");
|
||||
return await next();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
try {
|
||||
await ctx.reply("Error: " + String(err.message));
|
||||
} catch (e) {}
|
||||
}
|
||||
});
|
||||
bot.start((ctx) =>
|
||||
ctx.reply("File manager bot ready. Use /help to list commands"),
|
||||
);
|
||||
bot.help((ctx) =>
|
||||
ctx.reply(
|
||||
"/ls [path]\n/get <path>\n/put (send file with caption: /put <path>)\n/mv <src> <dst>\n/mkdir <path>\n/rm <path>",
|
||||
),
|
||||
);
|
||||
bot.command("ls", async (ctx) => {
|
||||
const arg = ctx.message.text.split(" ").slice(1).join(" ") || ".";
|
||||
const dir = safeJoin(BASE_DIR, arg);
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
||||
const lines = entries.map((e) => `${e.isDirectory() ? "d" : "-"}\t${e.name}`);
|
||||
await ctx.replyWithMarkdownV2("`" + lines.join("\n") + "`");
|
||||
});
|
||||
bot.command("mkdir", async (ctx) => {
|
||||
const arg = ctx.message.text.split(" ").slice(1).join(" ");
|
||||
if (!arg) return ctx.reply("Usage: /mkdir <path>");
|
||||
const target = safeJoin(BASE_DIR, arg);
|
||||
await fs.mkdir(target, { recursive: true });
|
||||
await ctx.reply("Created " + arg);
|
||||
});
|
||||
bot.command("mv", async (ctx) => {
|
||||
const parts = ctx.message.text.split(" ").slice(1);
|
||||
if (parts.length < 2) return ctx.reply("Usage: /mv <src> <dst>");
|
||||
const src = safeJoin(BASE_DIR, parts[0]);
|
||||
const dst = safeJoin(BASE_DIR, parts[1]);
|
||||
await fs.rename(src, dst);
|
||||
await ctx.reply("Moved");
|
||||
});
|
||||
bot.command("rm", async (ctx) => {
|
||||
const arg = ctx.message.text.split(" ").slice(1).join(" ");
|
||||
if (!arg) return ctx.reply("Usage: /rm <path>");
|
||||
const target = safeJoin(BASE_DIR, arg);
|
||||
const stat = await fs.lstat(target);
|
||||
if (stat.isDirectory()) await fs.rmdir(target, { recursive: true });
|
||||
else await fs.unlink(target);
|
||||
await ctx.reply("Removed " + arg);
|
||||
});
|
||||
bot.command("get", async (ctx) => {
|
||||
const arg = ctx.message.text.split(" ").slice(1).join(" ");
|
||||
if (!arg) return ctx.reply("Usage: /get <path>");
|
||||
const target = safeJoin(BASE_DIR, arg);
|
||||
await ctx.replyWithDocument({
|
||||
source: target,
|
||||
filename: path.basename(target),
|
||||
});
|
||||
});
|
||||
bot.on("document", async (ctx) => {
|
||||
const caption = (ctx.message.caption || "").trim();
|
||||
let destPath = ".";
|
||||
if (caption.startsWith("/put"))
|
||||
destPath = caption.split(" ").slice(1).join(" ") || ".";
|
||||
const fileId = ctx.message.document.file_id;
|
||||
const fileName = ctx.message.document.file_name || "file";
|
||||
const filePath = safeJoin(BASE_DIR, path.join(destPath, fileName));
|
||||
const fileLink = await ctx.telegram.getFileLink(fileId);
|
||||
const res = await fetch(fileLink.href);
|
||||
if (!res.ok) throw new Error("download failed");
|
||||
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
||||
const dest = await fs.open(filePath, "w");
|
||||
await pipeline(res.body, dest.createWriteStream());
|
||||
await dest.close();
|
||||
await ctx.reply("Saved to " + path.relative(BASE_DIR, filePath));
|
||||
});
|
||||
bot.launch();
|
||||
process.once("SIGINT", () => bot.stop("SIGINT"));
|
||||
process.once("SIGTERM", () => bot.stop("SIGTERM"));
|
||||
Reference in New Issue
Block a user