Initial commit
This commit is contained in:
commit
a9613544b7
|
@ -0,0 +1,4 @@
|
||||||
|
NODE_ENV=development
|
||||||
|
BOT_TOKEN=
|
||||||
|
FOOOCOS_API_URL=
|
||||||
|
DUMMY_CHAT_ID=
|
|
@ -0,0 +1,3 @@
|
||||||
|
.vscode
|
||||||
|
node_modules
|
||||||
|
.env
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 Timofey Gelazoniya
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -0,0 +1,137 @@
|
||||||
|
# fooocos-telegram-bot
|
||||||
|
|
||||||
|
![bot](static/gorilla.gif)
|
||||||
|
|
||||||
|
#### ⚠️ The project may contain some bugs and errors, provided as is
|
||||||
|
|
||||||
|
Dynamic inline bot leveraging the robust capabilities of Fooocos for image generation. This bot integrates seamlessly with Telegram, offering an intuitive and engaging user experience.
|
||||||
|
|
||||||
|
Current [FooocosAPI](https://github.com/konieshadow/Fooocus-API/tree/bf2f2c745a159e13b8de93fd000470ed98c973c4) commit hash: `bf2f2c745a159e13b8de93fd000470ed98c973c4`
|
||||||
|
|
||||||
|
> Mirror on my [<img src="https://git.zeldon.ru/assets/img/logo.svg" align="center" width="20" height="20"/> Git](https://git.zeldon.ru/zeldon/fooocos-telegram-bot.git)
|
||||||
|
|
||||||
|
# Getting Started
|
||||||
|
|
||||||
|
## System Requirements
|
||||||
|
Before diving in, ensure your system meets these prerequisites:
|
||||||
|
|
||||||
|
- Git
|
||||||
|
- Python >= 3.10
|
||||||
|
- NodeJS >= 20
|
||||||
|
- An Nvidia GPU with a minimum of 4GB VRAM (other GPUs may be supported; check the [official Fooocos repository](https://github.com/konieshadow/Fooocus-API) for details).
|
||||||
|
|
||||||
|
## Installation Guide for Windows
|
||||||
|
|
||||||
|
> This guide for **Windows** only, but for Linux the logic is exactly the same, but the commands may be slightly different
|
||||||
|
|
||||||
|
### Install the correct FooocosAPI version locally:
|
||||||
|
|
||||||
|
1. Clone the FooocosAPI repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/konieshadow/Fooocus-API/tree/bf2f2c745a159e13b8de93fd000470ed98c973c4
|
||||||
|
```
|
||||||
|
2. Navigate to the cloned directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd Fooocus-API
|
||||||
|
```
|
||||||
|
3. Create and activate a Python virtual environment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m venv venv
|
||||||
|
.\venv\Scripts\activate
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Install requirements:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
5. Launch FooocosAPI:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python .\main.py --host 0.0.0.0
|
||||||
|
```
|
||||||
|
If successful, you should see output similar to this:
|
||||||
|
|
||||||
|
![output](static/output.png)
|
||||||
|
|
||||||
|
For subsequent runs, simply activate the virtual environment and start the API:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
.\venv\Scripts\activate
|
||||||
|
python .\main.py --host 0.0.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
> Tip: You can also try the [latest FooocosAPI version](https://github.com/konieshadow/Fooocus-API), but you might need to regenerate the client (`npm run fooocos:typegen`) and address any TypeScript compiler errors.
|
||||||
|
|
||||||
|
### Setting Up the Bot:
|
||||||
|
|
||||||
|
1. Clone this repository
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/xzeldon/fooocos-telegram-bot.git
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm i
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Prepare your configuration file:
|
||||||
|
|
||||||
|
- Duplicate the `.env.sample` file and rename it to `.env`.
|
||||||
|
- Edit the `.env` file with your specific details as per the table below:
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Variable</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>NODE_ENV</td>
|
||||||
|
<td>String</td>
|
||||||
|
<td>Specifies the application environment. (<code>development</code> or <code>production</code>)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>BOT_TOKEN</td>
|
||||||
|
<td>
|
||||||
|
String
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
Telegram Bot API token obtained from <a href="https://t.me/BotFather">@BotFather</a>.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>FOOOCOS_API_URL</td>
|
||||||
|
<td>String</td>
|
||||||
|
<td>URL of the Fooocos-API endpoint.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>DUMMY_CHAT_ID</td>
|
||||||
|
<td>String</td>
|
||||||
|
<td>ID of a dummy chat for uploading images to retrieve their file_id for inline message editing.</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
4. Start the bot:
|
||||||
|
|
||||||
|
- For development with hot reload:
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
- For standard operation:
|
||||||
|
```bash
|
||||||
|
npm run start
|
||||||
|
```
|
||||||
|
|
||||||
|
# License
|
||||||
|
This project is open-source, licensed under the MIT License. Feel free to use, modify, and distribute it as you see fit.
|
|
@ -0,0 +1,6 @@
|
||||||
|
import createClient from "openapi-fetch";
|
||||||
|
import { paths } from './schema.js';
|
||||||
|
|
||||||
|
export const fooocos = createClient<paths>({
|
||||||
|
baseUrl: process.env["FOOOCOS_API_URL"]
|
||||||
|
});
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,16 @@
|
||||||
|
import { createBot } from "#root/bot.js";
|
||||||
|
import { logger } from "#root/logger.js";
|
||||||
|
import { run } from "@grammyjs/runner";
|
||||||
|
|
||||||
|
const main = async () => {
|
||||||
|
try {
|
||||||
|
const bot = createBot(process.env["BOT_TOKEN"]!);
|
||||||
|
await bot.init();
|
||||||
|
run(bot);
|
||||||
|
logger.info(`Bot @${bot.botInfo.username} (id = ${bot.botInfo.id}) is up and running...`);
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
main();
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"name": "fooocos-telegram-bot",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"type": "module",
|
||||||
|
"imports": {
|
||||||
|
"#root/*": "./dist/src/*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"fooocos:typegen": "openapi-typescript ./fooocos/openapi.json -o ./fooocos/schema.d.ts",
|
||||||
|
"dev": "npm run clean && tsc-watch --onSuccess \"tsx --env-file=.env index.ts\"",
|
||||||
|
"start": "tsx --env-file=.env index.ts",
|
||||||
|
"build": "npm run clean && tsc --noEmit false",
|
||||||
|
"clean": "rimraf dist"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.9.2",
|
||||||
|
"openapi-typescript": "^6.7.1",
|
||||||
|
"rimraf": "^5.0.5",
|
||||||
|
"tsc-watch": "^6.0.4",
|
||||||
|
"tsx": "^4.1.4",
|
||||||
|
"typescript": "^5.2.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@grammyjs/runner": "^2.0.3",
|
||||||
|
"@grammyjs/transformer-throttler": "^1.2.1",
|
||||||
|
"eventemitter3": "^5.0.1",
|
||||||
|
"grammy": "^1.19.2",
|
||||||
|
"openapi-fetch": "^0.8.1",
|
||||||
|
"p-queue": "^7.4.1",
|
||||||
|
"pino": "^8.16.2",
|
||||||
|
"pino-pretty": "^10.2.3"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
export const textToImageBody = (prompt: string) => {
|
||||||
|
return {
|
||||||
|
"prompt": prompt,
|
||||||
|
"negative_prompt": "",
|
||||||
|
"style_selections": [
|
||||||
|
"Fooocus V2",
|
||||||
|
"Fooocus Enhance",
|
||||||
|
"Fooocus Sharp"
|
||||||
|
],
|
||||||
|
// "Speed" | "Quality" | "Extreme Speed"
|
||||||
|
"performance_selection": "Speed",
|
||||||
|
"aspect_ratios_selection": "1152×896",
|
||||||
|
"image_number": 1,
|
||||||
|
"image_seed": -1,
|
||||||
|
"sharpness": 2,
|
||||||
|
"guidance_scale": 4,
|
||||||
|
"base_model_name": "juggernautXL_version6Rundiffusion.safetensors",
|
||||||
|
"refiner_model_name": "None",
|
||||||
|
"refiner_switch": 0.5,
|
||||||
|
"loras": [
|
||||||
|
{
|
||||||
|
"model_name": "sd_xl_offset_example-lora_1.0.safetensors",
|
||||||
|
"weight": 0.1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"advanced_params": {
|
||||||
|
"disable_preview": false,
|
||||||
|
"adm_scaler_positive": 1.5,
|
||||||
|
"adm_scaler_negative": 0.8,
|
||||||
|
"adm_scaler_end": 0.3,
|
||||||
|
"refiner_swap_method": "joint",
|
||||||
|
"adaptive_cfg": 7,
|
||||||
|
"sampler_name": "dpmpp_2m_sde_gpu",
|
||||||
|
"scheduler_name": "karras",
|
||||||
|
"overwrite_step": -1,
|
||||||
|
"overwrite_switch": -1,
|
||||||
|
"overwrite_width": -1,
|
||||||
|
"overwrite_height": -1,
|
||||||
|
"overwrite_vary_strength": -1,
|
||||||
|
"overwrite_upscale_strength": -1,
|
||||||
|
"mixing_image_prompt_and_vary_upscale": false,
|
||||||
|
"mixing_image_prompt_and_inpaint": false,
|
||||||
|
"debugging_cn_preprocessor": false,
|
||||||
|
"skipping_cn_preprocessor": false,
|
||||||
|
"controlnet_softness": 0.25,
|
||||||
|
"canny_low_threshold": 64,
|
||||||
|
"canny_high_threshold": 128,
|
||||||
|
"freeu_enabled": false,
|
||||||
|
"freeu_b1": 1.01,
|
||||||
|
"freeu_b2": 1.02,
|
||||||
|
"freeu_s1": 0.99,
|
||||||
|
"freeu_s2": 0.95,
|
||||||
|
"debugging_inpaint_preprocessor": false,
|
||||||
|
"inpaint_disable_initial_latent": false,
|
||||||
|
"inpaint_engine": "v1",
|
||||||
|
"inpaint_strength": 1,
|
||||||
|
"inpaint_respective_field": 1
|
||||||
|
},
|
||||||
|
"require_base64": false,
|
||||||
|
"async_process": true
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,99 @@
|
||||||
|
import { logger } from "#root/logger.js";
|
||||||
|
import { predict } from "#root/predict.js";
|
||||||
|
import { downloadImage, isFooocosAlive, uploadPhoto } from "#root/utils.js";
|
||||||
|
import { apiThrottler } from "@grammyjs/transformer-throttler";
|
||||||
|
import { Bot, Context, InlineKeyboard, InlineQueryResultBuilder, InputMediaBuilder } from "grammy";
|
||||||
|
|
||||||
|
const activeTasks = new Map<number, boolean>();
|
||||||
|
|
||||||
|
const articlePhoto = {
|
||||||
|
url: "https://i.imgur.com/oAWNcVt.jpg",
|
||||||
|
width: 512,
|
||||||
|
height: 512
|
||||||
|
};
|
||||||
|
|
||||||
|
async function handleInlineQuery(ctx: Context) {
|
||||||
|
if (!await isFooocosAlive()) {
|
||||||
|
const answer = InlineQueryResultBuilder.article('fooocos:down', "Сервис недоступен")
|
||||||
|
.text('Сервис временно недоступен. Попробуйте позже');
|
||||||
|
return ctx.answerInlineQuery([answer], { cache_time: 0 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const userId = Number(ctx.update.inline_query?.from.id);
|
||||||
|
const prompt = ctx.inlineQuery?.query;
|
||||||
|
|
||||||
|
if (activeTasks.get(userId)) {
|
||||||
|
logger.trace(`${userId} in activeTasks`);
|
||||||
|
return ctx.answerInlineQuery([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const answer = prompt ?
|
||||||
|
InlineQueryResultBuilder.photo(`fooocos:query:prompt`, articlePhoto.url, {
|
||||||
|
caption: prompt,
|
||||||
|
thumbnail_url: articlePhoto.url,
|
||||||
|
photo_width: articlePhoto.width,
|
||||||
|
photo_height: articlePhoto.height,
|
||||||
|
reply_markup: new InlineKeyboard()
|
||||||
|
.switchInlineCurrent('Вы в очереди')
|
||||||
|
}) :
|
||||||
|
InlineQueryResultBuilder.article('fooocos:query', 'введите промпт', {
|
||||||
|
reply_markup: new InlineKeyboard()
|
||||||
|
.switchInlineCurrent('Попробовать снова')
|
||||||
|
})
|
||||||
|
.text('Нет промпта');
|
||||||
|
|
||||||
|
return ctx.answerInlineQuery([answer], { cache_time: 0 });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleChosenInlineResult(ctx: Context) {
|
||||||
|
const userId = ctx.update.chosen_inline_result?.from.id!;
|
||||||
|
const messageId = ctx.chosenInlineResult?.inline_message_id!;
|
||||||
|
const prompt = ctx.chosenInlineResult?.query!;
|
||||||
|
|
||||||
|
if (!prompt) {
|
||||||
|
return ctx.api.editMessageTextInline(messageId, "Ошибка: нет промпта. Попробуй ещё раз!");
|
||||||
|
}
|
||||||
|
|
||||||
|
activeTasks.set(userId, true);
|
||||||
|
|
||||||
|
const gen = predict(prompt);
|
||||||
|
|
||||||
|
for await (const state of gen) {
|
||||||
|
if (state.status === "RUNNING") {
|
||||||
|
if (state.preview) {
|
||||||
|
const previewImage = Buffer.from(state.preview, 'base64');
|
||||||
|
const fileId = await uploadPhoto(ctx, previewImage);
|
||||||
|
const media = InputMediaBuilder.photo(fileId, {
|
||||||
|
caption: `Промпт: ${prompt}`
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ctx.api.editMessageMediaInline(messageId, media);
|
||||||
|
} catch (err) {
|
||||||
|
activeTasks.delete(userId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (state.status === "SUCCESS") {
|
||||||
|
const resultImage = await downloadImage(state.result![0]);
|
||||||
|
const fileId = await uploadPhoto(ctx, resultImage);
|
||||||
|
const media = InputMediaBuilder.photo(fileId, {
|
||||||
|
caption: `Промпт: ${prompt}`
|
||||||
|
});
|
||||||
|
activeTasks.delete(userId);
|
||||||
|
return ctx.api.editMessageMediaInline(messageId, media);
|
||||||
|
} else if (state.status === 'ERROR') {
|
||||||
|
activeTasks.delete(userId);
|
||||||
|
logger.error('ERROR: failed to generate image');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createBot(token: string) {
|
||||||
|
const bot = new Bot(token);
|
||||||
|
bot.api.config.use(apiThrottler());
|
||||||
|
const protectedBot = bot.errorBoundary((err) => logger.error(err));
|
||||||
|
protectedBot.on('chosen_inline_result', handleChosenInlineResult);
|
||||||
|
protectedBot.on('inline_query', handleInlineQuery);
|
||||||
|
return bot;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { isDev } from "#root/utils.js";
|
||||||
|
import { LoggerOptions, pino } from "pino";
|
||||||
|
import PinoPretty, { PrettyOptions } from "pino-pretty";
|
||||||
|
|
||||||
|
const options: LoggerOptions = {
|
||||||
|
level: isDev() ? 'trace' : 'info'
|
||||||
|
};
|
||||||
|
|
||||||
|
const prettyOptions: PrettyOptions = {
|
||||||
|
ignore: 'pid,hostname',
|
||||||
|
colorize: isDev() ? true : false,
|
||||||
|
translateTime: 'SYS:dd.mm.yyyy, HH:MM:ss'
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
export let logger = pino(options, PinoPretty(prettyOptions));
|
|
@ -0,0 +1,68 @@
|
||||||
|
import { fooocos } from "#fooocos/client.js";
|
||||||
|
import { components } from "#fooocos/schema.js";
|
||||||
|
import { textToImageBody } from "#root/body.js";
|
||||||
|
import { logger } from "#root/logger.js";
|
||||||
|
import { sleep } from "#root/utils.js";
|
||||||
|
|
||||||
|
type Text2ImgRequest = components["schemas"]["Text2ImgRequest"];
|
||||||
|
type AsyncJobResponse = components["schemas"]["AsyncJobResponse"];
|
||||||
|
|
||||||
|
type PredictStatus = "WAITING" | "RUNNING" | "SUCCESS" | "ERROR";
|
||||||
|
|
||||||
|
type PredictResponse = {
|
||||||
|
status: PredictStatus,
|
||||||
|
jobId: number,
|
||||||
|
preview?: string,
|
||||||
|
result?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function* predict(prompt: string): AsyncGenerator<PredictResponse> {
|
||||||
|
const defaultBody = textToImageBody(prompt) as Text2ImgRequest;
|
||||||
|
const response = await fooocos.POST('/v1/generation/text-to-image', {
|
||||||
|
body: defaultBody
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.error) throw response.error;
|
||||||
|
|
||||||
|
const data = response.data as AsyncJobResponse;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const poll = await fooocos.GET('/v1/generation/query-job', {
|
||||||
|
params: {
|
||||||
|
query: {
|
||||||
|
job_id: data.job_id,
|
||||||
|
require_step_preivew: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (poll.error) throw poll.error;
|
||||||
|
|
||||||
|
const status = poll.data.job_stage;
|
||||||
|
|
||||||
|
if (status === "WAITING") {
|
||||||
|
logger.trace(`job id ${poll.data.job_id} is waiting`);
|
||||||
|
yield { status: 'WAITING', jobId: poll.data.job_id };
|
||||||
|
} else if (status === "RUNNING") {
|
||||||
|
logger.trace(`job id ${poll.data.job_id} is running`);
|
||||||
|
const stepPreview = poll.data.job_step_preview!;
|
||||||
|
yield { status: 'RUNNING', jobId: poll.data.job_id, preview: stepPreview };
|
||||||
|
} else if (status === 'SUCCESS') {
|
||||||
|
logger.trace(`job id ${poll.data.job_id} is success`);
|
||||||
|
const imageUrls: string[] = [];
|
||||||
|
if (poll.data.job_result) {
|
||||||
|
for (let image of poll.data.job_result) {
|
||||||
|
imageUrls.push(image.url!.replace('http://127.0.0.1:8888', process.env["FOOOCOS_API_URL"]!));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yield { status: 'SUCCESS', jobId: poll.data.job_id, result: imageUrls };
|
||||||
|
break;
|
||||||
|
} else if (status === 'ERROR') {
|
||||||
|
logger.trace(`job id ${poll.data.job_id} is error`);
|
||||||
|
yield { status: 'ERROR', jobId: poll.data.job_id };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await sleep(3000);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { fooocos } from "#fooocos/client.js";
|
||||||
|
import { Context, InputFile } from "grammy";
|
||||||
|
|
||||||
|
export const isDev = () => process.env["NODE_ENV"] === 'development' ? true : false;
|
||||||
|
|
||||||
|
export function sleep(ms: number) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function isFooocosAlive() {
|
||||||
|
try {
|
||||||
|
await fooocos.GET('/', { parseAs: 'text', signal: AbortSignal.timeout(1000) });
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function uploadPhoto(ctx: Context, photo: Buffer) {
|
||||||
|
const dummyMessage = await ctx.api.sendPhoto(process.env["DUMMY_CHAT_ID"]!, new InputFile(photo));
|
||||||
|
const fileId = dummyMessage.photo[dummyMessage.photo.length - 1].file_id;
|
||||||
|
return fileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function downloadImage(url: string) {
|
||||||
|
const response = await fetch(url)
|
||||||
|
.then(r => r.arrayBuffer());
|
||||||
|
const buffer = Buffer.from(response);
|
||||||
|
return buffer;
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 6.6 MiB |
Binary file not shown.
After Width: | Height: | Size: 76 KiB |
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"preserveWatchOutput": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"module": "NodeNext",
|
||||||
|
"target": "ES2021",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": ".",
|
||||||
|
"paths": {
|
||||||
|
"#root/*": [
|
||||||
|
"./src/*"
|
||||||
|
],
|
||||||
|
"#fooocos/*": [
|
||||||
|
"./fooocos/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue