diff --git a/lib/Command/Bot/ListBots.php b/lib/Command/Bot/ListBots.php
index 8de65fd947a..9a607f30a74 100644
--- a/lib/Command/Bot/ListBots.php
+++ b/lib/Command/Bot/ListBots.php
@@ -13,6 +13,9 @@
use OCA\Talk\Model\BotConversation;
use OCA\Talk\Model\BotConversationMapper;
use OCA\Talk\Model\BotServerMapper;
+use OCA\Talk\Service\BotService;
+use OCP\App\IAppManager;
+use OCP\AppFramework\Utility\ITimeFactory;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@@ -21,6 +24,9 @@ class ListBots extends Base {
public function __construct(
private BotConversationMapper $botConversationMapper,
private BotServerMapper $botServerMapper,
+ private BotService $botService,
+ private IAppManager $appManager,
+ private ITimeFactory $timeFactory,
) {
parent::__construct();
}
@@ -58,6 +64,21 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$botData = $bot->jsonSerialize();
$botData['features'] = Bot::featureFlagsToLabels($botData['features']);
+ if (!$this->botService->isAppForBotEnabled($bot)) {
+ $botData['state'] = Bot::STATE_UNAVAILABLE;
+ if ($input->getOption('output') === 'plain') {
+ $botData['error_count'] = '' . 1 . '';
+ } else {
+ $botData['error_count'] = 1;
+ }
+ $botData['last_error_date'] = $this->timeFactory->getTime();
+ if ($input->getOption('output') === 'plain') {
+ $botData['last_error_message'] = 'App disabled';
+ } else {
+ $botData['last_error_message'] = 'App disabled';
+ }
+ }
+
if (!$output->isVerbose()) {
unset($botData['url']);
unset($botData['url_hash']);
diff --git a/lib/Command/Bot/Setup.php b/lib/Command/Bot/Setup.php
index b27401aa5c2..2aaaa20487a 100644
--- a/lib/Command/Bot/Setup.php
+++ b/lib/Command/Bot/Setup.php
@@ -16,6 +16,7 @@
use OCA\Talk\Model\BotConversation;
use OCA\Talk\Model\BotConversationMapper;
use OCA\Talk\Model\BotServerMapper;
+use OCA\Talk\Service\BotService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\DB\Exception;
use OCP\EventDispatcher\IEventDispatcher;
@@ -28,6 +29,7 @@ public function __construct(
private Manager $roomManager,
private BotServerMapper $botServerMapper,
private BotConversationMapper $botConversationMapper,
+ private BotService $botService,
private IEventDispatcher $dispatcher,
) {
parent::__construct();
@@ -63,6 +65,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return 1;
}
+ if (!$this->botService->isAppForBotEnabled($botServer)) {
+ $output->writeln('Bot app is disabled: ' . $botServer->getUrl() . '');
+ return 1;
+ }
+
$returnCode = 0;
foreach ($tokens as $token) {
try {
diff --git a/lib/Controller/BotController.php b/lib/Controller/BotController.php
index b32828cd5e1..47d650511e3 100644
--- a/lib/Controller/BotController.php
+++ b/lib/Controller/BotController.php
@@ -45,6 +45,7 @@
use OCP\Comments\MessageTooLongException;
use OCP\Comments\NotFoundException;
use OCP\EventDispatcher\IEventDispatcher;
+use OCP\IL10N;
use OCP\IRequest;
use Psr\Log\LoggerInterface;
@@ -66,6 +67,7 @@ public function __construct(
protected Manager $manager,
protected ReactionManager $reactionManager,
protected ThreadService $threadService,
+ protected IL10N $l,
protected LoggerInterface $logger,
private IEventDispatcher $dispatcher,
) {
@@ -346,6 +348,14 @@ public function adminListBots(): DataResponse {
foreach ($bots as $bot) {
$botData = $bot->jsonSerialize();
unset($botData['secret']);
+
+ if (!$this->botService->isAppForBotEnabled($bot)) {
+ $botData['state'] = Bot::STATE_UNAVAILABLE;
+ $botData['error_count'] = 1;
+ $botData['last_error_date'] = $this->timeFactory->getTime();
+ $botData['last_error_message'] = $this->l->t('App disabled');
+ }
+
$data[] = $botData;
}
@@ -374,6 +384,15 @@ public function listBots(): DataResponse {
$bots = $this->botServerMapper->getAllBots();
foreach ($bots as $bot) {
$botData = $this->formatBot($bot, in_array($bot->getId(), $alreadyInstalled, true));
+
+ if (!$this->botService->isAppForBotEnabled($bot)) {
+ if ($botData['state'] !== Bot::STATE_DISABLED) {
+ $botData['state'] = Bot::STATE_UNAVAILABLE;
+ } else {
+ continue;
+ }
+ }
+
if ($botData !== null) {
$data[] = $botData;
}
@@ -414,7 +433,7 @@ public function enableBot(int $botId): DataResponse {
], Http::STATUS_BAD_REQUEST);
}
- if ($bot->getState() !== Bot::STATE_ENABLED) {
+ if ($bot->getState() !== Bot::STATE_ENABLED || !$this->botService->isAppForBotEnabled($bot)) {
return new DataResponse([
'error' => 'bot',
], Http::STATUS_BAD_REQUEST);
diff --git a/lib/Model/Bot.php b/lib/Model/Bot.php
index ef390bde1d2..f1a0f53af3a 100644
--- a/lib/Model/Bot.php
+++ b/lib/Model/Bot.php
@@ -13,6 +13,7 @@ class Bot {
public const STATE_DISABLED = 0;
public const STATE_ENABLED = 1;
public const STATE_NO_SETUP = 2;
+ public const STATE_UNAVAILABLE = 3;
public const FEATURE_NONE = 0;
public const FEATURE_WEBHOOK = 1;
diff --git a/lib/Service/BotService.php b/lib/Service/BotService.php
index 4884a0c6d6f..716a436815c 100644
--- a/lib/Service/BotService.php
+++ b/lib/Service/BotService.php
@@ -27,6 +27,7 @@
use OCA\Talk\Model\BotServerMapper;
use OCA\Talk\Room;
use OCA\Talk\TalkSession;
+use OCP\App\IAppManager;
use OCP\AppFramework\Http;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Comments\IComment;
@@ -67,6 +68,7 @@ public function __construct(
protected LoggerInterface $logger,
protected ICertificateManager $certificateManager,
protected IEventDispatcher $dispatcher,
+ protected IAppManager $appManager,
) {
$this->activityPubHelper = new ActivityPubHelper();
}
@@ -503,4 +505,14 @@ public function validateBotParameters(string $name, string $secret, string $url,
throw new \InvalidArgumentException('The provided description is too long (max. 4000 chars)');
}
}
+
+ public function isAppForBotEnabled(BotServer $bot): bool {
+ if (!str_starts_with($bot->getUrl(), Bot::URL_APP_PREFIX)) {
+ return true;
+ }
+
+ $url = substr($bot->getUrl(), strlen(Bot::URL_APP_PREFIX));
+ [$appId] = explode('/', $url, 2);
+ return $this->appManager->isEnabledForAnyone($appId);
+ }
}
diff --git a/src/components/AdminSettings/BotsSettings.vue b/src/components/AdminSettings/BotsSettings.vue
index 59825634f43..0c4ebe33f62 100644
--- a/src/components/AdminSettings/BotsSettings.vue
+++ b/src/components/AdminSettings/BotsSettings.vue
@@ -150,6 +150,8 @@ export default {
return { state_icon_component: IconLockOutline, state_icon_label: t('spreed', 'Locked for moderators'), state_icon_color: 'var(--color-favorite)' }
case BOT.STATE.ENABLED:
return { state_icon_component: IconCheck, state_icon_label: t('spreed', 'Enabled'), state_icon_color: 'var(--color-border-success)' }
+ case BOT.STATE.UNAVAILABLE:
+ return { state_icon_component: IconCancel, state_icon_label: t('spreed', 'App disabled'), state_icon_color: 'var(--color-text-maxcontrast)' }
case BOT.STATE.DISABLED:
default:
return { state_icon_component: IconCancel, state_icon_label: t('spreed', 'Disabled'), state_icon_color: 'var(--color-border-error)' }
diff --git a/src/components/ConversationSettings/BotsSettings.vue b/src/components/ConversationSettings/BotsSettings.vue
index b8650fd466f..379afa0e61b 100644
--- a/src/components/ConversationSettings/BotsSettings.vue
+++ b/src/components/ConversationSettings/BotsSettings.vue
@@ -21,11 +21,17 @@
{{ bot.description ?? t('spreed', 'Description is not provided') }}
+
+
+
+
+ {{ t('spreed', 'The bot is not available anymore') }}
+
{{ toggleButtonTitle(bot) }}
@@ -38,6 +44,8 @@