-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
685150a
commit 339997f
Showing
5 changed files
with
543 additions
and
137 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,268 @@ | ||
# Создание Telegram-бота для управления списком задач с использованием Supabase и grammY | ||
|
||
В этой статье мы рассмотрим, как создать Telegram-бота для управления списком задач (CRUD: Create, Read, Update, Delete) с использованием Supabase и библиотеки grammY. Мы будем использовать два файла: `index.ts` и `todo-list.ts`. | ||
|
||
## Установка и настройка | ||
|
||
### Установка зависимостей | ||
Для начала установим необходимые зависимости: | ||
``` | ||
npm install grammy @grammyjs/storage-free supabase | ||
``` | ||
### Настройка Supabase | ||
|
||
Создайте проект в [Supabase](https://supabase.com/) и настройте таблицу `todo_list` со следующими полями: | ||
|
||
- `id` (integer, primary key) | ||
- `telegram_id` (text) | ||
- `task` (text) | ||
|
||
### Настройка grammY | ||
|
||
Создайте бота в Telegram с помощью [BotFather](https://t.me/BotFather) и получите токен. | ||
|
||
## Файл `todo-list.ts` | ||
|
||
Этот файл содержит функции для работы с базой данных Supabase. | ||
|
||
```typescript:supabase/functions/_shared/supabase/todo-list.ts | ||
import { supabase } from "./index.ts"; | ||
|
||
export async function readTask(id: number) { | ||
const { data, error } = await supabase | ||
.from('todo_list') | ||
.select('*') | ||
.eq('id', id) | ||
|
||
if (error) { | ||
throw error | ||
} | ||
return data | ||
} | ||
|
||
export async function createTask(telegram_id: string, task: string) { | ||
const { data, error } = await supabase | ||
.from('todo_list') | ||
.insert({ | ||
telegram_id, | ||
task, | ||
}) | ||
|
||
if (error) { | ||
throw error | ||
} | ||
return data | ||
} | ||
|
||
export async function updateTask(id: number, task: string) { | ||
const { data, error } = await supabase | ||
.from('todo_list') | ||
.update({ task }) | ||
.eq('id', id) | ||
|
||
if (error) { | ||
throw error | ||
} | ||
return data | ||
} | ||
|
||
export async function deleteTask(id: number) { | ||
const { data, error } = await supabase | ||
.from('todo_list') | ||
.delete() | ||
.eq('id', id) | ||
|
||
if (error) { | ||
throw error | ||
} | ||
return data | ||
} | ||
|
||
export async function readTasks(telegram_id: string) { | ||
const { data, error } = await supabase | ||
.from('todo_list') | ||
.select('*') | ||
.eq('telegram_id', telegram_id) | ||
|
||
if (error) { | ||
throw error | ||
} | ||
return data | ||
} | ||
``` | ||
|
||
## Файл `index.ts` | ||
|
||
Этот файл содержит логику бота на основе библиотеки grammY. | ||
|
||
```typescript:supabase/functions/todo-list/index.ts | ||
import { createTask, readTask, readTasks, updateTask, deleteTask } from "../_shared/supabase/todo-list.ts"; | ||
import { botNeuroCoder, handleUpdateNeuroCoder } from "../_shared/telegram/bots.ts"; | ||
|
||
console.log(`Function "telegram-bot" up and running!`) | ||
|
||
botNeuroCoder.command('start', async (ctx) => { | ||
await ctx.reply("Welcome! Вы попали в ToDo List Bot.", { | ||
reply_markup: { | ||
inline_keyboard: [ | ||
[{ text: 'Открыть меню', callback_data: 'open_menu' }] | ||
] | ||
} | ||
}) | ||
return | ||
}) | ||
|
||
botNeuroCoder.on('message:text', async (ctx) => { | ||
const message = ctx.message.text | ||
const telegram_id = ctx.from?.id.toString() | ||
|
||
if (message.startsWith('/')) return | ||
console.log(message) | ||
|
||
if (ctx.message.reply_to_message) { | ||
if (ctx.message.reply_to_message.text?.includes('Отправьте задачу.')) { | ||
await createTask(telegram_id, message) | ||
await ctx.reply('Задача создана') | ||
return | ||
} | ||
if (ctx.message.reply_to_message.text?.includes('Напишите новую задачу.')) { | ||
const tasks = await readTasks(telegram_id) | ||
console.log(tasks) | ||
await ctx.reply("Выберите задачу для изменения", { | ||
reply_markup: { | ||
inline_keyboard: tasks.map((task: any) => [ | ||
{ text: task.task, callback_data: `task_update_${task.id}_${message}` | ||
} | ||
]) | ||
} | ||
}) | ||
return | ||
} | ||
} | ||
}) | ||
|
||
botNeuroCoder.on('callback_query:data', async (ctx) => { | ||
const callbackData = ctx.callbackQuery.data | ||
|
||
console.log(callbackData, "callbackData") | ||
const telegram_id = ctx.from?.id.toString() | ||
|
||
if (callbackData === 'open_menu') { | ||
await ctx.reply("Вы открыли меню", { | ||
reply_markup: { | ||
inline_keyboard: [ | ||
[{ text: 'Create', callback_data: 'create_task' }], | ||
[{ text: 'Read', callback_data: 'read_tasks' }], | ||
[{ text: 'Update', callback_data: 'update_task' }], | ||
] | ||
} | ||
}) | ||
return | ||
} | ||
if (callbackData.includes('task')) { | ||
if (callbackData.includes('create')) { | ||
try { | ||
await ctx.reply("Отправьте задачу.", { | ||
reply_markup: { | ||
force_reply: true | ||
} | ||
}) | ||
return | ||
} catch (e) { | ||
await ctx.reply('Ошибка при создании задачи') | ||
throw new Error('Ошибка при создании задачи', e) | ||
} | ||
} | ||
if (callbackData.includes('read')) { | ||
try { | ||
if (callbackData.split("_").length === 3) { | ||
const task_id = callbackData.split("_")[2] | ||
const task = await readTask(Number(task_id)) | ||
await ctx.reply(task[0].task, { | ||
reply_markup: { | ||
inline_keyboard: [ | ||
[{ text: 'Delete', callback_data: `delete_task_${task_id}` }] | ||
] | ||
} | ||
}) | ||
return | ||
} else { | ||
const tasks = await readTasks(telegram_id) | ||
console.log(tasks) | ||
await ctx.reply("Выберите задачу", { | ||
reply_markup: { | ||
inline_keyboard: tasks.map((task: any) => [ | ||
{ text: task.task, callback_data: `task_read_${task.id}` | ||
} | ||
]) | ||
} | ||
}) | ||
} | ||
return | ||
} catch (e) { | ||
await ctx.reply('Ошибка при чтении задач') | ||
throw new Error('Ошибка при чтении задач', e) | ||
} | ||
} | ||
if (callbackData.includes('update')) { | ||
if (callbackData.split("_").length > 3) { | ||
const task_id = callbackData.split("_")[2] | ||
const new_task = callbackData.split("_")[3] | ||
await updateTask(Number(task_id), new_task) | ||
await ctx.reply("Задача обновлена") | ||
return | ||
} | ||
await ctx.reply("Напишите новую задачу.", { | ||
reply_markup: { | ||
force_reply: true | ||
} | ||
}) | ||
return | ||
} | ||
if (callbackData.includes('delete')) { | ||
const task_id = callbackData.split("_")[2] | ||
await deleteTask(Number(task_id)) | ||
await ctx.reply("Задача удалена.") | ||
return | ||
} | ||
} | ||
return | ||
}) | ||
|
||
Deno.serve(async (req) => { | ||
try { | ||
const url = new URL(req.url) | ||
if (url.searchParams.get('secret') !== Deno.env.get('FUNCTION_SECRET')) { | ||
return new Response('not allowed', { status: 405 }) | ||
} | ||
|
||
return await handleUpdateNeuroCoder(req) | ||
} catch (err) { | ||
console.error(err) | ||
} | ||
}) | ||
``` | ||
|
||
## Объяснение кода | ||
|
||
### Функции для работы с базой данных | ||
|
||
В файле `todo-list.ts` определены функции для работы с таблицей `todo_list` в Supabase: | ||
|
||
- `createTask`: Создает новую задачу. | ||
- `readTask`: Читает задачу по ID. | ||
- `readTasks`: Читает все задачи для конкретного пользователя. | ||
- `updateTask`: Обновляет задачу по ID. | ||
- `deleteTask`: Удаляет задачу по ID. | ||
|
||
### Логика бота | ||
|
||
В файле `index.ts` реализована логика бота с использованием библиотеки grammY: | ||
|
||
- Команда `/start` отправляет приветственное сообщение и предлагает открыть меню. | ||
- Обработчик текстовых сообщений создает или обновляет задачи в зависимости от контекста. | ||
- Обработчик `callback_query` обрабатывает нажатия на кнопки меню и выполняет соответствующие действия (создание, чтение, обновление, удаление задач). | ||
|
||
## Заключение | ||
|
||
В этой статье мы рассмотрели, как создать Telegram-бота для управления списком задач с использованием Supabase и библиотеки grammY. Мы определили функции для работы с базой данных и реализовали логику бота для выполнения операций CRUD. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
# Добавление команды для склейки нескольких видео в одно в Telegram-боте | ||
|
||
В этом руководстве мы рассмотрим, как добавить команду в Telegram-бота для склейки нескольких видео в одно с использованием библиотеки `grammy` и `ffmpeg`. Мы будем использовать файл `index.ts` в качестве примера. | ||
|
||
## Шаг 1: Установка зависимостей | ||
|
||
Для начала установим необходимые зависимости: | ||
|
||
```bash | ||
npm install grammy fluent-ffmpeg @ffmpeg-installer/ffmpeg | ||
``` | ||
|
||
## Шаг 2: Импорт необходимых модулей | ||
|
||
В файле `index.ts` импортируем необходимые модули: | ||
|
||
```typescript | ||
import { Context } from "grammy"; | ||
import ffmpeg from "fluent-ffmpeg"; | ||
import ffmpegInstaller from "@ffmpeg-installer/ffmpeg"; | ||
import path from "path"; | ||
import fs from "fs"; | ||
|
||
ffmpeg.setFfmpegPath(ffmpegInstaller.path); | ||
``` | ||
|
||
## Шаг 3: Проверка доступа к файлам | ||
|
||
Создадим функцию для проверки доступа к файлам: | ||
|
||
```typescript | ||
const checkAccess = (filePath: string, mode: number) => { | ||
return new Promise<void>((resolve, reject) => { | ||
fs.access(filePath, mode, (err) => { | ||
if (err) { | ||
reject(err); | ||
} else { | ||
resolve(); | ||
} | ||
}); | ||
}); | ||
}; | ||
``` | ||
|
||
## Шаг 4: Создание команды для склейки видео | ||
|
||
Создадим асинхронную функцию `short`, которая будет обрабатывать команду для склейки видео: | ||
|
||
```typescript | ||
const short = async (ctx: Context): Promise<void> => { | ||
try { | ||
await ctx.reply("Video creation started"); | ||
|
||
const inputFilePath = path.resolve(__dirname, "./assets/input/"); | ||
const outputFilePath = path.resolve(__dirname, "./assets/output/output.mp4"); | ||
|
||
try { | ||
await checkAccess(inputFilePath, fs.constants.R_OK); // Проверка прав на чтение | ||
await checkAccess(path.dirname(outputFilePath), fs.constants.W_OK); // Проверка прав на запись в директорию | ||
console.log("Access check passed"); | ||
} catch (err) { | ||
console.error("Access check failed:", err); | ||
throw err; | ||
} | ||
|
||
const createShortVideo = async () => { | ||
console.log(inputFilePath, outputFilePath); | ||
console.log("Merging started"); | ||
return new Promise<void>((resolve, reject) => { | ||
ffmpeg() | ||
.input(`${inputFilePath}/input.txt`) | ||
.output(outputFilePath) | ||
.inputOptions(["-f concat", "-safe 0"]) | ||
.outputOptions(["-c copy"]) | ||
.on("end", () => { | ||
console.log("Merging complete!"); | ||
resolve(); | ||
}) | ||
.on("error", (err) => { | ||
console.error("Error:", err); | ||
reject(err); | ||
}) | ||
.on("progress", (progress) => { | ||
console.log("Progress:", progress.percent); | ||
}) | ||
.run(); | ||
}); | ||
}; | ||
|
||
await createShortVideo(); | ||
return; | ||
} catch (error) { | ||
throw error; | ||
} | ||
}; | ||
|
||
export default short; | ||
``` | ||
|
||
## Заключение | ||
|
||
Теперь у вас есть команда для склейки нескольких видео в одно в вашем Telegram-боте. Эта команда проверяет доступ к файлам, а затем использует `ffmpeg` для объединения видео. Не забудьте настроить пути к входным и выходным файлам в соответствии с вашими требованиями. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.