I’m trying to get a simple custom app working that listens for when tasks are completed and does a couple things:
- logs the completion
- hits a custom server
/trackendpoint
I can enable it: I copy it into my apps/ dir and use occ app:enable task_tracker, but the handle() method isn’t called when I complete a task. I have some logging that never shows up, and the other server’s /track endpoint isn’t hit.
I’m using a 32.0.8 server, so I’m following the v32 developer manual.
composer.json
{
"name": "meonkeys/task_tracker",
"description": "Nextcloud app that tracks task completions, notifying external server.",
"license": "AGPL-3.0-or-later",
"require": {
"php": "^8.0",
"guzzlehttp/guzzle": "^7.0"
},
"autoload": {
"psr-4": {
"OCA\\TaskTracker\\": "lib/"
}
}
}
appinfo/info.xml
<?xml version="1.0"?>
<info xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
<id>task_tracker</id>
<name>Task Tracker</name>
<summary>Sends completed tasks to a Flask API</summary>
<version>1.0.0</version>
<description>Listens for Nextcloud Task completions, logs them, and pings a server.</description>
<licence>AGPL-3.0-or-later</licence>
<author mail="haircut@gmail.com" homepage="https://adammonsen.com">Adam Monsen</author>
<namespace>TaskTracker</namespace>
<category>tools</category>
<dependencies>
<nextcloud min-version="32"/>
</dependencies>
</info>
lib/AppInfo/Application.php
<?php
declare(strict_types=1);
namespace OCA\TaskTracker\AppInfo;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCA\Calendar\Event\CalendarObjectUpdatedEvent;
use OCA\TaskTracker\Listener\TaskCompletedListener;
class Application extends App implements IBootstrap {
public const APP_ID = 'task_tracker';
public function __construct(array $params = []) {
parent::__construct(self::APP_ID, $params);
}
public function register(IRegistrationContext $context): void {
$context->registerEventListener(
CalendarObjectUpdatedEvent::class,
TaskCompletedListener::class
);
}
public function boot(IBootContext $context): void {
// Nothing needed at boot time
}
}
lib/Listener/TaskCompletedListener.php
<?php
declare(strict_types=1);
namespace OCA\TaskTracker\Listener;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCA\Calendar\Event\CalendarObjectUpdatedEvent;
use GuzzleHttp\Client;
use Psr\Log\LoggerInterface;
use Sabre\VObject\Reader;
// see also: https://github.com/nextcloud/spreed/blob/main/lib/Listener/CalDavEventListener.php
class TaskCompletedListener implements IEventListener {
public function __construct(LoggerInterface $logger) {
$logger->info('Constructed. 😌');
}
public function handle(Event $event): void {
$this->logger->debug('😭 please just log something already');
$this->logger->info('handle');
if (!$event instanceof CalendarObjectUpdatedEvent) {
return;
}
$calendarData = $event->getCalendardata();
$vobj = Reader::read($calendarData);
$this->logger->info('got vobj');
// only look at tasks
if (isset($vobj->VTODO)) {
$this->logger->info('vobj is a task: [' . $vobj . ']');
$todo = $vobj->VTODO;
if (isset($todo->STATUS) && (string)$todo->STATUS === 'COMPLETED') {
$this->logger->info('sending to multi-tracker server');
$this->pingOtherServer($todo);
}
}
}
private function pingOtherServer($todo) {
$this->logger->info('in pingOtherServer');
$title = isset($todo->SUMMARY) ? (string)$todo->SUMMARY : 'Untitled Task';
$detail = isset($todo->DESCRIPTION) ? (string)$todo->DESCRIPTION : '';
try {
$this->logger->info('posting to multi-tracker server');
// actual URL redacted
$client->post('https://tracker.example.com/track', [
'json' => [
'title' => $title,
'detail' => $detail,
'src' => 'nextcloud',
'tags' => ['task']
],
'timeout' => 2
]);
} catch (\Exception $e) {
$this->logger->error('Task Tracker failed: ' . $e->getMessage());
}
}
}
Any ideas? I’m super rusty at PHP and I’m just cobbling this all together so I’m surely doing something naive.