Skip to content

Instantly share code, notes, and snippets.

@soruly
Last active October 8, 2022 09:09
Show Gist options
  • Save soruly/c2f7da4ea4cc237f280447afa0b2c59b to your computer and use it in GitHub Desktop.
Save soruly/c2f7da4ea4cc237f280447afa0b2c59b to your computer and use it in GitHub Desktop.
TELEGRAM_TOKEN= # e.g. 111111111:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
TELEGRAM_WEBHOOK= # e.g. https://your.host.com/
PORT= # (optional) Default: 3000
NOVELAI_TOKEN= # e.g. "Bearer eyJhbxxxxxxxxxxxxxXVCJ9.eyJpxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxbi9I"
{
"name": "soruly-telegram-bot",
"version": "0.0.1",
"description": "",
"main": "server.js",
"type": "module",
"scripts": {
"start": "pm2 start ecosystem.config.json",
"stop": "pm2 stop ecosystem.config.json",
"restart": "pm2 restart ecosystem.config.json",
"reload": "pm2 reload ecosystem.config.json",
"delete": "pm2 delete ecosystem.config.json",
"prettier": "prettier",
"format": "prettier --write \"**/*.js\"",
"lint": "prettier --check \"**/*.js\"",
"test": "prettier --check \"**/*.js\""
},
"repository": {
"type": "git",
"url": "git+https://github.com/soruly/soruly-telegram-bot.git"
},
"author": "soruly",
"license": "MIT",
"bugs": {
"url": "https://github.com/soruly/soruly-telegram-bot/issues"
},
"homepage": "https://github.com/soruly/soruly-telegram-bot",
"dependencies": {
"dotenv": "^16.0.3",
"express": "^4.18.1",
"express-rate-limit": "^6.6.0",
"node-fetch": "^3.2.10"
},
"devDependencies": {
"prettier": "^2.7.1"
}
}
import "dotenv/config";
import express from "express";
import rateLimit from "express-rate-limit";
import fetch, { FormData, File } from "node-fetch";
const { PORT = 3000, TELEGRAM_TOKEN, TELEGRAM_WEBHOOK, NOVELAI_TOKEN } = process.env;
const TELEGRAM_API = "https://api.telegram.org";
if (!TELEGRAM_TOKEN || !TELEGRAM_WEBHOOK || !NOVELAI_TOKEN) {
console.log("Please configure TELEGRAM_TOKEN and TELEGRAM_WEBHOOK and NOVELAI_TOKEN first");
process.exit();
}
console.log(`TELEGRAM_WEBHOOK: ${TELEGRAM_WEBHOOK}`);
console.log("Setting Telegram webhook...");
await fetch(
`${TELEGRAM_API}/bot${TELEGRAM_TOKEN}/setWebhook?url=${TELEGRAM_WEBHOOK}&max_connections=100`
)
.then((e) => e.json())
.then((e) => {
console.log(e);
});
fetch(`${TELEGRAM_API}/bot${TELEGRAM_TOKEN}/getMe`)
.then((e) => e.json())
.then((e) => {
console.log(e);
app.locals.botName = e.result?.username;
});
fetch(`${TELEGRAM_API}/bot${TELEGRAM_TOKEN}/setMyCommands`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
commands: [
{ command: "/portrait", description: "Generate image with 512x768" },
{ command: "/landscape", description: "Generate image with 768x512" },
{ command: "/square", description: "Generate image with 640x640" },
{ command: "/portrait_nsfw", description: "Generate NSFW image with 512x768" },
{ command: "/landscape_nsfw", description: "Generate NSFW image with 768x512" },
{ command: "/square_nsfw", description: "Generate NSFW image with 640x640" },
],
}),
});
const app = express();
app.disable("x-powered-by");
app.set("trust proxy", 1);
app.use(
rateLimit({
max: 100, // limit each IP to 100 requests
windowMs: 1000, // per second
delayMs: 0, // disable delaying - full speed until the max limit is reached
})
);
app.use(express.json());
const privateMessageHandler = async (message) => {
const responding_msg = message.reply_to_message ? message.reply_to_message : message;
console.log(responding_msg);
if (!responding_msg.entities?.length) {
return fetch(`${TELEGRAM_API}/bot${TELEGRAM_TOKEN}/sendMessage`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
chat_id: message.chat.id,
text: [
"Usage:",
"`/portrait original, 1girl, highres, claw_pose, long_hair, animal_ears, sailor_collar`",
"`/landscape original, 1girl, highres, claw_pose, long_hair, animal_ears, sailor_collar`",
"`/square original, 1girl, highres, claw_pose, long_hair, animal_ears, sailor_collar`",
"NSFW:",
"`/portrait_nsfw original, 1girl, highres, long_hair, animal_ears, nude, small_breasts, wet`",
"With seed:",
"`/portrait@3310422351 original, 1girl, highres, claw_pose, long_hair, animal_ears, sailor_collar`",
].join("\n"),
reply_to_message_id: responding_msg.message_id,
parse_mode: "Markdown",
}),
});
}
const entity = responding_msg.entities[0];
const command = responding_msg.text.substr(entity.offset, entity.length).trim();
let width = 640;
let height = 640;
if (command.startsWith("/landscape")) {
width = 768;
height = 512;
} else if (command.startsWith("/portrait")) {
width = 512;
height = 768;
}
const prompt = responding_msg.text.substring(responding_msg.entities[0].length).trim();
const seed = command.includes("@")
? Number(command.split("@")[1])
: 1000000000 + Math.floor(Math.random() * 2000000000);
if (!prompt || isNaN(seed)) {
return fetch(`${TELEGRAM_API}/bot${TELEGRAM_TOKEN}/sendMessage`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
chat_id: message.chat.id,
text: "Invalid input",
reply_to_message_id: responding_msg.message_id,
}),
});
}
const r = await fetch(`${TELEGRAM_API}/bot${TELEGRAM_TOKEN}/sendMessage`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
chat_id: message.chat.id,
text: `Generating ${width}x${height} image with seed ${seed}\n\`${prompt}\``,
reply_to_message_id: responding_msg.message_id,
parse_mode: "Markdown",
}),
}).then((e) => e.json());
console.log(r);
const res = await fetch("https://api.novelai.net/ai/generate-image", {
method: "post",
headers: {
authorization: NOVELAI_TOKEN,
"Content-Type": "application/json",
"user-agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36",
},
body: JSON.stringify({
input: prompt,
model: command.includes("nsfw") ? "nai-diffusion" : "safe-diffusion",
parameters: {
width,
height,
scale: 11,
sampler: "k_euler_ancestral",
steps: 28,
seed,
n_samples: 1,
// strength: 0.7,
// noise: 0.2,
ucPreset: 0,
uc: "lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry",
},
}),
});
const text = await res.text();
const base64Text = text.split("\n")[2].replace("data:", "").trim();
const img = Buffer.from(base64Text, "base64");
const formData = new FormData();
const abc = new File([img.buffer], "demo.png", { type: "image/png" });
formData.set("chat_id", message.chat.id);
formData.set("reply_to_message_id", responding_msg.message_id);
formData.set("photo", abc, "demo.png");
formData.set("caption", `seed: ${seed}\nprompt: \`${prompt}\``);
formData.set("parse_mode", "Markdown");
await fetch(`${TELEGRAM_API}/bot${TELEGRAM_TOKEN}/sendPhoto`, {
method: "POST",
body: formData,
});
await fetch(`${TELEGRAM_API}/bot${TELEGRAM_TOKEN}/deleteMessage`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
chat_id: message.chat.id,
message_id: r.result.message_id,
}),
}).then((e) => e.json());
};
app.post("/", (req, res) => {
const message = req.body?.message;
if (message?.chat?.type === "private") {
privateMessageHandler(message);
} else if (message?.chat?.type === "group" || message?.chat?.type === "supergroup") {
// groupMessageHandler(message);
}
res.sendStatus(204);
});
app.get("/", (req, res) => {
return res.send(
`<meta http-equiv="Refresh" content="0; URL=https://t.me/${app.locals.botName ?? ""}">`
);
});
app.listen(PORT, "0.0.0.0", () => console.log(`server listening on port ${PORT}`));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment