From dffab8736b50d41a3716107cede7cc5825054959 Mon Sep 17 00:00:00 2001 From: Tan Nguyen Date: Mon, 2 Mar 2026 01:47:20 +0700 Subject: [PATCH 01/14] refactor: simplify platform file handling in Event class and related tests --- src/Models/Event.php | 25 +++++++------------------ src/Trait/EventTrait.php | 4 ++-- tests/ConfigTest.php | 2 +- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/Models/Event.php b/src/Models/Event.php index 4896b4c..c01fe48 100644 --- a/src/Models/Event.php +++ b/src/Models/Event.php @@ -10,24 +10,13 @@ class Event public string $platform = EventConstant::DEFAULT_PLATFORM; - private string $platformFile = ''; - - /** - * @return string - */ - public function getPlatformFile(): string - { - return $this->platformFile; - } - - /** - * @param string $platformFile - * - * @return void - */ - public function setPlatformFile(string $platformFile): void - { - $this->platformFile = $platformFile; + public string $platformFile = '' { + get { + return $this->platformFile; + } + set { + $this->platformFile = $value; + } } /** diff --git a/src/Trait/EventTrait.php b/src/Trait/EventTrait.php index b6aed21..2732b6a 100644 --- a/src/Trait/EventTrait.php +++ b/src/Trait/EventTrait.php @@ -14,7 +14,7 @@ public function setPlatFormForEvent(?string $platform = EventConstant::DEFAULT_P { /** @var array $platformFileDefaults */ $platformFileDefaults = config('telegram-git-notifier.data_file.platform'); - $this->event->setPlatformFile($platformFile ?? $platformFileDefaults[$platform]); + $this->event = $platformFile ?? $platformFileDefaults[$platform]; $this->event->setEventConfig($platform); } @@ -38,7 +38,7 @@ public function validatePlatformFile(): void if (empty($this->event->getEventConfig())) { throw ConfigFileException::platformFile( $this->event->platform, - $this->event->getPlatformFile() + $this->event ); } } diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php index dd0ea7a..8520534 100644 --- a/tests/ConfigTest.php +++ b/tests/ConfigTest.php @@ -23,7 +23,7 @@ it('platform can be set for event with platform file', function () { $this->bot->setPlatFormForEvent('gitlab', 'storage/json/tgn/gitlab-events.json'); expect($this->bot->event->platform)->toBe('gitlab') - ->and($this->bot->event->getPlatformFile()) + ->and($this->bot) ->toBe('storage/json/tgn/gitlab-events.json'); }); From 36e9bb32925776b7fd7c61a07e5ac0cc756f427b Mon Sep 17 00:00:00 2001 From: Tan Nguyen Date: Mon, 2 Mar 2026 02:24:13 +0700 Subject: [PATCH 02/14] refactor: PHP 8.5 + architecture improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Platform enum and ChatTarget DTO - Add in-memory caching for Event and Setting models - Replace error_log() with PSR-3 LoggerInterface in Validator - Fix EventTrait bug (was assigning string to Event object) - Fix all implicit nullable parameter deprecations - Add retry logic with exponential backoff for Telegram API - Replace unsafe extract() in ConfigHelper with closure scope - Use match expressions, readonly properties, #[NoDiscard] - Centralize Telegram API calls in Webhook (eliminate 4x duplication) - Add strict type comparisons throughout - Fix test paths (storage/json/tgn/ → config/jsons/) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- composer.json | 1 + config/jsons/github-events.json | 0 config/jsons/gitlab-events.json | 0 config/jsons/tgn-settings.json | 0 config/tg-notifier.php | 6 +- src/Bot.php | 6 +- src/Constants/EventConstant.php | 7 + src/DTOs/ChatTarget.php | 50 ++++++++ src/Enums/Platform.php | 52 ++++++++ .../InvalidViewTemplateException.php | 2 +- src/Helpers/ConfigHelper.php | 50 ++++---- src/Interfaces/Structures/AppInterface.php | 6 +- .../Structures/NotificationInterface.php | 12 +- src/Models/Event.php | 92 ++++++++----- src/Models/Setting.php | 106 ++++++++------- src/Notifier.php | 57 +++++---- src/Objects/Validator.php | 39 ++++-- src/Structures/App.php | 4 +- src/Structures/Notification.php | 121 +++++++++++++----- src/Structures/TelegramBot.php | 7 +- src/Trait/EventSettingTrait.php | 12 +- src/Trait/EventTrait.php | 16 ++- src/Webhook.php | 94 +++++++------- tests/ConfigTest.php | 8 +- tests/NotifierTest.php | 4 +- tests/ValidatorTest.php | 2 +- 26 files changed, 487 insertions(+), 267 deletions(-) mode change 100644 => 100755 config/jsons/github-events.json mode change 100644 => 100755 config/jsons/gitlab-events.json mode change 100644 => 100755 config/jsons/tgn-settings.json create mode 100644 src/DTOs/ChatTarget.php create mode 100644 src/Enums/Platform.php diff --git a/composer.json b/composer.json index 7e4e442..4a34c66 100644 --- a/composer.json +++ b/composer.json @@ -42,6 +42,7 @@ "php": "^8.4|^8.5", "eleirbag89/telegrambotphp": "^1.4", "guzzlehttp/guzzle": "^7.8", + "psr/log": "^3.0", "symfony/http-foundation": "^7.0", "vlucas/phpdotenv": "^5.6" }, diff --git a/config/jsons/github-events.json b/config/jsons/github-events.json old mode 100644 new mode 100755 diff --git a/config/jsons/gitlab-events.json b/config/jsons/gitlab-events.json old mode 100644 new mode 100755 diff --git a/config/jsons/tgn-settings.json b/config/jsons/tgn-settings.json old mode 100644 new mode 100755 diff --git a/config/tg-notifier.php b/config/tg-notifier.php index 3b731d0..ae64e52 100644 --- a/config/tg-notifier.php +++ b/config/tg-notifier.php @@ -42,13 +42,13 @@ /** Set the path to the data file */ 'data_file' => [ 'setting' => $_ENV['TGN_PATH_SETTING'] ?? - 'storage/json/tgn/tgn-settings.json', + 'config/jsons/tgn-settings.json', 'platform' => [ 'gitlab' => $_ENV['TGN_PATH_PLATFORM_GITLAB'] ?? - 'storage/json/tgn/gitlab-events.json', + 'config/jsons/gitlab-events.json', 'github' => $_ENV['TGN_PATH_PLATFORM_GITHUB'] ?? - 'storage/json/tgn/github-events.json', + 'config/jsons/github-events.json', ], ], diff --git a/src/Bot.php b/src/Bot.php index cfd5f1b..d758a3a 100644 --- a/src/Bot.php +++ b/src/Bot.php @@ -39,12 +39,12 @@ class Bot implements BotInterface * @throws ConfigFileException */ public function __construct( - Telegram $telegram = null, + ?Telegram $telegram = null, ?string $chatBotId = null, - Event $event = null, + ?Event $event = null, ?string $platform = EventConstant::DEFAULT_PLATFORM, ?string $platformFile = null, - Setting $setting = null, + ?Setting $setting = null, ?string $settingFile = null, ) { $this->event = $event ?? new Event(); diff --git a/src/Constants/EventConstant.php b/src/Constants/EventConstant.php index e054688..6838298 100644 --- a/src/Constants/EventConstant.php +++ b/src/Constants/EventConstant.php @@ -2,6 +2,8 @@ namespace CSlant\TelegramGitNotifier\Constants; +use CSlant\TelegramGitNotifier\Enums\Platform; + final class EventConstant { public const DEFAULT_PLATFORM = 'github'; @@ -25,4 +27,9 @@ final class EventConstant 'github' => self::GITHUB_EVENT_SEPARATOR, 'gitlab' => self::GITLAB_EVENT_SEPARATOR, ]; + + public static function platformEnum(string $platform): Platform + { + return Platform::from($platform); + } } diff --git a/src/DTOs/ChatTarget.php b/src/DTOs/ChatTarget.php new file mode 100644 index 0000000..b31a514 --- /dev/null +++ b/src/DTOs/ChatTarget.php @@ -0,0 +1,50 @@ + $threadIds + */ + public function __construct( + public string $chatId, + public array $threadIds = [], + ) {} + + public function hasThreads(): bool + { + return $this->threadIds !== []; + } + + /** + * Parse a single "chatId:thread1,thread2" string. + */ + public static function fromString(string $raw): self + { + [$chatId, $threadIds] = explode(':', $raw) + [null, null]; + + return new self( + chatId: (string) $chatId, + threadIds: $threadIds ? explode(',', $threadIds) : [], + ); + } + + /** + * Parse "id1;id2:t1,t2;id3" format into an array of ChatTarget. + * + * @return list + */ + public static function parseMultiple(string $raw): array + { + if (trim($raw) === '') { + return []; + } + + return array_map( + static fn (string $pair) => self::fromString($pair), + explode(';', $raw), + ); + } +} diff --git a/src/Enums/Platform.php b/src/Enums/Platform.php new file mode 100644 index 0000000..78a6336 --- /dev/null +++ b/src/Enums/Platform.php @@ -0,0 +1,52 @@ + 'gh.', + self::GitLab => 'gl.', + }; + } + + public function webhookEventHeader(): string + { + return match ($this) { + self::GitHub => 'HTTP_X_GITHUB_EVENT', + self::GitLab => 'HTTP_X_GITLAB_EVENT', + }; + } + + /** + * @return array + */ + public static function webhookEventHeaders(): array + { + return array_combine( + array_map(fn (self $p) => $p->value, self::cases()), + array_map(fn (self $p) => $p->webhookEventHeader(), self::cases()), + ); + } + + /** + * @return array + */ + public static function eventSeparators(): array + { + return array_combine( + array_map(fn (self $p) => $p->value, self::cases()), + array_map(fn (self $p) => $p->eventSeparator(), self::cases()), + ); + } + + public static function default(): self + { + return self::GitHub; + } +} diff --git a/src/Exceptions/InvalidViewTemplateException.php b/src/Exceptions/InvalidViewTemplateException.php index 070adf7..b2b10f3 100644 --- a/src/Exceptions/InvalidViewTemplateException.php +++ b/src/Exceptions/InvalidViewTemplateException.php @@ -8,7 +8,7 @@ final class InvalidViewTemplateException extends TelegramGitNotifierException { public static function create( string $view, - Throwable $previous = null + ?Throwable $previous = null ): self { return new self( "Invalid view template: {$view}", diff --git a/src/Helpers/ConfigHelper.php b/src/Helpers/ConfigHelper.php index d650e48..3ee8589 100644 --- a/src/Helpers/ConfigHelper.php +++ b/src/Helpers/ConfigHelper.php @@ -19,36 +19,34 @@ public function __construct() } /** - * Handle config and return value - * - * @param string $string - * - * @return array|mixed + * Resolve a dot-notation config key to its value. */ public function execConfig(string $string): mixed { - $config = explode('.', $string); + $keys = explode('.', $string); $result = $this->config; - foreach ($config as $value) { - if (!isset($result[$value])) { + + foreach ($keys as $key) { + if (!isset($result[$key])) { return ''; } - $result = $result[$value]; + $result = $result[$key]; } return $result; } /** - * Return template data + * Render a PHP template file with the given data. * - * @param string $partialPath - * @param array $data + * Uses output buffering and a closure to isolate the template scope, + * preventing variable pollution. * - * @return bool|string + * @param string $partialPath Dot-notation path to the view file + * @param array $data Variables to pass to the template */ - public function getTemplateData(string $partialPath, array $data = []): bool|string + public function getTemplateData(string $partialPath, array $data = []): string { $viewPathFile = $this->execConfig('telegram-git-notifier.view.path') . '/' . str_replace('.', '/', $partialPath) . '.php'; @@ -57,16 +55,24 @@ public function getTemplateData(string $partialPath, array $data = []): bool|str return ''; } - $content = ''; - ob_start(); - try { - extract($data, EXTR_SKIP); - require_once $viewPathFile; - $content = ob_get_clean(); + $content = (static function (string $__viewFile, array $__data): string { + extract($__data, EXTR_SKIP); + ob_start(); + require $__viewFile; + + return (string) ob_get_clean(); + })($viewPathFile, $data); } catch (EntryNotFoundException|InvalidViewTemplateException|Throwable $e) { - ob_end_clean(); - error_log($e->getMessage()); + if (ob_get_level() > 0) { + ob_end_clean(); + } + + throw new InvalidViewTemplateException( + "Failed to render template '{$partialPath}': {$e->getMessage()}", + 0, + $e, + ); } return $content; diff --git a/src/Interfaces/Structures/AppInterface.php b/src/Interfaces/Structures/AppInterface.php index 7a77365..e9d53b0 100644 --- a/src/Interfaces/Structures/AppInterface.php +++ b/src/Interfaces/Structures/AppInterface.php @@ -44,7 +44,7 @@ public function sendPhoto(string $photo = '', array $caption = []): void; * @throws CallbackException * @see App::answerCallbackQuery() */ - public function answerCallbackQuery(string $text = null, array $options = []): void; + public function answerCallbackQuery(?string $text = null, array $options = []): void; /** * Edit message text and reply markup @@ -56,7 +56,7 @@ public function answerCallbackQuery(string $text = null, array $options = []): v * @throws BotException * @see App::editMessageText() */ - public function editMessageText(string $text = null, array $options = []): void; + public function editMessageText(?string $text = null, array $options = []): void; /** * Edit message reply markup from a telegram @@ -93,7 +93,7 @@ public function setCallbackContentMessage(array $options = []): array; * @return void * @see App::setCurrentChatBotId() */ - public function setCurrentChatBotId(string $chatId): void; + public function setCurrentChatBotId(?string $chatId = null): void; /** * Get the username of the bot diff --git a/src/Interfaces/Structures/NotificationInterface.php b/src/Interfaces/Structures/NotificationInterface.php index a68457b..ed6fe21 100644 --- a/src/Interfaces/Structures/NotificationInterface.php +++ b/src/Interfaces/Structures/NotificationInterface.php @@ -20,22 +20,22 @@ interface NotificationInterface extends AppInterface * @see Notification::accessDenied() */ public function accessDenied( - string $chatId = null, - string $viewTemplate = null, + ?string $chatId = null, + ?string $viewTemplate = null, ): void; /** - * Set payload from request + * Parse webhook payload from request and build notification message. * * @param Request $request * @param string $event * - * @return mixed|void + * @return object|null The parsed payload object, or null if content is empty. * @throws InvalidViewTemplateException * @throws MessageIsEmptyException * @see Notification::setPayload() */ - public function setPayload(Request $request, string $event); + public function setPayload(Request $request, string $event): ?object; /** * Send notification to telegram @@ -47,7 +47,7 @@ public function setPayload(Request $request, string $event); * @throws SendNotificationException * @see Notification::sendNotify() */ - public function sendNotify(string $message = null, array $options = []): bool; + public function sendNotify(?string $message = null, array $options = []): bool; /** * Get action name of event from payload data diff --git a/src/Models/Event.php b/src/Models/Event.php index c01fe48..3ec45d6 100644 --- a/src/Models/Event.php +++ b/src/Models/Event.php @@ -3,11 +3,17 @@ namespace CSlant\TelegramGitNotifier\Models; use CSlant\TelegramGitNotifier\Constants\EventConstant; +use CSlant\TelegramGitNotifier\Enums\Platform; class Event { + /** @var array */ private array $eventConfig = []; + private bool $dirty = false; + + private string $lastLoadedFile = ''; + public string $platform = EventConstant::DEFAULT_PLATFORM; public string $platformFile = '' { @@ -20,7 +26,7 @@ class Event } /** - * @return array + * @return array */ public function getEventConfig(): array { @@ -28,33 +34,35 @@ public function getEventConfig(): array } /** - * Set event config - * - * @param string|null $platform - * - * @return void + * Set event config from JSON file with in-memory caching. + * Only reads from disk if the file hasn't been loaded yet or platform changes. */ - public function setEventConfig( - string $platform = null - ): void { - $this->platform = $platform ?? EventConstant::DEFAULT_PLATFORM; - - $json = file_get_contents($this->platformFile); + public function setEventConfig(?string $platform = null): void + { + $newPlatform = $platform ?? EventConstant::DEFAULT_PLATFORM; + $this->platform = $newPlatform; - if (!empty($json)) { - $this->eventConfig = json_decode($json, true); + // Skip re-reading if same file is already loaded + if ($this->platformFile === $this->lastLoadedFile && $this->eventConfig !== []) { + return; } + + $this->loadFromFile(); } /** - * Update event config by event and action - * - * @param string $event - * @param string|null $action - * - * @return void + * Force reload event config from file. */ - public function updateEvent(string $event, string|null $action): void + public function reloadEventConfig(): void + { + $this->loadFromFile(); + } + + /** + * Update event config by event and action. + * Toggle the boolean value for the specified event/action. + */ + public function updateEvent(string $event, ?string $action): void { if (!empty($action)) { $this->eventConfig[$event][$action] @@ -63,19 +71,41 @@ public function updateEvent(string $event, string|null $action): void $this->eventConfig[$event] = !$this->eventConfig[$event]; } - $this->saveEventConfig(); + $this->dirty = true; + $this->persist(); } - /** - * Save event config - * - * @return void - */ - private function saveEventConfig(): void + public function getPlatformEnum(): Platform { - if (file_exists($this->platformFile)) { - $json = json_encode($this->eventConfig, JSON_PRETTY_PRINT); - file_put_contents($this->platformFile, $json, LOCK_EX); + return Platform::from($this->platform); + } + + private function loadFromFile(): void + { + if (empty($this->platformFile) || !file_exists($this->platformFile)) { + $this->eventConfig = []; + + return; + } + + $json = file_get_contents($this->platformFile); + + if (!empty($json)) { + $this->eventConfig = json_decode($json, true) ?? []; } + + $this->lastLoadedFile = $this->platformFile; + $this->dirty = false; + } + + private function persist(): void + { + if (!$this->dirty || !file_exists($this->platformFile)) { + return; + } + + $json = json_encode($this->eventConfig, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); + file_put_contents($this->platformFile, $json, LOCK_EX); + $this->dirty = false; } } diff --git a/src/Models/Setting.php b/src/Models/Setting.php index 6341143..d1be26d 100644 --- a/src/Models/Setting.php +++ b/src/Models/Setting.php @@ -6,91 +6,72 @@ class Setting { + /** @var array */ private array $settings = []; private string $settingFile = ''; + private bool $dirty = false; + /** - * @return array + * @return array */ public function getSettings(): array { return $this->settings; } - /** - * @param string $settingFile - * - * @return void - */ public function setSettingFile(string $settingFile): void { $this->settingFile = $settingFile; } - /** - * @return string - */ public function getSettingFile(): string { return $this->settingFile; } /** - * Set settings - * - * @return void + * Load settings from JSON file with in-memory caching. */ public function setSettingConfig(): void { - $json = file_get_contents($this->settingFile); - - if (!empty($json)) { - $this->settings = json_decode($json, true); + if ($this->settings !== []) { + return; } + + $this->loadFromFile(); } /** - * @return bool + * Force reload settings from file. */ - public function isAllEventsNotification(): bool + public function reloadSettings(): void { - if (!empty($this->settings) - && $this->settings[SettingConstant::T_ALL_EVENTS_NOTIFICATION] === true - ) { - return true; - } + $this->loadFromFile(); + } - return false; + public function isAllEventsNotification(): bool + { + return !empty($this->settings) + && ($this->settings[SettingConstant::T_ALL_EVENTS_NOTIFICATION] ?? false) === true; } - /** - * Condition for checking the notification status - * - * @return bool - */ public function isNotified(): bool { - if (!empty($this->settings) - && $this->settings[SettingConstant::T_IS_NOTIFIED] === true - ) { - return true; - } - - return false; + return !empty($this->settings) + && ($this->settings[SettingConstant::T_IS_NOTIFIED] ?? false) === true; } /** - * Update setting item value and save to file + * Update setting item value and save to file. * - * @param string $settingName - * @param array|string|bool|int|null $settingValue - * - * @return bool + * @param string $settingName Dot-notation key path + * @param mixed $settingValue New value (null = toggle boolean) */ public function updateSetting( string $settingName, - mixed $settingValue = null + mixed $settingValue = null, ): bool { $settingKeys = explode('.', $settingName); $lastKey = array_pop($settingKeys); @@ -106,29 +87,42 @@ public function updateSetting( } if (isset($nestedSettings[$lastKey])) { - $newValue = $settingValue ?? !$nestedSettings[$lastKey]; - $nestedSettings[$lastKey] = $newValue; + $nestedSettings[$lastKey] = $settingValue ?? !$nestedSettings[$lastKey]; + $this->dirty = true; - return $this->saveSettingsToFile(); + return $this->persist(); } return false; } - /** - * Save settings to json file - * - * @return bool - */ - private function saveSettingsToFile(): bool + private function loadFromFile(): void { - if (file_exists($this->settingFile)) { - $json = json_encode($this->settings, JSON_PRETTY_PRINT); - file_put_contents($this->settingFile, $json, LOCK_EX); + if (empty($this->settingFile) || !file_exists($this->settingFile)) { + $this->settings = []; - return true; + return; } - return false; + $json = file_get_contents($this->settingFile); + + if (!empty($json)) { + $this->settings = json_decode($json, true) ?? []; + } + + $this->dirty = false; + } + + private function persist(): bool + { + if (!$this->dirty || !file_exists($this->settingFile)) { + return false; + } + + $json = json_encode($this->settings, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); + file_put_contents($this->settingFile, $json, LOCK_EX); + $this->dirty = false; + + return true; } } diff --git a/src/Notifier.php b/src/Notifier.php index 62fbd08..3f49175 100644 --- a/src/Notifier.php +++ b/src/Notifier.php @@ -4,6 +4,7 @@ use CSlant\TelegramGitNotifier\Constants\EventConstant; use CSlant\TelegramGitNotifier\Constants\NotificationConstant; +use CSlant\TelegramGitNotifier\DTOs\ChatTarget; use CSlant\TelegramGitNotifier\Exceptions\ConfigFileException; use CSlant\TelegramGitNotifier\Interfaces\EventInterface; use CSlant\TelegramGitNotifier\Interfaces\Structures\NotificationInterface; @@ -26,22 +27,15 @@ class Notifier implements NotificationInterface, EventInterface public Client $client; /** - * @param Telegram|null $telegram - * @param string|null $chatBotId - * @param Event|null $event - * @param string|null $platform - * @param string|null $platformFile - * @param Client|null $client - * * @throws ConfigFileException */ public function __construct( - Telegram $telegram = null, + ?Telegram $telegram = null, ?string $chatBotId = null, - Event $event = null, + ?Event $event = null, ?string $platform = EventConstant::DEFAULT_PLATFORM, ?string $platformFile = null, - Client $client = null, + ?Client $client = null, ) { $this->event = $event ?? new Event(); $this->setPlatFormForEvent($platform, $platformFile); @@ -53,21 +47,40 @@ public function __construct( $this->client = $client ?? new Client(); } + /** + * Parse notification chat IDs string into structured array. + * + * @return array> Map of chatId => threadIds + */ public function parseNotifyChatIds(?string $chatIds = null): array { - $chatData = explode( - NotificationConstant::CHAT_ID_PAIRS_SEPARATOR, - $chatIds ?? config('telegram-git-notifier.bot.notify_chat_ids') - ); - $chatThreadMapping = []; - - foreach ($chatData as $data) { - [$chatId, $threadIds] = explode(NotificationConstant::CHAT_THREAD_ID_SEPARATOR, $data) + [null, null]; - $chatThreadMapping[$chatId] = $threadIds - ? explode(NotificationConstant::THREAD_ID_SEPARATOR, $threadIds) - : []; + $raw = $chatIds ?? config('telegram-git-notifier.bot.notify_chat_ids'); + + if (empty($raw)) { + return []; + } + + $targets = ChatTarget::parseMultiple($raw); + $mapping = []; + + foreach ($targets as $target) { + if ($target->chatId !== '') { + $mapping[$target->chatId] = $target->threadIds; + } } - return $chatThreadMapping; + return $mapping; + } + + /** + * Parse notification chat IDs into ChatTarget DTOs. + * + * @return list + */ + public function getChatTargets(?string $chatIds = null): array + { + $raw = $chatIds ?? config('telegram-git-notifier.bot.notify_chat_ids'); + + return ChatTarget::parseMultiple($raw ?? ''); } } diff --git a/src/Objects/Validator.php b/src/Objects/Validator.php index bb7d9fd..481d750 100644 --- a/src/Objects/Validator.php +++ b/src/Objects/Validator.php @@ -5,34 +5,40 @@ use CSlant\TelegramGitNotifier\Models\Event; use CSlant\TelegramGitNotifier\Models\Setting; use CSlant\TelegramGitNotifier\Trait\ActionEventTrait; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; class Validator { use ActionEventTrait; - private Setting $setting; + private readonly Setting $setting; - private Event $event; + private readonly Event $event; - public function __construct(Setting $setting, Event $event) - { + private readonly LoggerInterface $logger; + + public function __construct( + Setting $setting, + Event $event, + ?LoggerInterface $logger = null, + ) { $this->setting = $setting; $this->event = $event; + $this->logger = $logger ?? new NullLogger(); } /** - * Validate is access event before send notify - * - * @param string $platform Source code platform (GitHub, GitLab) - * @param string $event Event name (push, pull_request) - * @param object $payload + * Validate whether the event is allowed before sending notification. * - * @return bool + * @param string $platform Source code platform (github, gitlab) + * @param string $event Event name (push, pull_request, etc.) + * @param object $payload Webhook payload */ public function isAccessEvent( string $platform, string $event, - object $payload + object $payload, ): bool { if (!$this->setting->isNotified()) { return false; @@ -41,6 +47,7 @@ public function isAccessEvent( if ($this->setting->isAllEventsNotification()) { return true; } + $this->event->setEventConfig($platform); $eventConfig = $this->event->getEventConfig()[tgn_convert_event_name($event)] ?? false; @@ -51,10 +58,14 @@ public function isAccessEvent( } if (is_array($eventConfig) || !$eventConfig) { - $eventConfig = false; - error_log('\n Event config is not found \n'); + $this->logger->debug('Event config not found for event: {event}', [ + 'event' => $event, + 'platform' => $platform, + ]); + + return false; } - return $eventConfig; + return (bool) $eventConfig; } } diff --git a/src/Structures/App.php b/src/Structures/App.php index 6b8591c..ef9ab7a 100644 --- a/src/Structures/App.php +++ b/src/Structures/App.php @@ -15,7 +15,7 @@ trait App public string $chatBotId; - public function setCurrentChatBotId(string $chatBotId = null): void + public function setCurrentChatBotId(?string $chatBotId = null): void { $this->chatBotId = $chatBotId ?? config('telegram-git-notifier.bot.chat_id'); } @@ -64,7 +64,7 @@ public function sendPhoto(string $photo = '', array $options = []): void $this->telegram->sendPhoto($content); } - public function answerCallbackQuery(string $text = null, array $options = []): void + public function answerCallbackQuery(?string $text = null, array $options = []): void { if (empty($text)) { throw MessageIsEmptyException::create(); diff --git a/src/Structures/Notification.php b/src/Structures/Notification.php index 0f79066..e269378 100644 --- a/src/Structures/Notification.php +++ b/src/Structures/Notification.php @@ -15,8 +15,8 @@ trait Notification public string $message = ''; public function accessDenied( - string $chatId = null, - string $viewTemplate = null, + ?string $chatId = null, + ?string $viewTemplate = null, ): void { $this->telegram->sendMessage([ 'chat_id' => $this->chatBotId, @@ -29,18 +29,27 @@ public function accessDenied( ]); } - public function setPayload(Request $request, string $event) + /** + * Parse webhook payload from request and build notification message. + * + * @return object|null The parsed payload object, or null if content is empty. + * + * @throws MessageIsEmptyException + */ + public function setPayload(Request $request, string $event): ?object { - $content = null; + $content = match ($this->event->platform) { + 'gitlab' => $request->getContent(), + EventConstant::DEFAULT_PLATFORM => $request->request->get('payload'), + default => null, + }; - if ($this->event->platform === 'gitlab') { - $content = $request->getContent(); - } elseif ($this->event->platform === EventConstant::DEFAULT_PLATFORM) { - $content = $request->request->get('payload'); + if (is_string($content) && $content !== '') { + $this->payload = json_decode($content); } - if (is_string($content)) { - $this->payload = json_decode($content); + if (!isset($this->payload)) { + return null; } $this->setMessage($event); @@ -49,11 +58,8 @@ public function setPayload(Request $request, string $event) } /** - * Set message from payload + * Build notification message from event template. * - * @param string $typeEvent - * - * @return void * @throws MessageIsEmptyException */ private function setMessage(string $typeEvent): void @@ -61,9 +67,9 @@ private function setMessage(string $typeEvent): void $event = tgn_event_name($typeEvent); $action = $this->getActionOfEvent($this->payload); - $viewTemplate = empty($action) - ? "events.{$this->event->platform}.{$event}.default" - : "events.{$this->event->platform}.{$event}.{$action}"; + $viewTemplate = $action !== '' + ? "events.{$this->event->platform}.{$event}.{$action}" + : "events.{$this->event->platform}.{$event}.default"; $viewResult = tgn_view($viewTemplate, [ 'payload' => $this->payload, @@ -77,35 +83,80 @@ private function setMessage(string $typeEvent): void $this->message = $viewResult; } - public function sendNotify(string $message = null, array $options = []): bool + /** + * Send notification message to Telegram. + * + * @throws SendNotificationException + */ + #[\NoDiscard('The return value indicates whether the notification was sent successfully')] + public function sendNotify(?string $message = null, array $options = []): bool { - $this->message = !empty($message) ? $message : $this->message; + if (!empty($message)) { + $this->message = $message; + } - if (trim($this->message) === config('telegram-git-notifier.view.ignore-message')) { + $ignoreMessage = config('telegram-git-notifier.view.ignore-message'); + if (trim($this->message) === $ignoreMessage) { return false; } - $queryParams = array_merge($this->createTelegramBaseContent(), ['text' => $this->message], $options); + $queryParams = array_merge( + $this->createTelegramBaseContent(), + ['text' => $this->message], + $options, + ); - $url = 'https://api.telegram.org/bot' . config('telegram-git-notifier.bot.token') . '/sendMessage'; + $url = 'https://api.telegram.org/bot' + . config('telegram-git-notifier.bot.token') + . '/sendMessage'; - try { - $options = [ - 'form_params' => $queryParams, - 'verify' => config('telegram-git-notifier.app.request_verify'), - ]; + $requestOptions = [ + 'form_params' => $queryParams, + 'verify' => config('telegram-git-notifier.app.request_verify'), + ]; - $response = $this->client->request('POST', $url, $options); + return $this->sendWithRetry($url, $requestOptions); + } - if ($response->getStatusCode() === 200) { - return true; - } + /** + * Send HTTP request with exponential backoff retry for rate limits. + * + * @throws SendNotificationException + */ + private function sendWithRetry(string $url, array $options, int $maxRetries = 3): bool + { + $attempt = 0; + + while (true) { + try { + $response = $this->client->request('POST', $url, $options); - $body = (string) $response->getBody(); + if ($response->getStatusCode() === 200) { + return true; + } - throw SendNotificationException::create($body); - } catch (GuzzleException $e) { - throw SendNotificationException::create($e->getMessage()); + $body = (string) $response->getBody(); + + // Telegram returns 429 for rate limiting + if ($response->getStatusCode() === 429 && $attempt < $maxRetries) { + $retryAfter = json_decode($body, true)['parameters']['retry_after'] ?? (2 ** $attempt); + usleep((int) ($retryAfter * 1_000_000)); + $attempt++; + + continue; + } + + throw SendNotificationException::create($body); + } catch (GuzzleException $e) { + if ($attempt < $maxRetries && str_contains($e->getMessage(), '429')) { + usleep(2 ** $attempt * 1_000_000); + $attempt++; + + continue; + } + + throw SendNotificationException::create($e->getMessage()); + } } } } diff --git a/src/Structures/TelegramBot.php b/src/Structures/TelegramBot.php index f2e0df9..8bdfc50 100644 --- a/src/Structures/TelegramBot.php +++ b/src/Structures/TelegramBot.php @@ -18,16 +18,13 @@ public function isMessage(): bool public function isOwner(): bool { - return $this->telegram->ChatID() == $this->chatBotId; + return (string) $this->telegram->ChatID() === (string) $this->chatBotId; } public function isNotifyChat(): bool { $chatIds = config('telegram-git-notifier.bot.notify_chat_ids'); - if (in_array($this->telegram->ChatID(), $chatIds)) { - return true; - } - return false; + return in_array((string) $this->telegram->ChatID(), (array) $chatIds, true); } } diff --git a/src/Trait/EventSettingTrait.php b/src/Trait/EventSettingTrait.php index 2a0cb99..f72c267 100644 --- a/src/Trait/EventSettingTrait.php +++ b/src/Trait/EventSettingTrait.php @@ -10,7 +10,7 @@ trait EventSettingTrait public function eventMarkup( ?string $parentEvent = null, string $platform = EventConstant::DEFAULT_PLATFORM, - string $platformFile = null + ?string $platformFile = null ): array { $replyMarkup = $replyMarkupItem = []; @@ -123,10 +123,12 @@ public function getPlatformFromCallback( return $platform; } - if ($callback && str_contains($callback, EventConstant::GITHUB_EVENT_SEPARATOR)) { - return 'github'; - } elseif ($callback && str_contains($callback, EventConstant::GITLAB_EVENT_SEPARATOR)) { - return 'gitlab'; + if ($callback) { + return match (true) { + str_contains($callback, EventConstant::GITHUB_EVENT_SEPARATOR) => 'github', + str_contains($callback, EventConstant::GITLAB_EVENT_SEPARATOR) => 'gitlab', + default => EventConstant::DEFAULT_PLATFORM, + }; } return EventConstant::DEFAULT_PLATFORM; diff --git a/src/Trait/EventTrait.php b/src/Trait/EventTrait.php index 2732b6a..58dc9d9 100644 --- a/src/Trait/EventTrait.php +++ b/src/Trait/EventTrait.php @@ -10,11 +10,13 @@ trait EventTrait { use ActionEventTrait; - public function setPlatFormForEvent(?string $platform = EventConstant::DEFAULT_PLATFORM, ?string $platformFile = null): void - { - /** @var array $platformFileDefaults */ + public function setPlatFormForEvent( + ?string $platform = EventConstant::DEFAULT_PLATFORM, + ?string $platformFile = null, + ): void { + /** @var array $platformFileDefaults */ $platformFileDefaults = config('telegram-git-notifier.data_file.platform'); - $this->event = $platformFile ?? $platformFileDefaults[$platform]; + $this->event->platformFile = $platformFile ?? $platformFileDefaults[$platform] ?? ''; $this->event->setEventConfig($platform); } @@ -22,7 +24,7 @@ public function handleEventFromRequest(Request $request): ?string { foreach (EventConstant::WEBHOOK_EVENT_HEADER as $platform => $header) { $event = $request->server->get($header); - if (!is_null($event)) { + if ($event !== null) { $this->event->platform = $platform; $this->setPlatFormForEvent($platform); @@ -35,10 +37,10 @@ public function handleEventFromRequest(Request $request): ?string public function validatePlatformFile(): void { - if (empty($this->event->getEventConfig())) { + if ($this->event->getEventConfig() === []) { throw ConfigFileException::platformFile( $this->event->platform, - $this->event + $this->event->platformFile, ); } } diff --git a/src/Webhook.php b/src/Webhook.php index bd4eb07..e2032a1 100644 --- a/src/Webhook.php +++ b/src/Webhook.php @@ -13,11 +13,11 @@ class Webhook implements WebhookInterface private string $url; - private Client $client; + private readonly Client $client; - public function __construct() + public function __construct(?Client $client = null) { - $this->client = new Client(); + $this->client = $client ?? new Client(); } public function setToken(string $token): void @@ -32,65 +32,69 @@ public function setUrl(string $url): void public function setWebhook(): string { - $url = "https://api.telegram.org/bot{$this->token}/setWebhook?url={$this->url}"; - $options = [ - 'verify' => config('telegram-git-notifier.app.request_verify'), - ]; - - try { - $response = $this->client->request('GET', $url, $options); - - return $response->getBody()->getContents(); - } catch (GuzzleException) { - throw WebhookException::set(); - } + return $this->callTelegramApi( + "setWebhook?url={$this->url}", + static fn () => WebhookException::set(), + ); } public function deleteWebHook(): string { - $url = "https://api.telegram.org/bot{$this->token}/deleteWebhook"; - $options = [ - 'verify' => config('telegram-git-notifier.app.request_verify'), - ]; - - try { - $response = $this->client->request('GET', $url, $options); - - return $response->getBody()->getContents(); - } catch (GuzzleException) { - throw WebhookException::delete(); - } + return $this->callTelegramApi( + 'deleteWebhook', + static fn () => WebhookException::delete(), + ); } public function getWebHookInfo(): string { - $url = "https://api.telegram.org/bot{$this->token}/getWebhookInfo"; - $options = [ - 'verify' => config('telegram-git-notifier.app.request_verify'), - ]; - - try { - $response = $this->client->request('GET', $url, $options); - - return $response->getBody()->getContents(); - } catch (GuzzleException) { - throw WebhookException::getWebHookInfo(); - } + return $this->callTelegramApi( + 'getWebhookInfo', + static fn () => WebhookException::getWebHookInfo(), + ); } public function getUpdates(): string { - $url = "https://api.telegram.org/bot{$this->token}/getUpdates"; + return $this->callTelegramApi( + 'getUpdates', + static fn () => WebhookException::getUpdates(), + ); + } + + /** + * Centralized Telegram API call with retry support. + * + * @param string $endpoint API endpoint (after /bot{token}/) + * @param callable(): WebhookException $exceptionFactory + * @param int $maxRetries Max retry attempts for rate limiting + * + * @throws WebhookException + */ + private function callTelegramApi(string $endpoint, callable $exceptionFactory, int $maxRetries = 3): string + { + $url = "https://api.telegram.org/bot{$this->token}/{$endpoint}"; $options = [ 'verify' => config('telegram-git-notifier.app.request_verify'), ]; - try { - $response = $this->client->request('GET', $url, $options); + $attempt = 0; + + while (true) { + try { + $response = $this->client->request('GET', $url, $options); + + return $response->getBody()->getContents(); + } catch (GuzzleException $e) { + if ($attempt < $maxRetries && str_contains($e->getMessage(), '429')) { + usleep(2 ** $attempt * 1_000_000); + $attempt++; + + continue; + } - return $response->getBody()->getContents(); - } catch (GuzzleException) { - throw WebhookException::getUpdates(); + throw $exceptionFactory(); + } } } } diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php index 8520534..157f4df 100644 --- a/tests/ConfigTest.php +++ b/tests/ConfigTest.php @@ -21,10 +21,10 @@ }); it('platform can be set for event with platform file', function () { - $this->bot->setPlatFormForEvent('gitlab', 'storage/json/tgn/gitlab-events.json'); + $this->bot->setPlatFormForEvent('gitlab', 'config/jsons/gitlab-events.json'); expect($this->bot->event->platform)->toBe('gitlab') - ->and($this->bot) - ->toBe('storage/json/tgn/gitlab-events.json'); + ->and($this->bot->event->platformFile) + ->toBe('config/jsons/gitlab-events.json'); }); it('can get json config for event - github', function () { @@ -34,7 +34,7 @@ }); it('can get json config for event - gitlab', function () { - $this->bot->setPlatFormForEvent('gitlab', 'storage/json/tgn/gitlab-events.json'); + $this->bot->setPlatFormForEvent('gitlab', 'config/jsons/gitlab-events.json'); expect($this->bot->event->getEventConfig())->toBeArray() ->and($this->bot->event->getEventConfig())->toHaveKey('tag_push'); }); diff --git a/tests/NotifierTest.php b/tests/NotifierTest.php index 15373fe..e14ec52 100644 --- a/tests/NotifierTest.php +++ b/tests/NotifierTest.php @@ -7,11 +7,11 @@ }); it('validates that the event files exist', function () { - $this->nofitier->setPlatFormForEvent('gitlab', 'storage/json/tgn/gitlab-events.json'); + $this->nofitier->setPlatFormForEvent('gitlab', 'config/jsons/gitlab-events.json'); expect($this->nofitier->event->getEventConfig())->toBeArray() ->and($this->nofitier->event->getEventConfig())->toHaveKey('tag_push'); - $this->nofitier->setPlatFormForEvent('github', 'storage/json/tgn/github-events.json'); + $this->nofitier->setPlatFormForEvent('github', 'config/jsons/github-events.json'); expect($this->nofitier->event->getEventConfig())->toBeArray() ->and($this->nofitier->event->getEventConfig()) ->toHaveKey('issue_comment'); diff --git a/tests/ValidatorTest.php b/tests/ValidatorTest.php index 02eeafa..7eaef82 100644 --- a/tests/ValidatorTest.php +++ b/tests/ValidatorTest.php @@ -5,7 +5,7 @@ beforeEach(function () { $this->bot = new Bot(); - $this->bot->updateSetting('storage/json/tgn/setting.json'); + $this->bot->updateSetting('config/jsons/tgn-settings.json'); $this->bot->setPlatFormForEvent(); $this->validator = new Validator($this->bot->setting, $this->bot->event); }); From 5b0d6eb74d35722218460c656f6c7c14d9d7fe53 Mon Sep 17 00:00:00 2001 From: tanhongit <35853002+tanhongit@users.noreply.github.com> Date: Sun, 1 Mar 2026 19:28:02 +0000 Subject: [PATCH 03/14] Fix styling --- src/DTOs/ChatTarget.php | 3 ++- src/Notifier.php | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DTOs/ChatTarget.php b/src/DTOs/ChatTarget.php index b31a514..846b387 100644 --- a/src/DTOs/ChatTarget.php +++ b/src/DTOs/ChatTarget.php @@ -11,7 +11,8 @@ public function __construct( public string $chatId, public array $threadIds = [], - ) {} + ) { + } public function hasThreads(): bool { diff --git a/src/Notifier.php b/src/Notifier.php index 3f49175..b19bfb0 100644 --- a/src/Notifier.php +++ b/src/Notifier.php @@ -3,7 +3,6 @@ namespace CSlant\TelegramGitNotifier; use CSlant\TelegramGitNotifier\Constants\EventConstant; -use CSlant\TelegramGitNotifier\Constants\NotificationConstant; use CSlant\TelegramGitNotifier\DTOs\ChatTarget; use CSlant\TelegramGitNotifier\Exceptions\ConfigFileException; use CSlant\TelegramGitNotifier\Interfaces\EventInterface; From dece2c41dc6a9e485a93d6e47cc9e1ef5c4664d8 Mon Sep 17 00:00:00 2001 From: Tan Nguyen Date: Mon, 2 Mar 2026 17:33:05 +0700 Subject: [PATCH 04/14] refactor: replace empty() with explicit checks, fix null safety - Replace all empty() calls with explicit === '' / !== null checks - Add is_array() guard in ConfigHelper::execConfig() traversal - Handle preg_replace() null return in helpers - Fix setCallbackContentMessage() using isset() instead of truthy check - Make ConfigHelper::config readonly and private - Add return type to tgn_view() Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- common/helpers.php | 20 +++++++++----------- src/Bot.php | 2 +- src/Helpers/ConfigHelper.php | 4 ++-- src/Models/Event.php | 6 +++--- src/Models/Setting.php | 8 ++++---- src/Notifier.php | 2 +- src/Objects/Validator.php | 2 +- src/Structures/App.php | 10 +++++----- src/Structures/Notification.php | 2 +- src/Trait/ActionEventTrait.php | 2 +- 10 files changed, 28 insertions(+), 30 deletions(-) diff --git a/common/helpers.php b/common/helpers.php index a1d19e2..1b337e0 100755 --- a/common/helpers.php +++ b/common/helpers.php @@ -10,7 +10,7 @@ * * @return string|null */ - function tgn_singularity(string $word): string|null + function tgn_singularity(string $word): ?string { static $singular_rules = [ '/(quiz)zes$/i' => '$1', @@ -56,7 +56,7 @@ function tgn_singularity(string $word): string|null */ function tgn_snake_case(string $string): string { - $string = preg_replace('/\s+/', '_', $string); + $string = preg_replace('/\s+/', '_', $string) ?? $string; return strtolower($string); } @@ -100,7 +100,7 @@ function tgn_convert_event_name(string $event): string */ function tgn_convert_action_name(string $action): string { - return strtolower(preg_replace('/(?getTemplateData( $partialPath, $data ); - return $content ?: null; + return $content !== '' ? $content : null; } } } if (!function_exists('tgn_view')) { /** - * Get view template + * Get view template from the correct source (Laravel Blade or standalone PHP). * - * @param string $partialPath - * @param array $data - * - * @noinspection PhpMissingReturnTypeInspection + * @param string $partialPath Dot-notation template path + * @param array $data Variables to pass to the template */ - function tgn_view(string $partialPath, array $data = []) + function tgn_view(string $partialPath, array $data = []): mixed { if (class_exists('Illuminate\Foundation\Application')) { $partialPath = config('telegram-git-notifier.view.namespace') . '::' . $partialPath; diff --git a/src/Bot.php b/src/Bot.php index d758a3a..720817a 100644 --- a/src/Bot.php +++ b/src/Bot.php @@ -61,7 +61,7 @@ public function __construct( public function validateSettingFile(): void { - if (empty($this->setting->getSettingFile())) { + if ($this->setting->getSettingFile() === '') { throw ConfigFileException::settingFile($this->setting->getSettingFile()); } } diff --git a/src/Helpers/ConfigHelper.php b/src/Helpers/ConfigHelper.php index 3ee8589..bf99616 100644 --- a/src/Helpers/ConfigHelper.php +++ b/src/Helpers/ConfigHelper.php @@ -11,7 +11,7 @@ final class ConfigHelper /** * @var array */ - public array $config; + private readonly array $config; public function __construct() { @@ -27,7 +27,7 @@ public function execConfig(string $string): mixed $result = $this->config; foreach ($keys as $key) { - if (!isset($result[$key])) { + if (!is_array($result) || !array_key_exists($key, $result)) { return ''; } diff --git a/src/Models/Event.php b/src/Models/Event.php index 3ec45d6..961379a 100644 --- a/src/Models/Event.php +++ b/src/Models/Event.php @@ -64,7 +64,7 @@ public function reloadEventConfig(): void */ public function updateEvent(string $event, ?string $action): void { - if (!empty($action)) { + if ($action !== null && $action !== '') { $this->eventConfig[$event][$action] = !$this->eventConfig[$event][$action]; } else { @@ -82,7 +82,7 @@ public function getPlatformEnum(): Platform private function loadFromFile(): void { - if (empty($this->platformFile) || !file_exists($this->platformFile)) { + if ($this->platformFile === '' || !file_exists($this->platformFile)) { $this->eventConfig = []; return; @@ -90,7 +90,7 @@ private function loadFromFile(): void $json = file_get_contents($this->platformFile); - if (!empty($json)) { + if ($json !== false && $json !== '') { $this->eventConfig = json_decode($json, true) ?? []; } diff --git a/src/Models/Setting.php b/src/Models/Setting.php index d1be26d..f539057 100644 --- a/src/Models/Setting.php +++ b/src/Models/Setting.php @@ -53,13 +53,13 @@ public function reloadSettings(): void public function isAllEventsNotification(): bool { - return !empty($this->settings) + return $this->settings !== [] && ($this->settings[SettingConstant::T_ALL_EVENTS_NOTIFICATION] ?? false) === true; } public function isNotified(): bool { - return !empty($this->settings) + return $this->settings !== [] && ($this->settings[SettingConstant::T_IS_NOTIFIED] ?? false) === true; } @@ -98,7 +98,7 @@ public function updateSetting( private function loadFromFile(): void { - if (empty($this->settingFile) || !file_exists($this->settingFile)) { + if ($this->settingFile === '' || !file_exists($this->settingFile)) { $this->settings = []; return; @@ -106,7 +106,7 @@ private function loadFromFile(): void $json = file_get_contents($this->settingFile); - if (!empty($json)) { + if ($json !== false && $json !== '') { $this->settings = json_decode($json, true) ?? []; } diff --git a/src/Notifier.php b/src/Notifier.php index b19bfb0..437b48a 100644 --- a/src/Notifier.php +++ b/src/Notifier.php @@ -55,7 +55,7 @@ public function parseNotifyChatIds(?string $chatIds = null): array { $raw = $chatIds ?? config('telegram-git-notifier.bot.notify_chat_ids'); - if (empty($raw)) { + if ($raw === null || $raw === '') { return []; } diff --git a/src/Objects/Validator.php b/src/Objects/Validator.php index 481d750..3327a14 100644 --- a/src/Objects/Validator.php +++ b/src/Objects/Validator.php @@ -53,7 +53,7 @@ public function isAccessEvent( $eventConfig = $this->event->getEventConfig()[tgn_convert_event_name($event)] ?? false; $action = $this->getActionOfEvent($payload); - if (!empty($action) && isset($eventConfig[$action])) { + if ($action !== '' && isset($eventConfig[$action])) { $eventConfig = $eventConfig[$action]; } diff --git a/src/Structures/App.php b/src/Structures/App.php index ef9ab7a..66050c9 100644 --- a/src/Structures/App.php +++ b/src/Structures/App.php @@ -31,14 +31,14 @@ private function createTelegramBaseContent(): array public function sendMessage(?string $message = '', array $options = []): void { - if (empty($message)) { + if ($message === null || $message === '') { throw MessageIsEmptyException::create(); } $content = $this->createTelegramBaseContent(); $content['text'] = $message; - if (!empty($options['reply_markup'])) { + if (isset($options['reply_markup'])) { $content['reply_markup'] = $this->telegram->buildInlineKeyBoard( $options['reply_markup'] ); @@ -52,7 +52,7 @@ public function sendMessage(?string $message = '', array $options = []): void public function sendPhoto(string $photo = '', array $options = []): void { - if (empty($photo)) { + if ($photo === '') { throw EntryNotFoundException::fileNotFound(); } @@ -66,7 +66,7 @@ public function sendPhoto(string $photo = '', array $options = []): void public function answerCallbackQuery(?string $text = null, array $options = []): void { - if (empty($text)) { + if ($text === null || $text === '') { throw MessageIsEmptyException::create(); } @@ -122,7 +122,7 @@ public function setCallbackContentMessage(array $options = []): array 'parse_mode' => 'HTML', ]; - $content['reply_markup'] = $options['reply_markup'] + $content['reply_markup'] = isset($options['reply_markup']) ? $this->telegram->buildInlineKeyBoard($options['reply_markup']) : null; diff --git a/src/Structures/Notification.php b/src/Structures/Notification.php index e269378..7a83329 100644 --- a/src/Structures/Notification.php +++ b/src/Structures/Notification.php @@ -91,7 +91,7 @@ private function setMessage(string $typeEvent): void #[\NoDiscard('The return value indicates whether the notification was sent successfully')] public function sendNotify(?string $message = null, array $options = []): bool { - if (!empty($message)) { + if ($message !== null && $message !== '') { $this->message = $message; } diff --git a/src/Trait/ActionEventTrait.php b/src/Trait/ActionEventTrait.php index 9b1e6ac..21c985f 100644 --- a/src/Trait/ActionEventTrait.php +++ b/src/Trait/ActionEventTrait.php @@ -11,7 +11,7 @@ public function getActionOfEvent(object $payload): string ?? $payload->object_attributes?->noteable_type ?? ''; - if (!empty($action)) { + if ($action !== '') { return tgn_convert_action_name($action); } From 307fdc463a259d7a2895c09d8abc5d67760c35b3 Mon Sep 17 00:00:00 2001 From: Tan Nguyen Date: Mon, 2 Mar 2026 17:45:11 +0700 Subject: [PATCH 05/14] feat: update PHP and Laravel versions in CI configurations --- .github/workflows/phpstan.yml | 2 +- .github/workflows/setup_test.yml | 15 ++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 07465eb..dacf830 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: os: [ ubuntu-latest ] - php: [ '8.1', '8.2', '8.3' ] + php: [ '8.4', '8.5' ] steps: - uses: actions/checkout@v6 diff --git a/.github/workflows/setup_test.yml b/.github/workflows/setup_test.yml index 55199a7..a72340b 100644 --- a/.github/workflows/setup_test.yml +++ b/.github/workflows/setup_test.yml @@ -9,16 +9,13 @@ jobs: strategy: matrix: os: [ ubuntu-latest ] - php: [ '8.1', '8.2', '8.3' ] - laravel: [ 11.*, 10.*, 9.* ] + php: [ '8.4', '8.5' ] + laravel: [ 12.*, 11.* ] include: - - laravel: 11.* - testbench: 9.* - - laravel: 10.* - testbench: 8.* - exclude: - - laravel: 11.* - php: 8.1 + - laravel: 12.* + php: 8.4 + - laravel: 12.* + php: 8.5 steps: - name: Setup PHP uses: shivammathur/setup-php@v2 From 605316bfbf18a6f70fe353cb40cdd16b85e4b3be Mon Sep 17 00:00:00 2001 From: Tan Nguyen Date: Mon, 2 Mar 2026 17:56:21 +0700 Subject: [PATCH 06/14] fix: update phpstan/phpstan-deprecation-rules version to ^2.0 in composer.json --- phpstan.neon.dist | 1 - 1 file changed, 1 deletion(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 5bba7cc..d291033 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -4,4 +4,3 @@ parameters: - src - config tmpDir: build/phpstan - checkMissingIterableValueType: false From 0c638c72ca29a2f783d62383891b648a29dbb23f Mon Sep 17 00:00:00 2001 From: Tan Nguyen Date: Mon, 2 Mar 2026 18:04:51 +0700 Subject: [PATCH 07/14] fix: update file paths for event configurations to use storage directory --- config/tg-notifier.php | 6 +++--- tests/ConfigTest.php | 6 +++--- tests/NotifierTest.php | 4 ++-- tests/ValidatorTest.php | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/config/tg-notifier.php b/config/tg-notifier.php index ae64e52..3b731d0 100644 --- a/config/tg-notifier.php +++ b/config/tg-notifier.php @@ -42,13 +42,13 @@ /** Set the path to the data file */ 'data_file' => [ 'setting' => $_ENV['TGN_PATH_SETTING'] ?? - 'config/jsons/tgn-settings.json', + 'storage/json/tgn/tgn-settings.json', 'platform' => [ 'gitlab' => $_ENV['TGN_PATH_PLATFORM_GITLAB'] ?? - 'config/jsons/gitlab-events.json', + 'storage/json/tgn/gitlab-events.json', 'github' => $_ENV['TGN_PATH_PLATFORM_GITHUB'] ?? - 'config/jsons/github-events.json', + 'storage/json/tgn/github-events.json', ], ], diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php index 157f4df..da57c6c 100644 --- a/tests/ConfigTest.php +++ b/tests/ConfigTest.php @@ -21,10 +21,10 @@ }); it('platform can be set for event with platform file', function () { - $this->bot->setPlatFormForEvent('gitlab', 'config/jsons/gitlab-events.json'); + $this->bot->setPlatFormForEvent('gitlab', 'storage/json/tgn/gitlab-events.json'); expect($this->bot->event->platform)->toBe('gitlab') ->and($this->bot->event->platformFile) - ->toBe('config/jsons/gitlab-events.json'); + ->toBe('storage/json/tgn/gitlab-events.json'); }); it('can get json config for event - github', function () { @@ -34,7 +34,7 @@ }); it('can get json config for event - gitlab', function () { - $this->bot->setPlatFormForEvent('gitlab', 'config/jsons/gitlab-events.json'); + $this->bot->setPlatFormForEvent('gitlab', 'storage/json/tgn/gitlab-events.json'); expect($this->bot->event->getEventConfig())->toBeArray() ->and($this->bot->event->getEventConfig())->toHaveKey('tag_push'); }); diff --git a/tests/NotifierTest.php b/tests/NotifierTest.php index e14ec52..15373fe 100644 --- a/tests/NotifierTest.php +++ b/tests/NotifierTest.php @@ -7,11 +7,11 @@ }); it('validates that the event files exist', function () { - $this->nofitier->setPlatFormForEvent('gitlab', 'config/jsons/gitlab-events.json'); + $this->nofitier->setPlatFormForEvent('gitlab', 'storage/json/tgn/gitlab-events.json'); expect($this->nofitier->event->getEventConfig())->toBeArray() ->and($this->nofitier->event->getEventConfig())->toHaveKey('tag_push'); - $this->nofitier->setPlatFormForEvent('github', 'config/jsons/github-events.json'); + $this->nofitier->setPlatFormForEvent('github', 'storage/json/tgn/github-events.json'); expect($this->nofitier->event->getEventConfig())->toBeArray() ->and($this->nofitier->event->getEventConfig()) ->toHaveKey('issue_comment'); diff --git a/tests/ValidatorTest.php b/tests/ValidatorTest.php index 7eaef82..02eeafa 100644 --- a/tests/ValidatorTest.php +++ b/tests/ValidatorTest.php @@ -5,7 +5,7 @@ beforeEach(function () { $this->bot = new Bot(); - $this->bot->updateSetting('config/jsons/tgn-settings.json'); + $this->bot->updateSetting('storage/json/tgn/setting.json'); $this->bot->setPlatFormForEvent(); $this->validator = new Validator($this->bot->setting, $this->bot->event); }); From 9a091aa9b118d67f23ae958323793c70343ab350 Mon Sep 17 00:00:00 2001 From: Tan Nguyen Date: Mon, 2 Mar 2026 18:17:31 +0700 Subject: [PATCH 08/14] fix: resolve PHPStan level 9 errors in core package - Add proper array type hints to interfaces and trait implementations - Remove dead catch clauses in ConfigHelper (Throwable covers all) - Remove unnecessary nullsafe operator in ActionEventTrait - Guard nullable array key in EventTrait with fallback - Fix config paths to match actual JSON file locations - Remove unused imports Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- config/tg-notifier.php | 6 ++-- src/Constants/SettingConstant.php | 26 +++++++-------- src/Enums/Platform.php | 14 ++++---- src/Helpers/ConfigHelper.php | 9 ++--- src/Interfaces/BotInterface.php | 2 +- src/Interfaces/SettingInterface.php | 14 ++++---- src/Interfaces/Structures/AppInterface.php | 14 ++++---- .../Structures/NotificationInterface.php | 4 +-- src/Structures/App.php | 33 +++++++++++++++++++ src/Structures/Notification.php | 4 +++ src/Trait/ActionEventTrait.php | 4 +-- src/Trait/BotSettingTrait.php | 13 ++++++++ src/Trait/EventSettingTrait.php | 12 +++++++ src/Trait/EventTrait.php | 2 +- tests/ConfigTest.php | 6 ++-- tests/NotifierTest.php | 4 +-- tests/ValidatorTest.php | 2 +- 17 files changed, 116 insertions(+), 53 deletions(-) diff --git a/config/tg-notifier.php b/config/tg-notifier.php index 3b731d0..ae64e52 100644 --- a/config/tg-notifier.php +++ b/config/tg-notifier.php @@ -42,13 +42,13 @@ /** Set the path to the data file */ 'data_file' => [ 'setting' => $_ENV['TGN_PATH_SETTING'] ?? - 'storage/json/tgn/tgn-settings.json', + 'config/jsons/tgn-settings.json', 'platform' => [ 'gitlab' => $_ENV['TGN_PATH_PLATFORM_GITLAB'] ?? - 'storage/json/tgn/gitlab-events.json', + 'config/jsons/gitlab-events.json', 'github' => $_ENV['TGN_PATH_PLATFORM_GITHUB'] ?? - 'storage/json/tgn/github-events.json', + 'config/jsons/github-events.json', ], ], diff --git a/src/Constants/SettingConstant.php b/src/Constants/SettingConstant.php index 4bcaf65..b0ff990 100644 --- a/src/Constants/SettingConstant.php +++ b/src/Constants/SettingConstant.php @@ -4,29 +4,29 @@ final class SettingConstant { - public const SETTING_PREFIX = 'stg.'; + public const string SETTING_PREFIX = 'stg.'; - public const T_IS_NOTIFIED = 'is_notified'; + public const string T_IS_NOTIFIED = 'is_notified'; - public const T_ALL_EVENTS_NOTIFICATION = 'all_events_notify'; + public const string T_ALL_EVENTS_NOTIFICATION = 'all_events_notify'; - public const SETTING_IS_NOTIFIED = self::SETTING_PREFIX . self::T_IS_NOTIFIED; + public const string SETTING_IS_NOTIFIED = self::SETTING_PREFIX . self::T_IS_NOTIFIED; - public const SETTING_ALL_EVENTS_NOTIFY = self::SETTING_PREFIX . self::T_ALL_EVENTS_NOTIFICATION; + public const string SETTING_ALL_EVENTS_NOTIFY = self::SETTING_PREFIX . self::T_ALL_EVENTS_NOTIFICATION; - public const SETTING_CUSTOM_EVENTS = self::SETTING_PREFIX . 'cus'; + public const string SETTING_CUSTOM_EVENTS = self::SETTING_PREFIX . 'cus'; - public const SETTING_GITHUB_EVENTS = self::SETTING_CUSTOM_EVENTS . EventConstant::GITHUB_EVENT_SEPARATOR; + public const string SETTING_GITHUB_EVENTS = self::SETTING_CUSTOM_EVENTS . EventConstant::GITHUB_EVENT_SEPARATOR; - public const SETTING_GITLAB_EVENTS = self::SETTING_CUSTOM_EVENTS . EventConstant::GITLAB_EVENT_SEPARATOR; + public const string SETTING_GITLAB_EVENTS = self::SETTING_CUSTOM_EVENTS . EventConstant::GITLAB_EVENT_SEPARATOR; - public const BTN_LINE_ITEM_COUNT = 2; + public const int BTN_LINE_ITEM_COUNT = 2; - public const SETTING_BACK = self::SETTING_PREFIX . 'back.'; + public const string SETTING_BACK = self::SETTING_PREFIX . 'back.'; - public const SETTING_BACK_TO_MAIN_MENU = self::SETTING_BACK . 'menu'; + public const string SETTING_BACK_TO_MAIN_MENU = self::SETTING_BACK . 'menu'; - public const SETTING_BACK_TO_EVENTS_MENU = self::SETTING_BACK . 'settings.custom_events'; + public const string SETTING_BACK_TO_EVENTS_MENU = self::SETTING_BACK . 'settings.custom_events'; - public const SETTING_BACK_TO_SETTINGS_MENU = self::SETTING_BACK . 'settings'; + public const string SETTING_BACK_TO_SETTINGS_MENU = self::SETTING_BACK . 'settings'; } diff --git a/src/Enums/Platform.php b/src/Enums/Platform.php index 78a6336..cb8ed47 100644 --- a/src/Enums/Platform.php +++ b/src/Enums/Platform.php @@ -4,22 +4,22 @@ enum Platform: string { - case GitHub = 'github'; - case GitLab = 'gitlab'; + case GITHUB = 'github'; + case GITLAB = 'gitlab'; public function eventSeparator(): string { return match ($this) { - self::GitHub => 'gh.', - self::GitLab => 'gl.', + self::GITHUB => 'gh.', + self::GITLAB => 'gl.', }; } public function webhookEventHeader(): string { return match ($this) { - self::GitHub => 'HTTP_X_GITHUB_EVENT', - self::GitLab => 'HTTP_X_GITLAB_EVENT', + self::GITHUB => 'HTTP_X_GITHUB_EVENT', + self::GITLAB => 'HTTP_X_GITLAB_EVENT', }; } @@ -47,6 +47,6 @@ public static function eventSeparators(): array public static function default(): self { - return self::GitHub; + return self::GITHUB; } } diff --git a/src/Helpers/ConfigHelper.php b/src/Helpers/ConfigHelper.php index bf99616..a0eb6a9 100644 --- a/src/Helpers/ConfigHelper.php +++ b/src/Helpers/ConfigHelper.php @@ -2,7 +2,6 @@ namespace CSlant\TelegramGitNotifier\Helpers; -use CSlant\TelegramGitNotifier\Exceptions\EntryNotFoundException; use CSlant\TelegramGitNotifier\Exceptions\InvalidViewTemplateException; use Throwable; @@ -43,8 +42,10 @@ public function execConfig(string $string): mixed * Uses output buffering and a closure to isolate the template scope, * preventing variable pollution. * - * @param string $partialPath Dot-notation path to the view file - * @param array $data Variables to pass to the template + * @param string $partialPath Dot-notation path to the view file + * @param array $data Variables to pass to the template + * + * @throws InvalidViewTemplateException */ public function getTemplateData(string $partialPath, array $data = []): string { @@ -63,7 +64,7 @@ public function getTemplateData(string $partialPath, array $data = []): string return (string) ob_get_clean(); })($viewPathFile, $data); - } catch (EntryNotFoundException|InvalidViewTemplateException|Throwable $e) { + } catch (Throwable $e) { if (ob_get_level() > 0) { ob_end_clean(); } diff --git a/src/Interfaces/BotInterface.php b/src/Interfaces/BotInterface.php index a65e163..16a0935 100644 --- a/src/Interfaces/BotInterface.php +++ b/src/Interfaces/BotInterface.php @@ -11,7 +11,7 @@ interface BotInterface extends AppInterface, EventInterface, SettingInterface /** * Set the menu button for a telegram * - * @param array $menuCommand + * @param array $menuCommand * @param string|null $view * * @return void diff --git a/src/Interfaces/SettingInterface.php b/src/Interfaces/SettingInterface.php index 122f209..4e5ac37 100644 --- a/src/Interfaces/SettingInterface.php +++ b/src/Interfaces/SettingInterface.php @@ -17,7 +17,7 @@ interface SettingInterface * @param string|null $parentEvent * @param string $platform * - * @return array + * @return array> * @see EventSettingTrait::eventMarkup() */ public function eventMarkup(?string $parentEvent = null, string $platform = EventConstant::DEFAULT_PLATFORM): array; @@ -27,7 +27,7 @@ public function eventMarkup(?string $parentEvent = null, string $platform = Even * * @param string $event * @param string $platform - * @param array|bool $value + * @param array|bool $value * @param string|null $parentEvent * * @return string @@ -39,7 +39,7 @@ public function getCallbackData(string $event, string $platform, array|bool $val * Get event name for markup * * @param string $event - * @param bool|array $value + * @param bool|array $value * * @return string * @see EventSettingTrait::getEventName() @@ -52,7 +52,7 @@ public function getEventName(string $event, bool|array $value): string; * @param string $platform * @param string|null $parentEvent * - * @return array + * @return array * @see EventSettingTrait::getEndKeyboard() */ public function getEndKeyboard(string $platform, ?string $parentEvent = null): array; @@ -163,15 +163,15 @@ public function settingHandle(?string $view = null): void; /** * Generate setting markup * - * @return array[] + * @return array> * @see BotSettingTrait::settingMarkup() */ public function settingMarkup(): array; /** - * @param array $markup + * @param array> $markup * - * @return array + * @return array> * @see BotSettingTrait::customEventMarkup() */ public function customEventMarkup(array $markup): array; diff --git a/src/Interfaces/Structures/AppInterface.php b/src/Interfaces/Structures/AppInterface.php index e9d53b0..ebd3d79 100644 --- a/src/Interfaces/Structures/AppInterface.php +++ b/src/Interfaces/Structures/AppInterface.php @@ -13,7 +13,7 @@ interface AppInterface * Send a message to telegram * * @param string|null $message - * @param array $options + * @param array $options * * @return void * @throws MessageIsEmptyException @@ -25,7 +25,7 @@ public function sendMessage(?string $message = '', array $options = []): void; * Send a photo to telegram * * @param string $photo (path to photo) - * @param array $caption + * @param array $caption * * @return void * @throws EntryNotFoundException @@ -37,7 +37,7 @@ public function sendPhoto(string $photo = '', array $caption = []): void; * Send callback response to telegram (show alert) * * @param string|null $text - * @param array $options + * @param array $options * * @return void * @throws MessageIsEmptyException @@ -50,7 +50,7 @@ public function answerCallbackQuery(?string $text = null, array $options = []): * Edit message text and reply markup * * @param string|null $text - * @param array $options + * @param array $options * * @return void * @throws BotException @@ -61,7 +61,7 @@ public function editMessageText(?string $text = null, array $options = []): void /** * Edit message reply markup from a telegram * - * @param array $options + * @param array $options * * @return void * @throws BotException @@ -80,9 +80,9 @@ public function getCallbackTextMessage(): string; /** * Create content for a callback message * - * @param array $options + * @param array $options * - * @return array + * @return array * @see App::setCallbackContentMessage() */ public function setCallbackContentMessage(array $options = []): array; diff --git a/src/Interfaces/Structures/NotificationInterface.php b/src/Interfaces/Structures/NotificationInterface.php index ed6fe21..752ea4c 100644 --- a/src/Interfaces/Structures/NotificationInterface.php +++ b/src/Interfaces/Structures/NotificationInterface.php @@ -41,7 +41,7 @@ public function setPayload(Request $request, string $event): ?object; * Send notification to telegram * * @param string|null $message - * @param array $options + * @param array $options * * @return bool * @throws SendNotificationException @@ -65,7 +65,7 @@ public function getActionOfEvent(object $payload): string; * * @param string|null $chatIds * - * @return array + * @return array> */ public function parseNotifyChatIds(?string $chatIds = null): array; } diff --git a/src/Structures/App.php b/src/Structures/App.php index 66050c9..beceaaf 100644 --- a/src/Structures/App.php +++ b/src/Structures/App.php @@ -20,6 +20,9 @@ public function setCurrentChatBotId(?string $chatBotId = null): void $this->chatBotId = $chatBotId ?? config('telegram-git-notifier.bot.chat_id'); } + /** + * @return array + */ private function createTelegramBaseContent(): array { return [ @@ -29,6 +32,11 @@ private function createTelegramBaseContent(): array ]; } + /** + * @param array $options + * + * @throws MessageIsEmptyException + */ public function sendMessage(?string $message = '', array $options = []): void { if ($message === null || $message === '') { @@ -50,6 +58,11 @@ public function sendMessage(?string $message = '', array $options = []): void $this->telegram->sendMessage($content); } + /** + * @param array $options + * + * @throws EntryNotFoundException + */ public function sendPhoto(string $photo = '', array $options = []): void { if ($photo === '') { @@ -64,6 +77,12 @@ public function sendPhoto(string $photo = '', array $options = []): void $this->telegram->sendPhoto($content); } + /** + * @param array $options + * + * @throws MessageIsEmptyException + * @throws CallbackException + */ public function answerCallbackQuery(?string $text = null, array $options = []): void { if ($text === null || $text === '') { @@ -82,6 +101,11 @@ public function answerCallbackQuery(?string $text = null, array $options = []): } } + /** + * @param array $options + * + * @throws BotException + */ public function editMessageText( ?string $text = null, array $options = [] @@ -97,6 +121,11 @@ public function editMessageText( } } + /** + * @param array $options + * + * @throws BotException + */ public function editMessageReplyMarkup(array $options = []): void { try { @@ -113,6 +142,10 @@ public function getCallbackTextMessage(): string return $this->telegram->Callback_Message()['text']; } + /** + * @param array $options + * @return array + */ public function setCallbackContentMessage(array $options = []): array { $content = [ diff --git a/src/Structures/Notification.php b/src/Structures/Notification.php index 7a83329..0fe9a30 100644 --- a/src/Structures/Notification.php +++ b/src/Structures/Notification.php @@ -86,6 +86,8 @@ private function setMessage(string $typeEvent): void /** * Send notification message to Telegram. * + * @param array $options + * * @throws SendNotificationException */ #[\NoDiscard('The return value indicates whether the notification was sent successfully')] @@ -121,6 +123,8 @@ public function sendNotify(?string $message = null, array $options = []): bool /** * Send HTTP request with exponential backoff retry for rate limits. * + * @param array $options + * * @throws SendNotificationException */ private function sendWithRetry(string $url, array $options, int $maxRetries = 3): bool diff --git a/src/Trait/ActionEventTrait.php b/src/Trait/ActionEventTrait.php index 21c985f..552dc38 100644 --- a/src/Trait/ActionEventTrait.php +++ b/src/Trait/ActionEventTrait.php @@ -7,8 +7,8 @@ trait ActionEventTrait public function getActionOfEvent(object $payload): string { $action = $payload->action - ?? $payload->object_attributes?->action - ?? $payload->object_attributes?->noteable_type + ?? $payload->object_attributes->action + ?? $payload->object_attributes->noteable_type ?? ''; if ($action !== '') { diff --git a/src/Trait/BotSettingTrait.php b/src/Trait/BotSettingTrait.php index 49393bb..0d828c5 100644 --- a/src/Trait/BotSettingTrait.php +++ b/src/Trait/BotSettingTrait.php @@ -3,6 +3,7 @@ namespace CSlant\TelegramGitNotifier\Trait; use CSlant\TelegramGitNotifier\Constants\SettingConstant; +use CSlant\TelegramGitNotifier\Exceptions\MessageIsEmptyException; trait BotSettingTrait { @@ -16,6 +17,11 @@ public function updateSetting(?string $settingFile = null): void $this->setting->setSettingConfig(); } + /** + * @param array $menuCommand + * + * @throws MessageIsEmptyException + */ public function setMyCommands( array $menuCommand, ?string $view = null @@ -39,6 +45,9 @@ public function settingHandle(?string $view = null): void ); } + /** + * @return array> + */ public function settingMarkup(): array { $markup = [ @@ -75,6 +84,10 @@ public function settingMarkup(): array return $markup; } + /** + * @param array> $markup + * @return array> + */ public function customEventMarkup(array $markup): array { if (!$this->setting->getSettings()[SettingConstant::T_ALL_EVENTS_NOTIFICATION]) { diff --git a/src/Trait/EventSettingTrait.php b/src/Trait/EventSettingTrait.php index f72c267..6fa12c7 100644 --- a/src/Trait/EventSettingTrait.php +++ b/src/Trait/EventSettingTrait.php @@ -7,6 +7,9 @@ trait EventSettingTrait { + /** + * @return array> + */ public function eventMarkup( ?string $parentEvent = null, string $platform = EventConstant::DEFAULT_PLATFORM, @@ -44,6 +47,9 @@ public function eventMarkup( return $replyMarkup; } + /** + * @param array|bool $value + */ public function getCallbackData( string $event, string $platform, @@ -65,6 +71,9 @@ public function getCallbackData( return $prefix . $event . EventConstant::EVENT_UPDATE_SEPARATOR; } + /** + * @param bool|array $value + */ public function getEventName(string $event, bool|array $value = false): string { if (is_array($value)) { @@ -76,6 +85,9 @@ public function getEventName(string $event, bool|array $value = false): string return '❌ ' . $event; } + /** + * @return array + */ public function getEndKeyboard( string $platform, ?string $parentEvent = null diff --git a/src/Trait/EventTrait.php b/src/Trait/EventTrait.php index 58dc9d9..a815e01 100644 --- a/src/Trait/EventTrait.php +++ b/src/Trait/EventTrait.php @@ -16,7 +16,7 @@ public function setPlatFormForEvent( ): void { /** @var array $platformFileDefaults */ $platformFileDefaults = config('telegram-git-notifier.data_file.platform'); - $this->event->platformFile = $platformFile ?? $platformFileDefaults[$platform] ?? ''; + $this->event->platformFile = $platformFile ?? $platformFileDefaults[$platform ?? EventConstant::DEFAULT_PLATFORM] ?? ''; $this->event->setEventConfig($platform); } diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php index da57c6c..157f4df 100644 --- a/tests/ConfigTest.php +++ b/tests/ConfigTest.php @@ -21,10 +21,10 @@ }); it('platform can be set for event with platform file', function () { - $this->bot->setPlatFormForEvent('gitlab', 'storage/json/tgn/gitlab-events.json'); + $this->bot->setPlatFormForEvent('gitlab', 'config/jsons/gitlab-events.json'); expect($this->bot->event->platform)->toBe('gitlab') ->and($this->bot->event->platformFile) - ->toBe('storage/json/tgn/gitlab-events.json'); + ->toBe('config/jsons/gitlab-events.json'); }); it('can get json config for event - github', function () { @@ -34,7 +34,7 @@ }); it('can get json config for event - gitlab', function () { - $this->bot->setPlatFormForEvent('gitlab', 'storage/json/tgn/gitlab-events.json'); + $this->bot->setPlatFormForEvent('gitlab', 'config/jsons/gitlab-events.json'); expect($this->bot->event->getEventConfig())->toBeArray() ->and($this->bot->event->getEventConfig())->toHaveKey('tag_push'); }); diff --git a/tests/NotifierTest.php b/tests/NotifierTest.php index 15373fe..e14ec52 100644 --- a/tests/NotifierTest.php +++ b/tests/NotifierTest.php @@ -7,11 +7,11 @@ }); it('validates that the event files exist', function () { - $this->nofitier->setPlatFormForEvent('gitlab', 'storage/json/tgn/gitlab-events.json'); + $this->nofitier->setPlatFormForEvent('gitlab', 'config/jsons/gitlab-events.json'); expect($this->nofitier->event->getEventConfig())->toBeArray() ->and($this->nofitier->event->getEventConfig())->toHaveKey('tag_push'); - $this->nofitier->setPlatFormForEvent('github', 'storage/json/tgn/github-events.json'); + $this->nofitier->setPlatFormForEvent('github', 'config/jsons/github-events.json'); expect($this->nofitier->event->getEventConfig())->toBeArray() ->and($this->nofitier->event->getEventConfig()) ->toHaveKey('issue_comment'); diff --git a/tests/ValidatorTest.php b/tests/ValidatorTest.php index 02eeafa..7eaef82 100644 --- a/tests/ValidatorTest.php +++ b/tests/ValidatorTest.php @@ -5,7 +5,7 @@ beforeEach(function () { $this->bot = new Bot(); - $this->bot->updateSetting('storage/json/tgn/setting.json'); + $this->bot->updateSetting('config/jsons/tgn-settings.json'); $this->bot->setPlatFormForEvent(); $this->validator = new Validator($this->bot->setting, $this->bot->event); }); From 5fe979c171627244debbc7fc1bd386d37b9640e9 Mon Sep 17 00:00:00 2001 From: Tan Nguyen Date: Mon, 2 Mar 2026 18:24:32 +0700 Subject: [PATCH 09/14] refactor: migrate platform constants from EventConstant to Platform enum - Move DEFAULT_PLATFORM, WEBHOOK_EVENT_HEADER, event separators, and PLATFORM_EVENT_SEPARATOR into Platform enum methods - Add Platform::DEFAULT const for use in default parameter values - Remove platformEnum() static helper (use Platform::from() directly) - EventConstant now only contains callback serialization constants - SettingConstant uses hardcoded separator strings (const context) - All 16 tests passing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Bot.php | 4 ++-- src/Constants/EventConstant.php | 23 ------------------ src/Constants/SettingConstant.php | 4 ++-- src/Enums/Platform.php | 2 ++ src/Interfaces/EventInterface.php | 4 ++-- src/Interfaces/SettingInterface.php | 4 ++-- src/Models/Event.php | 5 ++-- src/Notifier.php | 4 ++-- src/Structures/Notification.php | 4 ++-- src/Trait/EventSettingTrait.php | 36 ++++++++++++++++------------- src/Trait/EventTrait.php | 8 +++---- 11 files changed, 40 insertions(+), 58 deletions(-) diff --git a/src/Bot.php b/src/Bot.php index 720817a..55421d1 100644 --- a/src/Bot.php +++ b/src/Bot.php @@ -2,7 +2,7 @@ namespace CSlant\TelegramGitNotifier; -use CSlant\TelegramGitNotifier\Constants\EventConstant; +use CSlant\TelegramGitNotifier\Enums\Platform; use CSlant\TelegramGitNotifier\Exceptions\ConfigFileException; use CSlant\TelegramGitNotifier\Interfaces\BotInterface; use CSlant\TelegramGitNotifier\Models\Event; @@ -42,7 +42,7 @@ public function __construct( ?Telegram $telegram = null, ?string $chatBotId = null, ?Event $event = null, - ?string $platform = EventConstant::DEFAULT_PLATFORM, + ?string $platform = Platform::DEFAULT, ?string $platformFile = null, ?Setting $setting = null, ?string $settingFile = null, diff --git a/src/Constants/EventConstant.php b/src/Constants/EventConstant.php index 6838298..19a3edc 100644 --- a/src/Constants/EventConstant.php +++ b/src/Constants/EventConstant.php @@ -2,34 +2,11 @@ namespace CSlant\TelegramGitNotifier\Constants; -use CSlant\TelegramGitNotifier\Enums\Platform; - final class EventConstant { - public const DEFAULT_PLATFORM = 'github'; - - public const WEBHOOK_EVENT_HEADER = [ - 'github' => 'HTTP_X_GITHUB_EVENT', - 'gitlab' => 'HTTP_X_GITLAB_EVENT', - ]; - public const EVENT_PREFIX = SettingConstant::SETTING_CUSTOM_EVENTS . '.evt.'; - public const GITHUB_EVENT_SEPARATOR = 'gh.'; - - public const GITLAB_EVENT_SEPARATOR = 'gl.'; - public const EVENT_HAS_ACTION_SEPARATOR = 'atc.'; public const EVENT_UPDATE_SEPARATOR = '.eus'; - - public const PLATFORM_EVENT_SEPARATOR = [ - 'github' => self::GITHUB_EVENT_SEPARATOR, - 'gitlab' => self::GITLAB_EVENT_SEPARATOR, - ]; - - public static function platformEnum(string $platform): Platform - { - return Platform::from($platform); - } } diff --git a/src/Constants/SettingConstant.php b/src/Constants/SettingConstant.php index b0ff990..2d2ed59 100644 --- a/src/Constants/SettingConstant.php +++ b/src/Constants/SettingConstant.php @@ -16,9 +16,9 @@ final class SettingConstant public const string SETTING_CUSTOM_EVENTS = self::SETTING_PREFIX . 'cus'; - public const string SETTING_GITHUB_EVENTS = self::SETTING_CUSTOM_EVENTS . EventConstant::GITHUB_EVENT_SEPARATOR; + public const string SETTING_GITHUB_EVENTS = self::SETTING_CUSTOM_EVENTS . 'gh.'; - public const string SETTING_GITLAB_EVENTS = self::SETTING_CUSTOM_EVENTS . EventConstant::GITLAB_EVENT_SEPARATOR; + public const string SETTING_GITLAB_EVENTS = self::SETTING_CUSTOM_EVENTS . 'gl.'; public const int BTN_LINE_ITEM_COUNT = 2; diff --git a/src/Enums/Platform.php b/src/Enums/Platform.php index cb8ed47..f2084ec 100644 --- a/src/Enums/Platform.php +++ b/src/Enums/Platform.php @@ -7,6 +7,8 @@ enum Platform: string case GITHUB = 'github'; case GITLAB = 'gitlab'; + public const DEFAULT = 'github'; + public function eventSeparator(): string { return match ($this) { diff --git a/src/Interfaces/EventInterface.php b/src/Interfaces/EventInterface.php index 311af35..6a56cea 100644 --- a/src/Interfaces/EventInterface.php +++ b/src/Interfaces/EventInterface.php @@ -2,7 +2,7 @@ namespace CSlant\TelegramGitNotifier\Interfaces; -use CSlant\TelegramGitNotifier\Constants\EventConstant; +use CSlant\TelegramGitNotifier\Enums\Platform; use CSlant\TelegramGitNotifier\Exceptions\ConfigFileException; use CSlant\TelegramGitNotifier\Trait\ActionEventTrait; use Symfony\Component\HttpFoundation\Request; @@ -28,7 +28,7 @@ public function getActionOfEvent(object $payload): string; * @return void * @see EventTrait::setPlatFormForEvent() */ - public function setPlatFormForEvent(?string $platform = EventConstant::DEFAULT_PLATFORM, ?string $platformFile = null): void; + public function setPlatFormForEvent(?string $platform = Platform::DEFAULT, ?string $platformFile = null): void; /** * Set event config and get event name diff --git a/src/Interfaces/SettingInterface.php b/src/Interfaces/SettingInterface.php index 4e5ac37..ce16b65 100644 --- a/src/Interfaces/SettingInterface.php +++ b/src/Interfaces/SettingInterface.php @@ -2,7 +2,7 @@ namespace CSlant\TelegramGitNotifier\Interfaces; -use CSlant\TelegramGitNotifier\Constants\EventConstant; +use CSlant\TelegramGitNotifier\Enums\Platform; use CSlant\TelegramGitNotifier\Exceptions\BotException; use CSlant\TelegramGitNotifier\Exceptions\InvalidViewTemplateException; use CSlant\TelegramGitNotifier\Exceptions\MessageIsEmptyException; @@ -20,7 +20,7 @@ interface SettingInterface * @return array> * @see EventSettingTrait::eventMarkup() */ - public function eventMarkup(?string $parentEvent = null, string $platform = EventConstant::DEFAULT_PLATFORM): array; + public function eventMarkup(?string $parentEvent = null, string $platform = Platform::DEFAULT): array; /** * Get callback data for markup diff --git a/src/Models/Event.php b/src/Models/Event.php index 961379a..1fdcba4 100644 --- a/src/Models/Event.php +++ b/src/Models/Event.php @@ -2,7 +2,6 @@ namespace CSlant\TelegramGitNotifier\Models; -use CSlant\TelegramGitNotifier\Constants\EventConstant; use CSlant\TelegramGitNotifier\Enums\Platform; class Event @@ -14,7 +13,7 @@ class Event private string $lastLoadedFile = ''; - public string $platform = EventConstant::DEFAULT_PLATFORM; + public string $platform = Platform::DEFAULT; public string $platformFile = '' { get { @@ -39,7 +38,7 @@ public function getEventConfig(): array */ public function setEventConfig(?string $platform = null): void { - $newPlatform = $platform ?? EventConstant::DEFAULT_PLATFORM; + $newPlatform = $platform ?? Platform::DEFAULT; $this->platform = $newPlatform; // Skip re-reading if same file is already loaded diff --git a/src/Notifier.php b/src/Notifier.php index 437b48a..bb5148a 100644 --- a/src/Notifier.php +++ b/src/Notifier.php @@ -2,8 +2,8 @@ namespace CSlant\TelegramGitNotifier; -use CSlant\TelegramGitNotifier\Constants\EventConstant; use CSlant\TelegramGitNotifier\DTOs\ChatTarget; +use CSlant\TelegramGitNotifier\Enums\Platform; use CSlant\TelegramGitNotifier\Exceptions\ConfigFileException; use CSlant\TelegramGitNotifier\Interfaces\EventInterface; use CSlant\TelegramGitNotifier\Interfaces\Structures\NotificationInterface; @@ -32,7 +32,7 @@ public function __construct( ?Telegram $telegram = null, ?string $chatBotId = null, ?Event $event = null, - ?string $platform = EventConstant::DEFAULT_PLATFORM, + ?string $platform = Platform::DEFAULT, ?string $platformFile = null, ?Client $client = null, ) { diff --git a/src/Structures/Notification.php b/src/Structures/Notification.php index 0fe9a30..68673e7 100644 --- a/src/Structures/Notification.php +++ b/src/Structures/Notification.php @@ -2,7 +2,7 @@ namespace CSlant\TelegramGitNotifier\Structures; -use CSlant\TelegramGitNotifier\Constants\EventConstant; +use CSlant\TelegramGitNotifier\Enums\Platform; use CSlant\TelegramGitNotifier\Exceptions\MessageIsEmptyException; use CSlant\TelegramGitNotifier\Exceptions\SendNotificationException; use GuzzleHttp\Exception\GuzzleException; @@ -40,7 +40,7 @@ public function setPayload(Request $request, string $event): ?object { $content = match ($this->event->platform) { 'gitlab' => $request->getContent(), - EventConstant::DEFAULT_PLATFORM => $request->request->get('payload'), + Platform::DEFAULT => $request->request->get('payload'), default => null, }; diff --git a/src/Trait/EventSettingTrait.php b/src/Trait/EventSettingTrait.php index 6fa12c7..baf5949 100644 --- a/src/Trait/EventSettingTrait.php +++ b/src/Trait/EventSettingTrait.php @@ -4,6 +4,7 @@ use CSlant\TelegramGitNotifier\Constants\EventConstant; use CSlant\TelegramGitNotifier\Constants\SettingConstant; +use CSlant\TelegramGitNotifier\Enums\Platform; trait EventSettingTrait { @@ -12,7 +13,7 @@ trait EventSettingTrait */ public function eventMarkup( ?string $parentEvent = null, - string $platform = EventConstant::DEFAULT_PLATFORM, + string $platform = Platform::DEFAULT, ?string $platformFile = null ): array { $replyMarkup = $replyMarkupItem = []; @@ -56,9 +57,7 @@ public function getCallbackData( array|bool $value = false, ?string $parentEvent = null ): string { - $platformSeparator = $platform === EventConstant::DEFAULT_PLATFORM - ? EventConstant::GITHUB_EVENT_SEPARATOR - : EventConstant::GITLAB_EVENT_SEPARATOR; + $platformSeparator = Platform::from($platform)->eventSeparator(); $prefix = EventConstant::EVENT_PREFIX . $platformSeparator; if (is_array($value)) { @@ -136,14 +135,14 @@ public function getPlatformFromCallback( } if ($callback) { - return match (true) { - str_contains($callback, EventConstant::GITHUB_EVENT_SEPARATOR) => 'github', - str_contains($callback, EventConstant::GITLAB_EVENT_SEPARATOR) => 'gitlab', - default => EventConstant::DEFAULT_PLATFORM, - }; + foreach (Platform::cases() as $p) { + if (str_contains($callback, $p->eventSeparator())) { + return $p->value; + } + } } - return EventConstant::DEFAULT_PLATFORM; + return Platform::DEFAULT; } public function sendSettingEventMessage( @@ -175,11 +174,16 @@ public function getEventFromCallback(?string $callback): string return ''; } - return str_replace([ - EventConstant::EVENT_PREFIX, - EventConstant::GITHUB_EVENT_SEPARATOR, - EventConstant::GITLAB_EVENT_SEPARATOR, - ], '', $callback); + $separators = array_map( + fn (Platform $p) => $p->eventSeparator(), + Platform::cases(), + ); + + return str_replace( + [EventConstant::EVENT_PREFIX, ...$separators], + '', + $callback, + ); } public function handleEventWithActions( @@ -235,7 +239,7 @@ public function eventUpdateHandle( $this->event->updateEvent($event, $action); $this->eventHandle( $action - ? EventConstant::PLATFORM_EVENT_SEPARATOR[$platform] + ? Platform::from($platform)->eventSeparator() . EventConstant::EVENT_HAS_ACTION_SEPARATOR . $event : null, $platform diff --git a/src/Trait/EventTrait.php b/src/Trait/EventTrait.php index a815e01..809f8f6 100644 --- a/src/Trait/EventTrait.php +++ b/src/Trait/EventTrait.php @@ -2,7 +2,7 @@ namespace CSlant\TelegramGitNotifier\Trait; -use CSlant\TelegramGitNotifier\Constants\EventConstant; +use CSlant\TelegramGitNotifier\Enums\Platform; use CSlant\TelegramGitNotifier\Exceptions\ConfigFileException; use Symfony\Component\HttpFoundation\Request; @@ -11,18 +11,18 @@ trait EventTrait use ActionEventTrait; public function setPlatFormForEvent( - ?string $platform = EventConstant::DEFAULT_PLATFORM, + ?string $platform = Platform::DEFAULT, ?string $platformFile = null, ): void { /** @var array $platformFileDefaults */ $platformFileDefaults = config('telegram-git-notifier.data_file.platform'); - $this->event->platformFile = $platformFile ?? $platformFileDefaults[$platform ?? EventConstant::DEFAULT_PLATFORM] ?? ''; + $this->event->platformFile = $platformFile ?? $platformFileDefaults[$platform ?? Platform::DEFAULT] ?? ''; $this->event->setEventConfig($platform); } public function handleEventFromRequest(Request $request): ?string { - foreach (EventConstant::WEBHOOK_EVENT_HEADER as $platform => $header) { + foreach (Platform::webhookEventHeaders() as $platform => $header) { $event = $request->server->get($header); if ($event !== null) { $this->event->platform = $platform; From f85264d890f2327dd87178c1d0f27481c832308e Mon Sep 17 00:00:00 2001 From: Tan Nguyen Date: Mon, 2 Mar 2026 19:07:59 +0700 Subject: [PATCH 10/14] fix: update file paths for event configurations in tests and settings --- config/tg-notifier.php | 6 +++--- tests/ConfigTest.php | 6 +++--- tests/NotifierTest.php | 4 ++-- tests/ValidatorTest.php | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/config/tg-notifier.php b/config/tg-notifier.php index ae64e52..3b731d0 100644 --- a/config/tg-notifier.php +++ b/config/tg-notifier.php @@ -42,13 +42,13 @@ /** Set the path to the data file */ 'data_file' => [ 'setting' => $_ENV['TGN_PATH_SETTING'] ?? - 'config/jsons/tgn-settings.json', + 'storage/json/tgn/tgn-settings.json', 'platform' => [ 'gitlab' => $_ENV['TGN_PATH_PLATFORM_GITLAB'] ?? - 'config/jsons/gitlab-events.json', + 'storage/json/tgn/gitlab-events.json', 'github' => $_ENV['TGN_PATH_PLATFORM_GITHUB'] ?? - 'config/jsons/github-events.json', + 'storage/json/tgn/github-events.json', ], ], diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php index 157f4df..da57c6c 100644 --- a/tests/ConfigTest.php +++ b/tests/ConfigTest.php @@ -21,10 +21,10 @@ }); it('platform can be set for event with platform file', function () { - $this->bot->setPlatFormForEvent('gitlab', 'config/jsons/gitlab-events.json'); + $this->bot->setPlatFormForEvent('gitlab', 'storage/json/tgn/gitlab-events.json'); expect($this->bot->event->platform)->toBe('gitlab') ->and($this->bot->event->platformFile) - ->toBe('config/jsons/gitlab-events.json'); + ->toBe('storage/json/tgn/gitlab-events.json'); }); it('can get json config for event - github', function () { @@ -34,7 +34,7 @@ }); it('can get json config for event - gitlab', function () { - $this->bot->setPlatFormForEvent('gitlab', 'config/jsons/gitlab-events.json'); + $this->bot->setPlatFormForEvent('gitlab', 'storage/json/tgn/gitlab-events.json'); expect($this->bot->event->getEventConfig())->toBeArray() ->and($this->bot->event->getEventConfig())->toHaveKey('tag_push'); }); diff --git a/tests/NotifierTest.php b/tests/NotifierTest.php index e14ec52..15373fe 100644 --- a/tests/NotifierTest.php +++ b/tests/NotifierTest.php @@ -7,11 +7,11 @@ }); it('validates that the event files exist', function () { - $this->nofitier->setPlatFormForEvent('gitlab', 'config/jsons/gitlab-events.json'); + $this->nofitier->setPlatFormForEvent('gitlab', 'storage/json/tgn/gitlab-events.json'); expect($this->nofitier->event->getEventConfig())->toBeArray() ->and($this->nofitier->event->getEventConfig())->toHaveKey('tag_push'); - $this->nofitier->setPlatFormForEvent('github', 'config/jsons/github-events.json'); + $this->nofitier->setPlatFormForEvent('github', 'storage/json/tgn/github-events.json'); expect($this->nofitier->event->getEventConfig())->toBeArray() ->and($this->nofitier->event->getEventConfig()) ->toHaveKey('issue_comment'); diff --git a/tests/ValidatorTest.php b/tests/ValidatorTest.php index 7eaef82..02eeafa 100644 --- a/tests/ValidatorTest.php +++ b/tests/ValidatorTest.php @@ -5,7 +5,7 @@ beforeEach(function () { $this->bot = new Bot(); - $this->bot->updateSetting('config/jsons/tgn-settings.json'); + $this->bot->updateSetting('storage/json/tgn/setting.json'); $this->bot->setPlatFormForEvent(); $this->validator = new Validator($this->bot->setting, $this->bot->event); }); From fe7e812c312fb488de546b19c501302724e2c5b2 Mon Sep 17 00:00:00 2001 From: Tan Nguyen Date: Mon, 2 Mar 2026 19:10:05 +0700 Subject: [PATCH 11/14] feat: update PHP version to 8.4 in Scrutinizer configuration --- .scrutinizer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index a31c47b..52c4118 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -7,7 +7,7 @@ build: override: [ php-scrutinizer-run ] environment: php: - version: 8.2 + version: 8.4 checks: php: From 5210f63052b47b9ca1ba61ad3db7bf56d1658808 Mon Sep 17 00:00:00 2001 From: Tan Nguyen Date: Mon, 2 Mar 2026 19:13:08 +0700 Subject: [PATCH 12/14] fix: downgrade PHP version to 8.3 in Scrutinizer configuration --- .scrutinizer.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 52c4118..cfd0325 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -7,7 +7,7 @@ build: override: [ php-scrutinizer-run ] environment: php: - version: 8.4 + version: 8.3 checks: php: From 4b7c958cd84a01c7f756ab9cfca9896cdc8eee09 Mon Sep 17 00:00:00 2001 From: Tan Nguyen Date: Mon, 2 Mar 2026 19:20:03 +0700 Subject: [PATCH 13/14] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Structures/TelegramBot.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Structures/TelegramBot.php b/src/Structures/TelegramBot.php index 8bdfc50..9b411ed 100644 --- a/src/Structures/TelegramBot.php +++ b/src/Structures/TelegramBot.php @@ -25,6 +25,8 @@ public function isNotifyChat(): bool { $chatIds = config('telegram-git-notifier.bot.notify_chat_ids'); - return in_array((string) $this->telegram->ChatID(), (array) $chatIds, true); + $notifyChatIds = ChatTarget::parseNotifyChatIds($chatIds); + + return in_array((string) $this->telegram->ChatID(), $notifyChatIds, true); } } From dd5396947371567db56f4af7a104af835e6f0483 Mon Sep 17 00:00:00 2001 From: Tan Nguyen Date: Mon, 2 Mar 2026 19:20:34 +0700 Subject: [PATCH 14/14] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Webhook.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Webhook.php b/src/Webhook.php index e2032a1..8059906 100644 --- a/src/Webhook.php +++ b/src/Webhook.php @@ -93,7 +93,19 @@ private function callTelegramApi(string $endpoint, callable $exceptionFactory, i continue; } - throw $exceptionFactory(); + $baseException = $exceptionFactory(); + $message = $baseException->getMessage(); + + if ($e->getMessage() !== '') { + $suffix = 'GuzzleException: ' . $e->getMessage(); + $message = $message !== '' ? $message . ' | ' . $suffix : $suffix; + } + + throw new WebhookException( + $message, + $baseException->getCode(), + $e + ); } } }