gnu-social/plugins/WebHooks/WebHooks.php
Hugo Sales 7027633ed5
[PLUGIN][WebHooks] Make request method configurable
This way, PUT can be used, which doesn't seem to be the standard, so isn't the default, but which makes sense to me, as it doesn't have a response, which we don't care about anyway
2022-03-24 00:51:00 +00:00

103 lines
4.2 KiB
PHP

<?php
declare(strict_types = 1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social 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.
//
// GNU social 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 GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
namespace Plugin\WebHooks;
use App\Core\DB\DB;
use App\Core\Event;
use App\Core\HTTPClient;
use App\Core\Log;
use App\Core\Modules\Plugin;
use App\Core\Queue\Queue;
use App\Core\Router\RouteLoader;
use App\Entity\Activity;
use App\Entity\Actor;
use App\Util\Exception\ServerException;
use Exception;
use Functional as F;
use Plugin\WebHooks\Controller as C;
use Plugin\WebHooks\Entity as E;
use Symfony\Component\HttpFoundation\Request;
class WebHooks extends Plugin
{
public const controller_route = 'webhook';
public function onAddRoute(RouteLoader $r)
{
$r->connect(self::controller_route, '/webhook-settings', C\WebHooks::class);
}
public function onPopulateSettingsTabs(Request $request, string $section, array &$tabs): bool
{
if ($section === 'others') {
$tabs[] = [
'title' => 'Web Hooks',
'desc' => 'Add hooks that run when an internal event occurs, allowing your third party resource to react',
'id' => 'settings-webhooks',
'controller' => C\WebHooks::setup(),
];
}
return Event::next;
}
public function onNewNotificationEnd(Actor $sender, Activity $activity, array $effective_targets, ?string $reason)
{
foreach ($effective_targets as $actor) {
$target = DB::findOneBy(E\WebHook::class, ['actor_id' => $actor->getId(), 'event' => 'notifications'], return_null: true)?->getTarget();
if (!\is_null($target)) {
Queue::enqueue(['notifications', $target, $actor, [$sender, $activity, $effective_targets, $reason]], queue: 'webhook');
}
}
return Event::next;
}
/**
* @param array<Actor $sender, Activity $activity, array $effective_targets, ?string $reason> $args
*/
public function onQueueWebhook(string $type, string $target, Actor $actor, array $args)
{
switch ($type) {
case 'notifications':
[$sender, $activity, $targets, $reason] = $args;
$data = [
'type' => 'notification',
'activity' => '%activity%',
'actor' => ['id' => $sender->getId(), 'nickname' => $sender->getNickname()],
'targets' => F\map(array_values($targets), fn (Actor $actor) => ['id' => $actor->getId(), 'nickname' => $actor->getNickname()]),
'reason' => $reason,
];
// toJson(Activity) is already JSON (hopefully that's obvious :') ), so replace it after converting the rest to JSON
$json = str_replace('"activity":"%activity%"', '"activity":' . \Plugin\ActivityPub\Util\Model\Activity::toJson($activity), json_encode($data));
Log::debug("WebHooks: POST {$target} on behalf of actor {$actor->getId()} ({$actor->getNickname()})", [$data, ['json' => $json]]);
try {
$method = Common::config('plugin_webhooks', 'method');
HTTPClient::{$method}($target, ['body' => $json, 'headers' => ['content-type' => 'application/json', 'user-agent' => 'GNU social']]);
} catch (Exception $e) {
Log::debug("WebHooks: Failed POST {$target} on behalf of actor {$actor->getId()} ({$actor->getNickname()})", [$e]);
}
return Event::stop;
default:
throw new ServerException("Webhook notification handler for event {$type} not implemented");
}
}
}