summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoas Schilling <coding@schilljs.com>2023-07-23 14:12:33 +0200
committerJoas Schilling <coding@schilljs.com>2023-08-07 11:35:24 +0200
commitae1fe40ac0378b1e98a0609699261ac36b583bbe (patch)
treee7f66330d99511134be1c04ff0a041218ff02df5
parent495e09b4e588b26274ca9ec687389f6addec1fdf (diff)
feat(bots): Add commands and events to install bots
Signed-off-by: Joas Schilling <coding@schilljs.com>
-rw-r--r--appinfo/info.xml6
-rw-r--r--lib/AppInfo/Application.php2
-rw-r--r--lib/Command/Bot/Install.php103
-rw-r--r--lib/Command/Bot/ListBots.php89
-rw-r--r--lib/Command/Bot/Remove.php68
-rw-r--r--lib/Command/Bot/Setup.php103
-rw-r--r--lib/Command/Bot/State.php83
-rw-r--r--lib/Command/Bot/Uninstall.php65
-rw-r--r--lib/Events/BotInstallEvent.php55
-rw-r--r--lib/Listener/BotListener.php25
-rw-r--r--lib/Model/Bot.php6
-rw-r--r--lib/Model/BotConversationMapper.php20
-rw-r--r--lib/Model/BotServer.php2
-rw-r--r--lib/Model/BotServerMapper.php35
-rw-r--r--lib/Service/BotService.php6
15 files changed, 665 insertions, 3 deletions
diff --git a/appinfo/info.xml b/appinfo/info.xml
index d99540c6c..bea4033df 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -81,6 +81,12 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m
</repair-steps>
<commands>
+ <command>OCA\Talk\Command\Bot\Install</command>
+ <command>OCA\Talk\Command\Bot\ListBots</command>
+ <command>OCA\Talk\Command\Bot\Remove</command>
+ <command>OCA\Talk\Command\Bot\State</command>
+ <command>OCA\Talk\Command\Bot\Setup</command>
+ <command>OCA\Talk\Command\Bot\Uninstall</command>
<command>OCA\Talk\Command\Command\Add</command>
<command>OCA\Talk\Command\Command\AddSamples</command>
<command>OCA\Talk\Command\Command\Delete</command>
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index 43c46441e..d462604d0 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -45,6 +45,7 @@ use OCA\Talk\Dashboard\TalkWidget;
use OCA\Talk\Deck\DeckPluginLoader;
use OCA\Talk\Events\AttendeesAddedEvent;
use OCA\Talk\Events\AttendeesRemovedEvent;
+use OCA\Talk\Events\BotInstallEvent;
use OCA\Talk\Events\RoomEvent;
use OCA\Talk\Events\SendCallNotificationEvent;
use OCA\Talk\Federation\CloudFederationProviderTalk;
@@ -125,6 +126,7 @@ class Application extends App implements IBootstrap {
$context->registerEventListener(AddContentSecurityPolicyEvent::class, CSPListener::class);
$context->registerEventListener(AddFeaturePolicyEvent::class, FeaturePolicyListener::class);
+ $context->registerEventListener(BotInstallEvent::class, BotListener::class);
$context->registerEventListener(GroupDeletedEvent::class, GroupDeletedListener::class);
$context->registerEventListener(GroupChangedEvent::class, DisplayNameListener::class);
$context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class);
diff --git a/lib/Command/Bot/Install.php b/lib/Command/Bot/Install.php
new file mode 100644
index 000000000..1f0ba5160
--- /dev/null
+++ b/lib/Command/Bot/Install.php
@@ -0,0 +1,103 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Talk\Command\Bot;
+
+use OC\Core\Command\Base;
+use OCA\Talk\Model\Bot;
+use OCA\Talk\Model\BotServer;
+use OCA\Talk\Model\BotServerMapper;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Install extends Base {
+ public function __construct(
+ private BotServerMapper $botServerMapper,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ parent::configure();
+ $this
+ ->setName('talk:bot:install')
+ ->setDescription('Install a new bot on the server')
+ ->addArgument(
+ 'name',
+ InputArgument::REQUIRED,
+ 'The name under which the messages will be posted'
+ )
+ ->addArgument(
+ 'secret',
+ InputArgument::REQUIRED,
+ 'Secret used to validate API calls'
+ )
+ ->addArgument(
+ 'url',
+ InputArgument::REQUIRED,
+ 'Webhook endpoint to post messages to'
+ )
+ ->addArgument(
+ 'description',
+ InputArgument::OPTIONAL,
+ 'Optional description shown in the admin settings'
+ )
+ ->addOption(
+ 'no-setup',
+ null,
+ InputOption::VALUE_NONE,
+ 'Prevent moderators from setting up the bot in a conversation'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $name = $input->getArgument('name');
+ $secret = $input->getArgument('secret');
+ $url = $input->getArgument('url');
+ $description = $input->getArgument('description');
+ $noSetup = $input->getOption('no-setup');
+
+ $bot = new BotServer();
+ $bot->setName($name);
+ $bot->setSecret($secret);
+ $bot->setUrl($url);
+ $bot->setUrlHash(sha1($url));
+ $bot->setDescription($description);
+ $bot->setState($noSetup ? Bot::STATE_NO_SETUP : Bot::STATE_ENABLED);
+ try {
+ $this->botServerMapper->insert($bot);
+ } catch (\Exception $e) {
+ $output->writeln('<error>' . get_class($e) . ': ' . $e->getMessage() . '</error>');
+ return 1;
+ }
+
+
+ $output->writeln('<info>Bot installed</info>');
+ return 0;
+ }
+}
diff --git a/lib/Command/Bot/ListBots.php b/lib/Command/Bot/ListBots.php
new file mode 100644
index 000000000..4f5251c93
--- /dev/null
+++ b/lib/Command/Bot/ListBots.php
@@ -0,0 +1,89 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Talk\Command\Bot;
+
+use OC\Core\Command\Base;
+use OCA\Talk\Model\BotConversation;
+use OCA\Talk\Model\BotConversationMapper;
+use OCA\Talk\Model\BotServerMapper;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class ListBots extends Base {
+ public function __construct(
+ private BotConversationMapper $botConversationMapper,
+ private BotServerMapper $botServerMapper,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ parent::configure();
+ $this
+ ->setName('talk:bot:list')
+ ->setDescription('List all installed bots of the server or a conversation')
+ ->addArgument(
+ 'token',
+ InputArgument::OPTIONAL,
+ 'Conversation token to limit the bot list for'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $bots = $this->botServerMapper->getAllBots();
+ $token = $input->getArgument('token');
+
+ if ($token) {
+ $botIds = array_map(static function (BotConversation $bot): int {
+ return $bot->getBotId();
+ }, $this->botConversationMapper->findForToken($token));
+ }
+
+ $data = [];
+ foreach ($bots as $bot) {
+ if ($token && !in_array($bot->getId(), $botIds, true)) {
+ continue;
+ }
+
+ $botData = $bot->jsonSerialize();
+
+ if (!$output->isVerbose()) {
+ unset($botData['url']);
+ unset($botData['url_hash']);
+ unset($botData['secret']);
+ unset($botData['last_error_date']);
+ unset($botData['last_error_message']);
+ }
+
+ $data[] = $botData;
+ }
+
+ $this->writeTableInOutputFormat($input, $output, $data);
+ return 0;
+ }
+}
diff --git a/lib/Command/Bot/Remove.php b/lib/Command/Bot/Remove.php
new file mode 100644
index 000000000..fa6378ea9
--- /dev/null
+++ b/lib/Command/Bot/Remove.php
@@ -0,0 +1,68 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Talk\Command\Bot;
+
+use OC\Core\Command\Base;
+use OCA\Talk\Model\BotConversationMapper;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Remove extends Base {
+ public function __construct(
+ private BotConversationMapper $botConversationMapper,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ parent::configure();
+ $this
+ ->setName('talk:bot:remove')
+ ->setDescription('Remove a bot from a conversation')
+ ->addArgument(
+ 'bot-id',
+ InputArgument::REQUIRED,
+ 'The ID of the bot to remove in a conversation'
+ )
+ ->addArgument(
+ 'token',
+ InputArgument::IS_ARRAY,
+ 'Conversation tokens to remove bot up for'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $botId = (int) $input->getArgument('bot-id');
+ $tokens = $input->getArgument('token');
+
+ $this->botConversationMapper->deleteByBotIdAndTokens($botId, $tokens);
+
+ $output->writeln('<info>Remove bot from given conversations</info>');
+ return 0;
+ }
+}
diff --git a/lib/Command/Bot/Setup.php b/lib/Command/Bot/Setup.php
new file mode 100644
index 000000000..00562e965
--- /dev/null
+++ b/lib/Command/Bot/Setup.php
@@ -0,0 +1,103 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Talk\Command\Bot;
+
+use OC\Core\Command\Base;
+use OCA\Talk\Exceptions\RoomNotFoundException;
+use OCA\Talk\Manager;
+use OCA\Talk\Model\Bot;
+use OCA\Talk\Model\BotConversation;
+use OCA\Talk\Model\BotConversationMapper;
+use OCA\Talk\Model\BotServerMapper;
+use OCP\AppFramework\Db\DoesNotExistException;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Setup extends Base {
+ public function __construct(
+ private Manager $roomManager,
+ private BotServerMapper $botServerMapper,
+ private BotConversationMapper $botConversationMapper,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ parent::configure();
+ $this
+ ->setName('talk:bot:setup')
+ ->setDescription('Add a bot to a conversation')
+ ->addArgument(
+ 'bot-id',
+ InputArgument::REQUIRED,
+ 'The ID of the bot to set up in a conversation'
+ )
+ ->addArgument(
+ 'token',
+ InputArgument::IS_ARRAY,
+ 'Conversation tokens to set the bot up for'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $botId = (int) $input->getArgument('bot-id');
+ $tokens = $input->getArgument('token');
+
+ try {
+ $this->botServerMapper->findById($botId);
+ } catch (DoesNotExistException) {
+ $output->writeln('<error>Bot could not be found by id: ' . $botId . '</error>');
+ return 1;
+ }
+
+ $returnCode = 0;
+ foreach ($tokens as $token) {
+ try {
+ $this->roomManager->getRoomByToken($token);
+ } catch (RoomNotFoundException) {
+ $output->writeln('<error>Conversation could not be found by token: ' . $token . '</error>');
+ return 1;
+ }
+
+ $bot = new BotConversation();
+ $bot->setBotId($botId);
+ $bot->setToken($token);
+ $bot->setState(Bot::STATE_ENABLED);
+
+ try {
+ $this->botConversationMapper->insert($bot);
+ $output->writeln('<info>Successfully set up for conversation ' . $token . '</info>');
+ } catch (\Exception $e) {
+ $output->writeln('<error>' . get_class($e) . ': ' . $e->getMessage() . '</error>');
+ $returnCode = 3;
+ }
+ }
+
+ return $returnCode;
+ }
+}
diff --git a/lib/Command/Bot/State.php b/lib/Command/Bot/State.php
new file mode 100644
index 000000000..9c059a3ca
--- /dev/null
+++ b/lib/Command/Bot/State.php
@@ -0,0 +1,83 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Talk\Command\Bot;
+
+use OC\Core\Command\Base;
+use OCA\Talk\Model\Bot;
+use OCA\Talk\Model\BotServerMapper;
+use OCP\AppFramework\Db\DoesNotExistException;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class State extends Base {
+ public function __construct(
+ private BotServerMapper $botServerMapper,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ parent::configure();
+ $this
+ ->setName('talk:bot:state')
+ ->setDescription('List all installed bots of the server or a conversation')
+ ->addArgument(
+ 'bot-id',
+ InputArgument::REQUIRED,
+ 'Bot ID to change the state for'
+ )
+ ->addArgument(
+ 'state',
+ InputArgument::REQUIRED,
+ 'New state for the bot (0 = disabled, 1 = enabled, 2 = no setup via GUI)'
+ )
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ $botId = (int) $input->getArgument('bot-id');
+ $state = (int) $input->getArgument('state');
+
+ if (!in_array($state, [Bot::STATE_DISABLED, Bot::STATE_ENABLED, Bot::STATE_NO_SETUP], true)) {
+ $output->writeln('<error>Provided state is invalid</error>');
+ return 1;
+ }
+
+ try {
+ $bot = $this->botServerMapper->findById($botId);
+ } catch (DoesNotExistException) {
+ $output->writeln('<error>Bot could not be found by id: ' . $botId . '</error>');
+ return 1;
+ }
+
+ $bot->setState($state);
+ $this->botServerMapper->update($bot);