From 4f4742e356d9553edf8a90f707ff2c0962885b5f Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Thu, 10 Oct 2024 18:09:26 +0800 Subject: [PATCH] [Notifier] Add LINE Bot bridge --- .../FrameworkExtension.php | 1 + .../Resources/config/notifier_transports.php | 1 + .../Notifier/Bridge/LineBot/.gitattributes | 3 + .../LineBot/.github/PULL_REQUEST_TEMPLATE.md | 8 ++ .../.github/workflows/close-pull-request.yml | 20 ++++ .../Notifier/Bridge/LineBot/.gitignore | 3 + .../Notifier/Bridge/LineBot/CHANGELOG.md | 7 ++ .../Component/Notifier/Bridge/LineBot/LICENSE | 19 ++++ .../Bridge/LineBot/LineBotTransport.php | 91 +++++++++++++++++++ .../LineBot/LineBotTransportFactory.php | 48 ++++++++++ .../Notifier/Bridge/LineBot/README.md | 24 +++++ .../Tests/LineBotTransportFactoryTest.php | 77 ++++++++++++++++ .../LineBot/Tests/LineBotTransportTest.php | 69 ++++++++++++++ .../Notifier/Bridge/LineBot/composer.json | 33 +++++++ .../Notifier/Bridge/LineBot/phpunit.xml.dist | 31 +++++++ .../Exception/UnsupportedSchemeException.php | 4 + .../UnsupportedSchemeExceptionTest.php | 2 + 17 files changed, 441 insertions(+) create mode 100644 src/Symfony/Component/Notifier/Bridge/LineBot/.gitattributes create mode 100644 src/Symfony/Component/Notifier/Bridge/LineBot/.github/PULL_REQUEST_TEMPLATE.md create mode 100644 src/Symfony/Component/Notifier/Bridge/LineBot/.github/workflows/close-pull-request.yml create mode 100644 src/Symfony/Component/Notifier/Bridge/LineBot/.gitignore create mode 100644 src/Symfony/Component/Notifier/Bridge/LineBot/CHANGELOG.md create mode 100644 src/Symfony/Component/Notifier/Bridge/LineBot/LICENSE create mode 100644 src/Symfony/Component/Notifier/Bridge/LineBot/LineBotTransport.php create mode 100644 src/Symfony/Component/Notifier/Bridge/LineBot/LineBotTransportFactory.php create mode 100644 src/Symfony/Component/Notifier/Bridge/LineBot/README.md create mode 100644 src/Symfony/Component/Notifier/Bridge/LineBot/Tests/LineBotTransportFactoryTest.php create mode 100644 src/Symfony/Component/Notifier/Bridge/LineBot/Tests/LineBotTransportTest.php create mode 100644 src/Symfony/Component/Notifier/Bridge/LineBot/composer.json create mode 100644 src/Symfony/Component/Notifier/Bridge/LineBot/phpunit.xml.dist diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 1393797711883..c4ea185b84beb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -2839,6 +2839,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ NotifierBridge\JoliNotif\JoliNotifTransportFactory::class => 'notifier.transport_factory.joli-notif', NotifierBridge\KazInfoTeh\KazInfoTehTransportFactory::class => 'notifier.transport_factory.kaz-info-teh', NotifierBridge\LightSms\LightSmsTransportFactory::class => 'notifier.transport_factory.light-sms', + NotifierBridge\LineBot\LineBotTransportFactory::class => 'notifier.transport_factory.line-bot', NotifierBridge\LineNotify\LineNotifyTransportFactory::class => 'notifier.transport_factory.line-notify', NotifierBridge\LinkedIn\LinkedInTransportFactory::class => 'notifier.transport_factory.linked-in', NotifierBridge\Lox24\Lox24TransportFactory::class => 'notifier.transport_factory.lox24', diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php index 7c8002993db0b..073eda08b3413 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php @@ -32,6 +32,7 @@ 'fake-chat' => Bridge\FakeChat\FakeChatTransportFactory::class, 'firebase' => Bridge\Firebase\FirebaseTransportFactory::class, 'google-chat' => Bridge\GoogleChat\GoogleChatTransportFactory::class, + 'line-bot' => Bridge\LineBot\LineBotTransportFactory::class, 'line-notify' => Bridge\LineNotify\LineNotifyTransportFactory::class, 'linked-in' => Bridge\LinkedIn\LinkedInTransportFactory::class, 'mastodon' => Bridge\Mastodon\MastodonTransportFactory::class, diff --git a/src/Symfony/Component/Notifier/Bridge/LineBot/.gitattributes b/src/Symfony/Component/Notifier/Bridge/LineBot/.gitattributes new file mode 100644 index 0000000000000..14c3c35940427 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/LineBot/.gitattributes @@ -0,0 +1,3 @@ +/Tests export-ignore +/phpunit.xml.dist export-ignore +/.git* export-ignore diff --git a/src/Symfony/Component/Notifier/Bridge/LineBot/.github/PULL_REQUEST_TEMPLATE.md b/src/Symfony/Component/Notifier/Bridge/LineBot/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000000..4689c4dad430e --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/LineBot/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,8 @@ +Please do not submit any Pull Requests here. They will be closed. +--- + +Please submit your PR here instead: +https://github.com/symfony/symfony + +This repository is what we call a "subtree split": a read-only subset of that main repository. +We're looking forward to your PR there! diff --git a/src/Symfony/Component/Notifier/Bridge/LineBot/.github/workflows/close-pull-request.yml b/src/Symfony/Component/Notifier/Bridge/LineBot/.github/workflows/close-pull-request.yml new file mode 100644 index 0000000000000..e55b47817e69a --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/LineBot/.github/workflows/close-pull-request.yml @@ -0,0 +1,20 @@ +name: Close Pull Request + +on: + pull_request_target: + types: [opened] + +jobs: + run: + runs-on: ubuntu-latest + steps: + - uses: superbrothers/close-pull-request@v3 + with: + comment: | + Thanks for your Pull Request! We love contributions. + + However, you should instead open your PR on the main repository: + https://github.com/symfony/symfony + + This repository is what we call a "subtree split": a read-only subset of that main repository. + We're looking forward to your PR there! diff --git a/src/Symfony/Component/Notifier/Bridge/LineBot/.gitignore b/src/Symfony/Component/Notifier/Bridge/LineBot/.gitignore new file mode 100644 index 0000000000000..c49a5d8df5c65 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/LineBot/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/src/Symfony/Component/Notifier/Bridge/LineBot/CHANGELOG.md b/src/Symfony/Component/Notifier/Bridge/LineBot/CHANGELOG.md new file mode 100644 index 0000000000000..b9a197008ad62 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/LineBot/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +7.2 +--- + + * Add LINE Bot bridge diff --git a/src/Symfony/Component/Notifier/Bridge/LineBot/LICENSE b/src/Symfony/Component/Notifier/Bridge/LineBot/LICENSE new file mode 100644 index 0000000000000..e374a5c8339d3 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/LineBot/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2024-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/Symfony/Component/Notifier/Bridge/LineBot/LineBotTransport.php b/src/Symfony/Component/Notifier/Bridge/LineBot/LineBotTransport.php new file mode 100644 index 0000000000000..0ceabb56a3967 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/LineBot/LineBotTransport.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\LineBot; + +use Symfony\Component\Notifier\Exception\TransportException; +use Symfony\Component\Notifier\Exception\UnsupportedMessageTypeException; +use Symfony\Component\Notifier\Message\ChatMessage; +use Symfony\Component\Notifier\Message\MessageInterface; +use Symfony\Component\Notifier\Message\SentMessage; +use Symfony\Component\Notifier\Transport\AbstractTransport; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +/** + * @author Yi-Jyun Pan + */ +final class LineBotTransport extends AbstractTransport +{ + protected const HOST = 'api.line.me'; + + public function __construct( + #[\SensitiveParameter] private readonly string $accessToken, + private readonly string $receiver, + ?HttpClientInterface $client = null, + ?EventDispatcherInterface $dispatcher = null, + ) { + parent::__construct($client, $dispatcher); + } + + protected function doSend(MessageInterface $message): SentMessage + { + if (!$message instanceof ChatMessage) { + throw new UnsupportedMessageTypeException(__CLASS__, ChatMessage::class, $message); + } + + $response = $this->client->request( + 'POST', + \sprintf('https://%s/v2/bot/message/push', $this->getEndpoint()), + [ + 'auth_bearer' => $this->accessToken, + 'json' => [ + 'to' => $this->receiver, + 'messages' => [ + [ + 'type' => 'text', + 'text' => $message->getSubject(), + ], + ], + ], + ], + ); + + try { + $statusCode = $response->getStatusCode(); + } catch (\Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface $e) { + throw new TransportException('Could not reach the remote LINE server.', $response, 0, $e); + } + + if (200 !== $statusCode) { + $originalContent = $message->getSubject(); + + $result = $response->toArray(false) ?: ['message' => '']; + if (!isset($result['message']) || !\is_string($result['message'])) { + $result['message'] = ''; + } + + throw new TransportException(\sprintf('Unable to post the LINE message: "%s" (%d: "%s").', $originalContent, $statusCode, trim($result['message'])), $response); + } + + return new SentMessage($message, (string) $this); + } + + public function supports(MessageInterface $message): bool + { + return $message instanceof ChatMessage; + } + + public function __toString(): string + { + return \sprintf('linebot://%s?receiver=%s', $this->getEndpoint(), $this->receiver); + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/LineBot/LineBotTransportFactory.php b/src/Symfony/Component/Notifier/Bridge/LineBot/LineBotTransportFactory.php new file mode 100644 index 0000000000000..70a5909f32893 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/LineBot/LineBotTransportFactory.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\LineBot; + +use Symfony\Component\Notifier\Exception\InvalidArgumentException; +use Symfony\Component\Notifier\Exception\UnsupportedSchemeException; +use Symfony\Component\Notifier\Transport\AbstractTransportFactory; +use Symfony\Component\Notifier\Transport\Dsn; + +/** + * @author Yi-Jyun Pan + */ +final class LineBotTransportFactory extends AbstractTransportFactory +{ + private const SCHEME = 'linebot'; + + protected function getSupportedSchemes(): array + { + return [self::SCHEME]; + } + + public function create(Dsn $dsn): LineBotTransport + { + if (self::SCHEME !== $dsn->getScheme()) { + throw new UnsupportedSchemeException($dsn, self::SCHEME, $this->getSupportedSchemes()); + } + + $accessToken = $this->getUser($dsn); + $receiver = $dsn->getRequiredOption('receiver'); + if (!\is_string($receiver)) { + throw new InvalidArgumentException('The "receiver" option must be a string.'); + } + + $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); + $port = $dsn->getPort(); + + return (new LineBotTransport($accessToken, $receiver, $this->client, $this->dispatcher))->setHost($host)->setPort($port); + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/LineBot/README.md b/src/Symfony/Component/Notifier/Bridge/LineBot/README.md new file mode 100644 index 0000000000000..143eade7551c7 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/LineBot/README.md @@ -0,0 +1,24 @@ +LINE Bot Bridge +=============== + +Provides [LINE Bot (Push Message)](https://developers.line.biz/en/reference/messaging-api/#send-push-message) integration for Symfony Notifier. + +DSN example +----------- + +``` +linebot://TOKEN@default?receiver=RECEIVER +``` + +where: + + - `TOKEN` should be encoded in URL format. + - `RECEIVER` can be retrieved from https://developers.line.biz/en/docs/messaging-api/getting-user-ids/#getting-user-ids. + +Resources +--------- + + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/src/Symfony/Component/Notifier/Bridge/LineBot/Tests/LineBotTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/LineBot/Tests/LineBotTransportFactoryTest.php new file mode 100644 index 0000000000000..b75968b7fbd48 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/LineBot/Tests/LineBotTransportFactoryTest.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\LineBot\Tests; + +use Symfony\Component\Notifier\Bridge\LineBot\LineBotTransportFactory; +use Symfony\Component\Notifier\Test\AbstractTransportFactoryTestCase; +use Symfony\Component\Notifier\Test\IncompleteDsnTestTrait; +use Symfony\Component\Notifier\Test\MissingRequiredOptionTestTrait; +use Symfony\Component\Notifier\Transport\Dsn; + +/** + * @author Yi-Jyun Pan + */ +final class LineBotTransportFactoryTest extends AbstractTransportFactoryTestCase +{ + use IncompleteDsnTestTrait; + use MissingRequiredOptionTestTrait; + + private const MOCK_TOKEN = 'eyJhbGciOiJIUzI1NiJ9.eyJSb2xlIjoiQWRtaW4iL+CJJc3N1ZXIiOiJJc3N1ZXIiLCJVc2VybmFtZSI6IkphdmFJblVzZSIsImV4cCI6MTcyODU1MjA3OSwiaW+F0IjoxNzI4NTUyMDc5fQ.SPKpGKwsXBay2uXDh7tATW20S2vZpw9qcmYjNp46Ir/AB/12345677='; + + public function createFactory(): LineBotTransportFactory + { + return new LineBotTransportFactory(); + } + + public static function supportsProvider(): iterable + { + yield [true, 'linebot://host?receiver=abc&token=token']; + yield [true, 'linebot://host']; + yield [false, 'somethingElse://host']; + } + + public static function createProvider(): iterable + { + $encodedToken = urlencode(self::MOCK_TOKEN); + + yield [ + 'linebot://api.line.me?receiver=test', + 'linebot://'.$encodedToken.'@default?receiver=test', + ]; + } + + public static function incompleteDsnProvider(): iterable + { + yield ['linebot://host.test?receiver=xxx', 'User is not set.']; + } + + public static function missingRequiredOptionProvider(): iterable + { + yield ['linebot://token@host', 'receiver']; + } + + public static function unsupportedSchemeProvider(): iterable + { + yield ['somethingElse://token@host']; + yield ['somethingElse://token@host?receiver=abc&token=token']; + } + + public function testDsnToken() + { + $encodedToken = urlencode(self::MOCK_TOKEN); + + $uri = "linebot://$encodedToken@default?receiver=test"; + $dsn = new Dsn($uri); + + $this->assertSame(self::MOCK_TOKEN, $dsn->getUser()); + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/LineBot/Tests/LineBotTransportTest.php b/src/Symfony/Component/Notifier/Bridge/LineBot/Tests/LineBotTransportTest.php new file mode 100644 index 0000000000000..10d6cf559adef --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/LineBot/Tests/LineBotTransportTest.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\LineBot\Tests; + +use Symfony\Component\HttpClient\MockHttpClient; +use Symfony\Component\Notifier\Bridge\LineBot\LineBotTransport; +use Symfony\Component\Notifier\Exception\TransportException; +use Symfony\Component\Notifier\Message\ChatMessage; +use Symfony\Component\Notifier\Message\SmsMessage; +use Symfony\Component\Notifier\Test\TransportTestCase; +use Symfony\Component\Notifier\Tests\Transport\DummyMessage; +use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; + +/** + * @author Yi-Jyun Pan + */ +final class LineBotTransportTest extends TransportTestCase +{ + public static function createTransport(?HttpClientInterface $client = null): LineBotTransport + { + return (new LineBotTransport('testToken', 'testReceiver', $client ?? new MockHttpClient()))->setHost('host.test'); + } + + public static function toStringProvider(): iterable + { + yield ['linebot://host.test?receiver=testReceiver', self::createTransport()]; + } + + public static function supportedMessagesProvider(): iterable + { + yield [new ChatMessage('Hello!')]; + } + + public static function unsupportedMessagesProvider(): iterable + { + yield [new SmsMessage('0611223344', 'Hello!')]; + yield [new DummyMessage()]; + } + + public function testSendWithErrorResponseThrows() + { + $response = $this->createMock(ResponseInterface::class); + $response->expects($this->exactly(2)) + ->method('getStatusCode') + ->willReturn(400); + $response->expects($this->once()) + ->method('getContent') + ->willReturn(json_encode(['message' => 'testDescription'])); + + $client = new MockHttpClient(static fn (): ResponseInterface => $response); + + $transport = $this->createTransport($client); + + $this->expectException(TransportException::class); + $this->expectExceptionMessageMatches('/testMessage.+400: "testDescription"/'); + + $transport->send(new ChatMessage('testMessage')); + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/LineBot/composer.json b/src/Symfony/Component/Notifier/Bridge/LineBot/composer.json new file mode 100644 index 0000000000000..7e200237c7cfa --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/LineBot/composer.json @@ -0,0 +1,33 @@ +{ + "name": "symfony/line-bot-notifier", + "type": "symfony-notifier-bridge", + "description": "Provides LINE Bot (Push Message) integration for Symfony Notifier.", + "keywords": ["line", "bot", "chat"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Yi-Jyun Pan", + "email": "me@pan93.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=8.2", + "symfony/http-client": "^6.4|^7.0", + "symfony/notifier": "^7.2" + }, + "require-dev": { + "symfony/event-dispatcher": "^6.4|^7.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\LineBot\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/src/Symfony/Component/Notifier/Bridge/LineBot/phpunit.xml.dist b/src/Symfony/Component/Notifier/Bridge/LineBot/phpunit.xml.dist new file mode 100644 index 0000000000000..85f1320118f8c --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/LineBot/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php b/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php index 99915ab8f36db..37ac9fdcec834 100644 --- a/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php +++ b/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php @@ -124,6 +124,10 @@ class UnsupportedSchemeException extends LogicException 'class' => Bridge\LightSms\LightSmsTransportFactory::class, 'package' => 'symfony/light-sms-notifier', ], + 'linebot' => [ + 'class' => Bridge\LineBot\LineBotTransportFactory::class, + 'package' => 'symfony/line-bot-notifier', + ], 'linenotify' => [ 'class' => Bridge\LineNotify\LineNotifyTransportFactory::class, 'package' => 'symfony/line-notify-notifier', diff --git a/src/Symfony/Component/Notifier/Tests/Exception/UnsupportedSchemeExceptionTest.php b/src/Symfony/Component/Notifier/Tests/Exception/UnsupportedSchemeExceptionTest.php index 314bc34f78810..d3d3676655602 100644 --- a/src/Symfony/Component/Notifier/Tests/Exception/UnsupportedSchemeExceptionTest.php +++ b/src/Symfony/Component/Notifier/Tests/Exception/UnsupportedSchemeExceptionTest.php @@ -52,6 +52,7 @@ public static function setUpBeforeClass(): void Bridge\Isendpro\IsendproTransportFactory::class => false, Bridge\KazInfoTeh\KazInfoTehTransportFactory::class => false, Bridge\LightSms\LightSmsTransportFactory::class => false, + Bridge\LineBot\LineBotTransportFactory::class => false, Bridge\LineNotify\LineNotifyTransportFactory::class => false, Bridge\LinkedIn\LinkedInTransportFactory::class => false, Bridge\Lox24\Lox24TransportFactory::class => false, @@ -147,6 +148,7 @@ public static function messageWhereSchemeIsPartOfSchemeToPackageMapProvider(): \ yield ['isendpro', 'symfony/isendpro-notifier']; yield ['kaz-info-teh', 'symfony/kaz-info-teh-notifier']; yield ['lightsms', 'symfony/light-sms-notifier']; + yield ['linebot', 'symfony/line-bot-notifier']; yield ['linenotify', 'symfony/line-notify-notifier']; yield ['linkedin', 'symfony/linked-in-notifier']; yield ['lox24', 'symfony/lox24-notifier'];