Первичная реализация, команды /list и /info
This commit is contained in:
@@ -10,6 +10,10 @@ APP_TITLE="IPTV Плейлисты"
|
|||||||
APP_TIMEZONE=Europe/Moscow
|
APP_TIMEZONE=Europe/Moscow
|
||||||
PAGE_SIZE=10
|
PAGE_SIZE=10
|
||||||
|
|
||||||
|
# config/bot.php
|
||||||
|
TG_BOT_TOKEN=
|
||||||
|
TG_BOT_SECRET=
|
||||||
|
|
||||||
# config/cache.php
|
# config/cache.php
|
||||||
CACHE_HOST="keydb"
|
CACHE_HOST="keydb"
|
||||||
CACHE_PORT=6379
|
CACHE_PORT=6379
|
||||||
|
|||||||
38
app/Controllers/BotController.php
Normal file
38
app/Controllers/BotController.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Антон Аксенов
|
||||||
|
* This file is part of iptv.axenov.dev web interface
|
||||||
|
* MIT License: https://git.axenov.dev/IPTV/web/src/branch/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
use App\Core\Bot;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use TelegramBot\Api\Exception;
|
||||||
|
use TelegramBot\Api\InvalidArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Контроллер методов ТГ бота
|
||||||
|
*/
|
||||||
|
class BotController extends BasicController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
* @throws \Exception
|
||||||
|
* @see https://github.com/TelegramBot/Api
|
||||||
|
* @see https://core.telegram.org/bots/api
|
||||||
|
* @see https://core.telegram.org/bots/api#markdownv2-style
|
||||||
|
*/
|
||||||
|
public function webhook(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
|
||||||
|
{
|
||||||
|
$bot = new Bot($request);
|
||||||
|
$bot->process();
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
296
app/Core/Bot.php
Normal file
296
app/Core/Bot.php
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Антон Аксенов
|
||||||
|
* This file is part of iptv.axenov.dev web interface
|
||||||
|
* MIT License: https://git.axenov.dev/IPTV/web/src/branch/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Core;
|
||||||
|
|
||||||
|
use App\Errors\InvalidTelegramSecretException;
|
||||||
|
use App\Errors\PlaylistNotFoundException;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use Exception;
|
||||||
|
use JsonException;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use TelegramBot\Api\BotApi;
|
||||||
|
use TelegramBot\Api\InvalidArgumentException;
|
||||||
|
use TelegramBot\Api\Types\ForceReply;
|
||||||
|
use TelegramBot\Api\Types\Inline\InlineKeyboardMarkup;
|
||||||
|
use TelegramBot\Api\Types\MessageEntity;
|
||||||
|
use TelegramBot\Api\Types\ReplyKeyboardMarkup;
|
||||||
|
use TelegramBot\Api\Types\ReplyKeyboardRemove;
|
||||||
|
use TelegramBot\Api\Types\Update;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обработчик команд бота
|
||||||
|
*/
|
||||||
|
class Bot
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var BotApi Объект Telegram Bot API
|
||||||
|
*/
|
||||||
|
protected BotApi $bot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Update Объект обновления бота
|
||||||
|
*/
|
||||||
|
protected Update $update;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Конструктор
|
||||||
|
*
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @throws InvalidTelegramSecretException
|
||||||
|
* @throws JsonException
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function __construct(ServerRequestInterface $request)
|
||||||
|
{
|
||||||
|
$this->checkSecret($request);
|
||||||
|
|
||||||
|
$body = json_decode((string)$request->getBody(), true);
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
throw new JsonException(json_last_error_msg());
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->bot = new BotApi(config('bot.token'));
|
||||||
|
$this->update = Update::fromResponse($body);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Запсукает обработку команды
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
public function process(): bool
|
||||||
|
{
|
||||||
|
$commandText = $this->getBotCommandText();
|
||||||
|
return match ($commandText) {
|
||||||
|
'/list', '/list@iptv_aggregator_bot' => $this->processListCommand(),
|
||||||
|
'/info', '/info@iptv_aggregator_bot' => $this->processInfoCommand(),
|
||||||
|
default => true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обрабатывает команду /list
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
protected function processListCommand(): bool
|
||||||
|
{
|
||||||
|
$playlists = ini()->getPlaylists();
|
||||||
|
if (empty($playlists)) {
|
||||||
|
$replyText = 'Плейлистов нет';
|
||||||
|
} else {
|
||||||
|
$replyText = [];
|
||||||
|
foreach ($playlists as $code => $pls) {
|
||||||
|
$statusEmoji = match ($pls['isOnline']) {
|
||||||
|
true => '🟢',
|
||||||
|
false => '🔴',
|
||||||
|
default => '⚪',
|
||||||
|
};
|
||||||
|
|
||||||
|
in_array('adult', $pls['tags'] ?? []) === true && $statusEmoji .= "🔞";
|
||||||
|
$replyText[] = "$statusEmoji \[$code\]";
|
||||||
|
}
|
||||||
|
$replyText = "Полный список плейлистов:\n\n**>" . implode("\n>", $replyText) . "||\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->reply($replyText, InlineKeyboardMarkup::fromResponse([
|
||||||
|
'inline_keyboard' => [
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'text' => 'Список на сайте',
|
||||||
|
'url' => "https://iptv.axenov.dev/",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'text' => 'FAQ',
|
||||||
|
'url' => 'https://iptv.axenov.dev/faq',
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обрабатывает команду /info
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @throws InvalidArgumentException
|
||||||
|
*/
|
||||||
|
protected function processInfoCommand(): bool
|
||||||
|
{
|
||||||
|
$message = $this->update->getMessage();
|
||||||
|
$text = $message->getText();
|
||||||
|
$command = $this->getBotCommand();
|
||||||
|
|
||||||
|
$code = mb_substr($text, $command->getLength() + 1 , mb_strlen($text));
|
||||||
|
if (empty($code)) {
|
||||||
|
return $this->reply('Укажите код плейлиста с сайта, чтобы получить результаты его проверки, ' .
|
||||||
|
'например, `/info ru` или `/info@iptv_aggregator_bot ru`');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pls = ini()->getPlaylist($code);
|
||||||
|
} catch (PlaylistNotFoundException) {
|
||||||
|
return $this->reply("Плейлист `$code` не найден");
|
||||||
|
}
|
||||||
|
|
||||||
|
$statusEmoji = match ($pls['isOnline']) {
|
||||||
|
true => '🟢',
|
||||||
|
false => '🔴',
|
||||||
|
default => '⚪',
|
||||||
|
};
|
||||||
|
|
||||||
|
in_array('adult', $pls['tags']) && $statusEmoji .= "🔞";
|
||||||
|
$replyText[] = $statusEmoji . ' [' . $this->escape($pls['name']) . "](https://m3u.su/$code/details)\n";
|
||||||
|
empty($pls['description']) || $replyText[] = $this->escape($pls['description']) . "\n";
|
||||||
|
|
||||||
|
if ($pls['isOnline'] === null) {
|
||||||
|
$replyText[] = "⏲️ *Проверка:* в очереди";
|
||||||
|
} else {
|
||||||
|
$now = DateTimeImmutable::createFromTimestamp(time());
|
||||||
|
$checkedAt = DateTimeImmutable::createFromTimestamp($pls['checkedAt']);
|
||||||
|
$minutes = $checkedAt->diff($now)->i;
|
||||||
|
$replyText[] = "⏲️ *Проверка:* " . ($minutes > 1 ? "$minutes мин\. назад" : "только что");
|
||||||
|
$replyText[] = "📺 *Каналов:* " . count($pls['channels']) .
|
||||||
|
' \(онлайн ' . $pls['onlineCount'] . ', оффлайн ' . $pls['offlineCount'] . "\)";
|
||||||
|
$replyText[] = "⏪ *Перемотка:* " . ($pls['hasCatchup'] === true ? '*есть*' : 'нет');
|
||||||
|
$replyText[] = "🗞️ *Телепрограмма:* " . ($pls['hasTvg'] === true ? '*есть*' : 'нет');
|
||||||
|
|
||||||
|
if (count($pls['groups'] ?? []) > 0) {
|
||||||
|
$groups = array_map(fn (array $group) => $this->escape($group['name']), $pls['groups']);
|
||||||
|
$replyText[] = "\n🗂️ *Группы* \(" . count($pls['groups']) . "\):\n**>\- " .
|
||||||
|
implode("\n>\- ", $groups) . '||';
|
||||||
|
} else {
|
||||||
|
$replyText[] = "🗂️ *Группы:* нет";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($pls['tags'] ?? []) > 0) {
|
||||||
|
$replyText[] = "\n🏷️ *Теги* \(" . count($pls['tags']) . "\):\n**>" .
|
||||||
|
$this->escape(trim(implode(' ', $pls['tags']))) . "||\n";
|
||||||
|
} else {
|
||||||
|
$replyText[] = "🏷️ *Теги:* нет";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$replyText[] = "🔗 *Ссылка для ТВ:* \(скопируй подходящую\)";
|
||||||
|
$replyText[] = "\- `https://m3u.su/$code`";
|
||||||
|
$replyText[] = "\- `https://m3u.su/$code.m3u`";
|
||||||
|
$replyText[] = "\- `https://iptv.axenov.dev/$code`";
|
||||||
|
$replyText[] = "\- `https://iptv.axenov.dev/$code.m3u`\n";
|
||||||
|
|
||||||
|
return $this->reply(
|
||||||
|
implode("\n", $replyText),
|
||||||
|
InlineKeyboardMarkup::fromResponse([
|
||||||
|
'inline_keyboard' => [
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'text' => is_null($pls['isOnline']) ? 'Подробности' : 'Список каналов',
|
||||||
|
'url' => "https://iptv.axenov.dev/$code/details",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'text' => 'FAQ',
|
||||||
|
'url' => 'https://iptv.axenov.dev/faq',
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сверяет секретный заголовок с заданным в конфиге
|
||||||
|
*
|
||||||
|
* @param ServerRequestInterface $request
|
||||||
|
* @return void
|
||||||
|
* @throws InvalidTelegramSecretException
|
||||||
|
*/
|
||||||
|
protected function checkSecret(ServerRequestInterface $request): void
|
||||||
|
{
|
||||||
|
$secret = config('bot.secret');
|
||||||
|
if (empty($secret)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$header = $request->getHeaderLine('X-Telegram-Bot-Api-Secret-Token');
|
||||||
|
if (empty($header) || $header !== $secret) {
|
||||||
|
throw new InvalidTelegramSecretException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Возвращает объект, описывающий команду бота
|
||||||
|
*
|
||||||
|
* @return MessageEntity|null
|
||||||
|
*/
|
||||||
|
protected function getBotCommand(): ?MessageEntity
|
||||||
|
{
|
||||||
|
return array_filter(
|
||||||
|
$this->update->getMessage()->getEntities(),
|
||||||
|
static fn (MessageEntity $entity) => $entity->getType() === 'bot_command',
|
||||||
|
)[0] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Возвращает текст команды бота
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
protected function getBotCommandText(): ?string
|
||||||
|
{
|
||||||
|
$text = $this->update->getMessage()->getText();
|
||||||
|
$command = $this->getBotCommand();
|
||||||
|
return $command ? mb_substr($text, 0, $command->getLength()) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Отправляет текстовое сообщение в ответ на команду, отправленную пользователем
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @param InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply|null $keyboard
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function reply(
|
||||||
|
string $text,
|
||||||
|
InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply|null $keyboard = null,
|
||||||
|
): bool {
|
||||||
|
try {
|
||||||
|
$this->bot->sendMessage(
|
||||||
|
chatId: $this->update->getMessage()->getChat()->getId(),
|
||||||
|
text: $text,
|
||||||
|
parseMode: 'MarkdownV2',
|
||||||
|
replyToMessageId: $this->update->getMessage()->getMessageId(),
|
||||||
|
replyMarkup: $keyboard,
|
||||||
|
);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
error_log($e->getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Экранирует служебные символы в строке
|
||||||
|
*
|
||||||
|
* @param string $string
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function escape(string $string): string
|
||||||
|
{
|
||||||
|
return str_replace(
|
||||||
|
['_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!'],
|
||||||
|
['\_', '\*', '\[', '\]', '\(', '\)', '\~', '\`', '\>', '\#', '\+', '\-', '\=', '\|', '\{', '\}', '\.', '\!'],
|
||||||
|
$string,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,7 +59,7 @@ class IniFile
|
|||||||
'offlineCount' => 0,
|
'offlineCount' => 0,
|
||||||
'checkedAt' => null,
|
'checkedAt' => null,
|
||||||
];
|
];
|
||||||
} else if (!isset($data['attributes'])) {
|
} elseif (!isset($data['attributes'])) {
|
||||||
$data['attributes'] = [];
|
$data['attributes'] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
20
app/Errors/InvalidTelegramSecretException.php
Normal file
20
app/Errors/InvalidTelegramSecretException.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Антон Аксенов
|
||||||
|
* This file is part of iptv.axenov.dev web interface
|
||||||
|
* MIT License: https://git.axenov.dev/IPTV/web/src/branch/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Errors;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
class InvalidTelegramSecretException extends Exception
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct("Ошибка валидации запроса от Telegram Bot API");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.3",
|
"php": "^8.4",
|
||||||
"ext-curl": "*",
|
"ext-curl": "*",
|
||||||
"ext-fileinfo": "*",
|
"ext-fileinfo": "*",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
"nyholm/psr7": "^1.8",
|
"nyholm/psr7": "^1.8",
|
||||||
"slim/slim": "^4.14",
|
"slim/slim": "^4.14",
|
||||||
"slim/twig-view": "^3.4",
|
"slim/twig-view": "^3.4",
|
||||||
|
"telegram-bot/api": "^2.5",
|
||||||
"vlucas/phpdotenv": "^5.6"
|
"vlucas/phpdotenv": "^5.6"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
|||||||
112
composer.lock
generated
112
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "eb7c9751c009420f33c222a768c1797a",
|
"content-hash": "af7bdd24bd2061cc3e8277488f404fb0",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "carbonphp/carbon-doctrine-types",
|
"name": "carbonphp/carbon-doctrine-types",
|
||||||
@@ -1570,7 +1570,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/clock",
|
"name": "symfony/clock",
|
||||||
"version": "v7.2.0",
|
"version": "v7.3.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/clock.git",
|
"url": "https://github.com/symfony/clock.git",
|
||||||
@@ -1624,7 +1624,7 @@
|
|||||||
"time"
|
"time"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/clock/tree/v7.2.0"
|
"source": "https://github.com/symfony/clock/tree/v7.3.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1644,16 +1644,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/deprecation-contracts",
|
"name": "symfony/deprecation-contracts",
|
||||||
"version": "v3.5.1",
|
"version": "v3.6.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||||
"reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6"
|
"reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
|
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
|
||||||
"reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
|
"reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -1666,7 +1666,7 @@
|
|||||||
"name": "symfony/contracts"
|
"name": "symfony/contracts"
|
||||||
},
|
},
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "3.5-dev"
|
"dev-main": "3.6-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@@ -1691,7 +1691,7 @@
|
|||||||
"description": "A generic function and convention to trigger deprecation notices",
|
"description": "A generic function and convention to trigger deprecation notices",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1"
|
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1707,7 +1707,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-09-25T14:20:29+00:00"
|
"time": "2024-09-25T14:21:43+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-ctype",
|
"name": "symfony/polyfill-ctype",
|
||||||
@@ -2103,16 +2103,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/translation",
|
"name": "symfony/translation",
|
||||||
"version": "v7.2.6",
|
"version": "v7.3.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/translation.git",
|
"url": "https://github.com/symfony/translation.git",
|
||||||
"reference": "e7fd8e2a4239b79a0fd9fb1fef3e0e7f969c6dc6"
|
"reference": "4aba29076a29a3aa667e09b791e5f868973a8667"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/translation/zipball/e7fd8e2a4239b79a0fd9fb1fef3e0e7f969c6dc6",
|
"url": "https://api.github.com/repos/symfony/translation/zipball/4aba29076a29a3aa667e09b791e5f868973a8667",
|
||||||
"reference": "e7fd8e2a4239b79a0fd9fb1fef3e0e7f969c6dc6",
|
"reference": "4aba29076a29a3aa667e09b791e5f868973a8667",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -2122,6 +2122,7 @@
|
|||||||
"symfony/translation-contracts": "^2.5|^3.0"
|
"symfony/translation-contracts": "^2.5|^3.0"
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
|
"nikic/php-parser": "<5.0",
|
||||||
"symfony/config": "<6.4",
|
"symfony/config": "<6.4",
|
||||||
"symfony/console": "<6.4",
|
"symfony/console": "<6.4",
|
||||||
"symfony/dependency-injection": "<6.4",
|
"symfony/dependency-injection": "<6.4",
|
||||||
@@ -2135,7 +2136,7 @@
|
|||||||
"symfony/translation-implementation": "2.3|3.0"
|
"symfony/translation-implementation": "2.3|3.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"nikic/php-parser": "^4.18|^5.0",
|
"nikic/php-parser": "^5.0",
|
||||||
"psr/log": "^1|^2|^3",
|
"psr/log": "^1|^2|^3",
|
||||||
"symfony/config": "^6.4|^7.0",
|
"symfony/config": "^6.4|^7.0",
|
||||||
"symfony/console": "^6.4|^7.0",
|
"symfony/console": "^6.4|^7.0",
|
||||||
@@ -2178,7 +2179,7 @@
|
|||||||
"description": "Provides tools to internationalize your application",
|
"description": "Provides tools to internationalize your application",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/translation/tree/v7.2.6"
|
"source": "https://github.com/symfony/translation/tree/v7.3.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -2194,20 +2195,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-04-07T19:09:28+00:00"
|
"time": "2025-05-29T07:19:49+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/translation-contracts",
|
"name": "symfony/translation-contracts",
|
||||||
"version": "v3.5.1",
|
"version": "v3.6.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/translation-contracts.git",
|
"url": "https://github.com/symfony/translation-contracts.git",
|
||||||
"reference": "4667ff3bd513750603a09c8dedbea942487fb07c"
|
"reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c",
|
"url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d",
|
||||||
"reference": "4667ff3bd513750603a09c8dedbea942487fb07c",
|
"reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -2220,7 +2221,7 @@
|
|||||||
"name": "symfony/contracts"
|
"name": "symfony/contracts"
|
||||||
},
|
},
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "3.5-dev"
|
"dev-main": "3.6-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@@ -2256,7 +2257,7 @@
|
|||||||
"standards"
|
"standards"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/translation-contracts/tree/v3.5.1"
|
"source": "https://github.com/symfony/translation-contracts/tree/v3.6.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -2272,7 +2273,68 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-09-25T14:20:29+00:00"
|
"time": "2024-09-27T08:32:26+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "telegram-bot/api",
|
||||||
|
"version": "v2.5.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/TelegramBot/Api.git",
|
||||||
|
"reference": "eaae3526223db49a1bad76a2dfa501dc287979cf"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/TelegramBot/Api/zipball/eaae3526223db49a1bad76a2dfa501dc287979cf",
|
||||||
|
"reference": "eaae3526223db49a1bad76a2dfa501dc287979cf",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-curl": "*",
|
||||||
|
"ext-json": "*",
|
||||||
|
"php": ">=5.5.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.16",
|
||||||
|
"symfony/phpunit-bridge": "*",
|
||||||
|
"vimeo/psalm": "^5.9"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "2.5-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"TelegramBot\\Api\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Ilya Gusev",
|
||||||
|
"email": "mail@igusev.ru",
|
||||||
|
"homepage": "https://php-cat.com",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHP Wrapper for Telegram Bot API",
|
||||||
|
"homepage": "https://github.com/TelegramBot/Api",
|
||||||
|
"keywords": [
|
||||||
|
"bot",
|
||||||
|
"bot api",
|
||||||
|
"php",
|
||||||
|
"telegram"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/TelegramBot/Api/issues",
|
||||||
|
"source": "https://github.com/TelegramBot/Api/tree/v2.5.0"
|
||||||
|
},
|
||||||
|
"time": "2023-08-09T13:53:12+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "twig/twig",
|
"name": "twig/twig",
|
||||||
@@ -2445,7 +2507,7 @@
|
|||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"prefer-lowest": false,
|
"prefer-lowest": false,
|
||||||
"platform": {
|
"platform": {
|
||||||
"php": "^8.3",
|
"php": "^8.4",
|
||||||
"ext-curl": "*",
|
"ext-curl": "*",
|
||||||
"ext-fileinfo": "*",
|
"ext-fileinfo": "*",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
|
|||||||
13
config/bot.php
Normal file
13
config/bot.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2025, Антон Аксенов
|
||||||
|
* This file is part of iptv.axenov.dev web interface
|
||||||
|
* MIT License: https://git.axenov.dev/IPTV/web/src/branch/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'token' => env('TG_BOT_TOKEN'),
|
||||||
|
'secret' => env('TG_BOT_SECRET'),
|
||||||
|
];
|
||||||
@@ -6,16 +6,29 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use App\Controllers\ApiController;
|
use App\Controllers\ApiController;
|
||||||
use App\Controllers\BasicController;
|
use App\Controllers\BotController;
|
||||||
use App\Controllers\WebController;
|
use App\Controllers\WebController;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Web routes
|
| Web routes
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
[
|
||||||
|
'method' => ['GET', 'POST'],
|
||||||
|
'path' => '/bot/webhook',
|
||||||
|
'handler' => [BotController::class, 'webhook'],
|
||||||
|
'name' => 'bot::webhook',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'method' => ['GET', 'POST'],
|
||||||
|
'path' => '/bot/update',
|
||||||
|
'handler' => [BotController::class, 'update'],
|
||||||
|
'name' => 'bot::update',
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'method' => 'GET',
|
'method' => 'GET',
|
||||||
'path' => '/[page/{page:[0-9]+}]',
|
'path' => '/[page/{page:[0-9]+}]',
|
||||||
@@ -59,12 +72,6 @@ return [
|
|||||||
'handler' => [ApiController::class, 'makeQrCode'],
|
'handler' => [ApiController::class, 'makeQrCode'],
|
||||||
'name' => 'api::makeQrCode',
|
'name' => 'api::makeQrCode',
|
||||||
],
|
],
|
||||||
// [
|
|
||||||
// 'method' => 'GET',
|
|
||||||
// 'path' => '/{code:[0-9a-zA-Z]+}/logo/{hash:[0-9a-z]+}',
|
|
||||||
// 'handler' => [ApiController::class, 'logo'],
|
|
||||||
// 'name' => 'api::getChannelLogo',
|
|
||||||
// ],
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@@ -74,7 +81,7 @@ return [
|
|||||||
|
|
||||||
[
|
[
|
||||||
'method' => '*',
|
'method' => '*',
|
||||||
'path' => '/{path:.*}',
|
'path' => '/{path:.+}',
|
||||||
'handler' => [BasicController::class, 'notFound'],
|
'handler' => [BasicController::class, 'notFound'],
|
||||||
'name' => 'not-found',
|
'name' => 'not-found',
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user