Skip to content

Commit

Permalink
Merge pull request #34 from nextcloud/feat/noid/add-repair-command
Browse files Browse the repository at this point in the history
feat(occ): Add a command to repair the bot installation on the local …
  • Loading branch information
nickvergessen authored Dec 13, 2023
2 parents 771cb34 + 792d1dd commit 21c43dd
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 85 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# Changelog
All notable changes to this project will be documented in this file.

## 1.0.1 - 2023-12-13
### Added
- Added an OCC command to repair the bot

### Fixed
- Fixed a bug in the OCC command to delete a secret

## 1.0.0 - 2023-10-30
### Added
- Nextcloud 28 compatibility
Expand Down
3 changes: 2 additions & 1 deletion appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
- You can also post multiple tasks in a single message, just put each on its own line starting with a keyword
- At the end of the call, the bot will summarize it and list all the attendees as well as the tasks in a markdown chat message]]></description>

<version>1.0.0</version>
<version>1.0.1</version>
<licence>agpl</licence>

<author>Joas Schilling</author>
Expand Down Expand Up @@ -42,5 +42,6 @@
<command>OCA\CallSummaryBot\Command\CreateSecret</command>
<command>OCA\CallSummaryBot\Command\DeleteSecret</command>
<command>OCA\CallSummaryBot\Command\ListSecret</command>
<command>OCA\CallSummaryBot\Command\Repair</command>
</commands>
</info>
3 changes: 1 addition & 2 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"autoload-dev": {
"psr-4": {
"OCP\\": "vendor/nextcloud/ocp/OCP",
"OCA\\CallSummaryBot\\": "lib/"
}
},
Expand All @@ -27,7 +26,7 @@
"cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:fix": "php-cs-fixer fix",
"psalm": "psalm.phar --threads=1",
"psalm:update-baseline": "psalm.phar --threads=1 --update-baseline --set-baseline=tests/psalm-baseline.xml",
"psalm:update-baseline": "psalm.phar --threads=1 --update-baseline",
"psalm:clear": "psalm.phar --clear-cache && psalm.phar --clear-global-cache",
"psalm:fix": "psalm.phar --alter --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType",
"test:unit": "vendor/bin/phpunit --color -c tests/phpunit.xml"
Expand Down
2 changes: 1 addition & 1 deletion lib/Command/DeleteSecret.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ protected function configure(): void {
protected function execute(InputInterface $input, OutputInterface $output): int {
$this->config->deleteAppValue('call_summary_bot', 'secret_' . $input->getArgument('id'));

$output->writeln('Deleted secrect');
$output->writeln('Deleted secret');

return 0;
}
Expand Down
100 changes: 100 additions & 0 deletions lib/Command/Repair.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

declare(strict_types=1);
/**
* @copyright Copyright (c) 2023 Joas Schilling <[email protected]>
*
* @author Joas Schilling <[email protected]>
*
* @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\CallSummaryBot\Command;

use OC\Core\Command\Base;
use OCA\CallSummaryBot\Service\BotService;
use OCP\IConfig;
use OCP\IURLGenerator;
use OCP\Security\ISecureRandom;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;

class Repair extends Base {
public function __construct(
private IConfig $config,
private IURLGenerator $url,
private BotService $service,
private ISecureRandom $random,
) {
parent::__construct();
}

protected function configure(): void {
parent::configure();
$this
->setName('call-summary-bot:repair')
->setDescription('Removes previous secrets and connects to the local server only')
;
}

protected function execute(InputInterface $input, OutputInterface $output): int {
$backend = rtrim($this->url->getAbsoluteURL(''), '/') . '/';

if (!str_starts_with($backend, 'http://') && !str_starts_with($backend, 'https://')) {
$output->writeln('<error>Backend URL must start with http:// or https:// - Please use the full "overwrite.cli.url" value from the config of the Nextcloud server</error>');
return 1;
}

/** @var QuestionHelper $helper */
$helper = $this->getHelper('question');

$question = new ConfirmationQuestion('<comment>Is ' . $backend . ' the correct server URL?</comment> [y/N] ', false);
if (!$helper->ask($input, $output, $question)) {
return 1;
}

$keys = $this->config->getAppKeys('call_summary_bot');

$secrets = [];
foreach ($keys as $key) {
if (str_starts_with($key, 'secret_')) {
$secrets[$key] = json_decode($this->config->getAppValue('call_summary_bot', $key), true, 512, JSON_THROW_ON_ERROR);
}
}

$this->writeTableInOutputFormat($input, $output, $secrets);

$question = new ConfirmationQuestion('<comment>Are you sure these secrets shall be deleted?</comment> [y/N] ', false);
if (!$helper->ask($input, $output, $question)) {
$output->writeln('<comment>Aborted repairing</comment>');
return 1;
}

foreach ($secrets as $configKey => $secret) {
$this->service->uninstallBot($secret['secret'], $secret['backend']);
$this->config->deleteAppValue('call_summary_bot', $configKey);
$output->writeln('<info>Deleted bot for backend: ' . $secret['backend'] . '</info>');
}

$this->service->installbot($backend);
$output->writeln('<info>Installed bot for backend: ' . $backend . '</info>');

return 0;
}
}
52 changes: 3 additions & 49 deletions lib/Migration/InstallBot.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,16 @@

namespace OCA\CallSummaryBot\Migration;

use OCA\CallSummaryBot\Model\Bot;
use OCA\CallSummaryBot\Service\BotService;
use OCA\Talk\Events\BotInstallEvent;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IConfig;
use OCP\IURLGenerator;
use OCP\L10N\IFactory;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
use OCP\Security\ISecureRandom;

class InstallBot implements IRepairStep {
public function __construct(
protected IConfig $config,
protected IEventDispatcher $dispatcher,
protected ISecureRandom $random,
protected IURLGenerator $url,
protected IFactory $l10nFactory,
protected BotService $service,
) {
}

Expand All @@ -57,45 +50,6 @@ public function run(IOutput $output): void {
}

$backend = $this->url->getAbsoluteURL('');
$id = sha1($backend);

$secretData = $this->config->getAppValue('call_summary_bot', 'secret_' . $id);
if ($secretData) {
$secretArray = json_decode($secretData, true, 512, JSON_THROW_ON_ERROR);
$secret = $secretArray['secret'] ?? $this->random->generate(64, ISecureRandom::CHAR_HUMAN_READABLE);
} else {
$secret = $this->random->generate(64, ISecureRandom::CHAR_HUMAN_READABLE);
}

foreach (Bot::SUPPORTED_LANGUAGES as $lang) {
$this->installLanguage($secret, $lang);
}

$this->config->setAppValue('call_summary_bot', 'secret_' . $id, json_encode([
'id' => $id,
'secret' => $secret,
'backend' => $backend,
], JSON_THROW_ON_ERROR));
}

protected function installLanguage(string $secret, string $lang): void {
$libL10n = $this->l10nFactory->get('lib', $lang);
$langName = $libL10n->t('__language_name__');
if ($langName === '__language_name__') {
$langName = $lang === 'en' ? 'British English' : $lang;
}

$l = $this->l10nFactory->get('call_summary_bot', $lang);

$event = new BotInstallEvent(
$l->t('Call summary'),
$secret . str_replace('_', '', $lang),
$this->url->linkToOCSRouteAbsolute('call_summary_bot.Bot.receiveWebhook', ['lang' => $lang]),
$l->t('Call summary (%s)', $langName) . ' - ' . $l->t('The call summary bot posts an overview message after the call listing all participants and outlining tasks'),
);
try {
$this->dispatcher->dispatchTyped($event);
} catch (\Throwable $e) {
}
$this->service->installBot($backend);
}
}
34 changes: 3 additions & 31 deletions lib/Migration/UninstallBot.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,18 @@

namespace OCA\CallSummaryBot\Migration;

use OCA\CallSummaryBot\Model\Bot;
use OCA\CallSummaryBot\Service\BotService;
use OCA\Talk\Events\BotUninstallEvent;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IConfig;
use OCP\IURLGenerator;
use OCP\L10N\IFactory;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
use OCP\Security\ISecureRandom;

class UninstallBot implements IRepairStep {
public function __construct(
protected IConfig $config,
protected IEventDispatcher $dispatcher,
protected ISecureRandom $random,
protected IURLGenerator $url,
protected IFactory $l10nFactory,
protected BotService $service,
) {
}

Expand All @@ -63,31 +58,8 @@ public function run(IOutput $output): void {
if ($secretData) {
$secretArray = json_decode($secretData, true, 512, JSON_THROW_ON_ERROR);
if ($secretArray['secret']) {
foreach (Bot::SUPPORTED_LANGUAGES as $lang) {
$this->uninstallLanguage($secretArray['secret'], $lang);
}
$this->service->uninstallBot($secretArray['secret'], $backend);
}
}
}

protected function uninstallLanguage(string $secret, string $lang): void {
$event = new BotUninstallEvent(
$secret . str_replace('_', '', $lang),
$this->url->linkToOCSRouteAbsolute('call_summary_bot.Bot.receiveWebhook', ['lang' => $lang]),
);
try {
$this->dispatcher->dispatchTyped($event);
} catch (\Throwable $e) {
}

// Also remove legacy secret bots
$event = new BotUninstallEvent(
$secret,
$this->url->linkToOCSRouteAbsolute('call_summary_bot.Bot.receiveWebhook', ['lang' => $lang]),
);
try {
$this->dispatcher->dispatchTyped($event);
} catch (\Throwable $e) {
}
}
}
Loading

0 comments on commit 21c43dd

Please sign in to comment.