Begin work on custom code plugins support.
This commit is contained in:
parent
dece3e9e5a
commit
33210b9450
|
@ -32,3 +32,8 @@ web/static/yarn-error\.log
|
|||
# Docker files
|
||||
/docker-compose.yml
|
||||
/*.tar.gz
|
||||
|
||||
# Plugins
|
||||
/plugins/*
|
||||
/plugins/**/*
|
||||
!/plugins/.gitkeep
|
||||
|
|
|
@ -58,6 +58,10 @@ ini_set('session.use_strict_mode', 1);
|
|||
$autoloader = require(APP_INCLUDE_VENDOR . '/autoload.php');
|
||||
$autoloader->setPsr4('Proxy\\', APP_INCLUDE_TEMP . '/proxies');
|
||||
|
||||
// Initialize plugins
|
||||
$plugins = new \App\Plugins(APP_INCLUDE_ROOT.'/plugins');
|
||||
$plugins->registerAutoloaders($autoloader);
|
||||
|
||||
// Set up DI container.
|
||||
$di = new \Slim\Container([
|
||||
'settings' => [
|
||||
|
@ -69,8 +73,12 @@ $di = new \Slim\Container([
|
|||
]
|
||||
]);
|
||||
|
||||
$di[\App\Plugins::class] = $plugins;
|
||||
|
||||
// Define services.
|
||||
$settings = require(dirname(__DIR__).'/config/settings.php');
|
||||
call_user_func(include(dirname(__DIR__).'/config/services.php'), $di, $settings);
|
||||
|
||||
$plugins->registerServices($di, $settings);
|
||||
|
||||
return $di;
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
"php-http/guzzle6-adapter": "^1.1",
|
||||
"slim/slim": "^3.0",
|
||||
"supervisorphp/supervisor": "^3.0",
|
||||
"symfony/event-dispatcher": "^4.1",
|
||||
"symfony/finder": "^4.1",
|
||||
"zendframework/zend-config": "^3.1.0",
|
||||
"zendframework/zend-paginator": "^2.7"
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "594ac3f2423a143d5621fb2cffbd11fa",
|
||||
"content-hash": "1d77d7c553cb7dd8c3f52c09642fcf36",
|
||||
"packages": [
|
||||
{
|
||||
"name": "azuracast/azuraforms",
|
||||
|
@ -3419,6 +3419,69 @@
|
|||
"homepage": "https://symfony.com",
|
||||
"time": "2018-07-26T11:24:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
"version": "v4.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||
"reference": "bfb30c2ad377615a463ebbc875eba64a99f6aa3e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/bfb30c2ad377615a463ebbc875eba64a99f6aa3e",
|
||||
"reference": "bfb30c2ad377615a463ebbc875eba64a99f6aa3e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1.3"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/dependency-injection": "<3.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/log": "~1.0",
|
||||
"symfony/config": "~3.4|~4.0",
|
||||
"symfony/dependency-injection": "~3.4|~4.0",
|
||||
"symfony/expression-language": "~3.4|~4.0",
|
||||
"symfony/stopwatch": "~3.4|~4.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/dependency-injection": "",
|
||||
"symfony/http-kernel": ""
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\EventDispatcher\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony EventDispatcher Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2018-07-26T09:10:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v4.1.4",
|
||||
|
@ -5956,69 +6019,6 @@
|
|||
"homepage": "https://symfony.com",
|
||||
"time": "2018-07-26T11:00:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
"version": "v4.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||
"reference": "bfb30c2ad377615a463ebbc875eba64a99f6aa3e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/bfb30c2ad377615a463ebbc875eba64a99f6aa3e",
|
||||
"reference": "bfb30c2ad377615a463ebbc875eba64a99f6aa3e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1.3"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/dependency-injection": "<3.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/log": "~1.0",
|
||||
"symfony/config": "~3.4|~4.0",
|
||||
"symfony/dependency-injection": "~3.4|~4.0",
|
||||
"symfony/expression-language": "~3.4|~4.0",
|
||||
"symfony/stopwatch": "~3.4|~4.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/dependency-injection": "",
|
||||
"symfony/http-kernel": ""
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\EventDispatcher\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony EventDispatcher Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2018-07-26T09:10:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.9.0",
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
return function (\Symfony\Component\EventDispatcher\EventDispatcher $dispatcher, \Slim\Container $di, $settings) {
|
||||
|
||||
$dispatcher->addSubscriber(new \App\EventHandler\DefaultRoutes(__DIR__.'/routes.php'));
|
||||
$dispatcher->addSubscriber(new \App\EventHandler\DefaultView($di));
|
||||
$dispatcher->addSubscriber(new \App\EventHandler\DefaultNowPlaying());
|
||||
|
||||
$dispatcher->addListener(\App\Event\SendWebhooks::NAME, function(\App\Event\SendWebhooks $event) use ($di) {
|
||||
/** @var \App\Webhook\Dispatcher $webhook_dispatcher */
|
||||
$webhook_dispatcher = $di[\App\Webhook\Dispatcher::class];
|
||||
|
||||
$webhook_dispatcher->dispatch($event);
|
||||
});
|
||||
};
|
|
@ -233,46 +233,9 @@ return function (\Slim\Container $di, $settings) {
|
|||
$view = new App\View(dirname(__DIR__) . '/resources/templates');
|
||||
$view->setFileExtension('phtml');
|
||||
|
||||
$view->registerFunction('service', function($service) use ($di) {
|
||||
return $di->get($service);
|
||||
});
|
||||
|
||||
$view->registerFunction('escapeJs', function($string) {
|
||||
return json_encode($string);
|
||||
});
|
||||
|
||||
$view->registerFunction('mailto', function ($address, $link_text = null) {
|
||||
$address = substr(chunk_split(bin2hex(" $address"), 2, ";&#x"), 3, -3);
|
||||
$link_text = $link_text ?? $address;
|
||||
|
||||
return '<a href="mailto:' . $address . '">' . $link_text . '</a>';
|
||||
});
|
||||
|
||||
$view->registerFunction('pluralize', function ($word, $num = 0) {
|
||||
if ((int)$num === 1) {
|
||||
return $word;
|
||||
} else {
|
||||
return \Doctrine\Common\Inflector\Inflector::pluralize($word);
|
||||
}
|
||||
});
|
||||
|
||||
$view->registerFunction('truncate', function ($text, $length = 80) {
|
||||
return \App\Utilities::truncate_text($text, $length);
|
||||
});
|
||||
|
||||
/** @var \App\Session $session */
|
||||
$session = $di[\App\Session::class];
|
||||
|
||||
$view->addData([
|
||||
'app_settings' => $di['app_settings'],
|
||||
'router' => $di['router'],
|
||||
'request' => $di['request'],
|
||||
'assets' => $di[\App\Assets::class],
|
||||
'auth' => $di[\App\Auth::class],
|
||||
'acl' => $di[\App\Acl::class],
|
||||
'flash' => $session->getFlash(),
|
||||
'customization' => $di[\App\Customization::class],
|
||||
]);
|
||||
/** @var \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher */
|
||||
$dispatcher = $di[\Symfony\Component\EventDispatcher\EventDispatcher::class];
|
||||
$dispatcher->dispatch(\App\Event\BuildView::NAME, new \App\Event\BuildView($view));
|
||||
|
||||
return $view;
|
||||
});
|
||||
|
@ -337,6 +300,21 @@ return function (\Slim\Container $di, $settings) {
|
|||
]);
|
||||
};
|
||||
|
||||
$di[\Symfony\Component\EventDispatcher\EventDispatcher::class] = function($di) use ($settings) {
|
||||
$dispatcher = new \Symfony\Component\EventDispatcher\EventDispatcher();
|
||||
|
||||
// Register application default events.
|
||||
call_user_func(include(__DIR__.'/events.php'), $dispatcher, $di, $settings);
|
||||
|
||||
/** @var \App\Plugins $plugins */
|
||||
$plugins = $di[\App\Plugins::class];
|
||||
|
||||
// Register plugin-provided events.
|
||||
$plugins->registerEvents($dispatcher, $di, $settings);
|
||||
|
||||
return $dispatcher;
|
||||
};
|
||||
|
||||
//
|
||||
// AzuraCast-specific dependencies
|
||||
//
|
||||
|
@ -401,23 +379,9 @@ return function (\Slim\Container $di, $settings) {
|
|||
|
||||
$app = new \Slim\App($di);
|
||||
|
||||
// Get the current user entity object and assign it into the request if it exists.
|
||||
$app->add(\App\Middleware\GetCurrentUser::class);
|
||||
|
||||
// Inject the application router into the request object.
|
||||
$app->add(\App\Middleware\EnableRouter::class);
|
||||
|
||||
// Inject the session manager into the request object.
|
||||
$app->add(\App\Middleware\EnableSession::class);
|
||||
|
||||
// Check HTTPS setting and enforce Content Security Policy accordingly.
|
||||
$app->add(\App\Middleware\EnforceSecurity::class);
|
||||
|
||||
// Remove trailing slash from all URLs when routing.
|
||||
$app->add(\App\Middleware\RemoveSlashes::class);
|
||||
|
||||
// Load routes
|
||||
call_user_func(include(__DIR__.'/routes.php'), $app);
|
||||
/** @var \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher */
|
||||
$dispatcher = $di[\Symfony\Component\EventDispatcher\EventDispatcher::class];
|
||||
$dispatcher->dispatch(\App\Event\BuildRoutes::NAME, new \App\Event\BuildRoutes($app));
|
||||
|
||||
return $app;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
namespace App\Event;
|
||||
|
||||
use Slim\App;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
class BuildRoutes extends Event
|
||||
{
|
||||
const NAME = 'build-routes';
|
||||
|
||||
protected $app;
|
||||
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
public function getApp(): App
|
||||
{
|
||||
return $this->app;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
namespace App\Event;
|
||||
|
||||
use App\View;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
class BuildView extends Event
|
||||
{
|
||||
const NAME = 'build-view';
|
||||
|
||||
protected $view;
|
||||
|
||||
public function __construct(View $view)
|
||||
{
|
||||
$this->view = $view;
|
||||
}
|
||||
|
||||
public function getView(): View
|
||||
{
|
||||
return $this->view;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
namespace App\Event;
|
||||
|
||||
use App\Entity\Station;
|
||||
use App\Radio\Frontend\FrontendAbstract;
|
||||
use App\Radio\Remote\RemoteAbstract;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
class GenerateRawNowPlaying extends Event
|
||||
{
|
||||
const NAME = 'nowplaying-generate-raw';
|
||||
|
||||
/** @var Station */
|
||||
protected $station;
|
||||
|
||||
/** @var FrontendAbstract */
|
||||
protected $frontend;
|
||||
|
||||
/** @var RemoteAbstract[] */
|
||||
protected $remotes;
|
||||
|
||||
/** @var bool */
|
||||
protected $include_clients = false;
|
||||
|
||||
/** @var string|null The preloaded "payload" to supply to the nowplaying adapters, if one is available. */
|
||||
protected $payload;
|
||||
|
||||
/** @var array The composed "raw" NowPlaying data. */
|
||||
protected $np_raw = [];
|
||||
|
||||
public function __construct(
|
||||
Station $station,
|
||||
FrontendAbstract $frontend,
|
||||
array $remotes,
|
||||
$payload = null,
|
||||
$include_clients = false
|
||||
) {
|
||||
$this->station = $station;
|
||||
$this->frontend = $frontend;
|
||||
$this->remotes = $remotes;
|
||||
$this->payload = $payload;
|
||||
$this->include_clients = $include_clients;
|
||||
}
|
||||
|
||||
public function getStation(): Station
|
||||
{
|
||||
return $this->station;
|
||||
}
|
||||
|
||||
public function getFrontend(): FrontendAbstract
|
||||
{
|
||||
return $this->frontend;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RemoteAbstract[]
|
||||
*/
|
||||
public function getRemotes(): array
|
||||
{
|
||||
return $this->remotes;
|
||||
}
|
||||
|
||||
public function includeClients(): bool
|
||||
{
|
||||
return $this->include_clients;
|
||||
}
|
||||
|
||||
public function getPayload(): ?string
|
||||
{
|
||||
return $this->payload;
|
||||
}
|
||||
|
||||
public function getRawResponse(): array
|
||||
{
|
||||
return $this->np_raw;
|
||||
}
|
||||
|
||||
public function setRawResponse(array $np): void
|
||||
{
|
||||
$this->np_raw = $np;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
namespace App\Event;
|
||||
|
||||
use App\Entity\Api\NowPlaying;
|
||||
use App\Entity\Station;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
|
||||
class SendWebhooks extends Event
|
||||
{
|
||||
const NAME = 'webhooks-send';
|
||||
|
||||
/** @var Station */
|
||||
protected $station;
|
||||
|
||||
/** @var NowPlaying */
|
||||
protected $np;
|
||||
|
||||
/** @var array */
|
||||
protected $triggers = [];
|
||||
|
||||
/** @var bool */
|
||||
protected $is_standalone = true;
|
||||
|
||||
public function __construct(Station $station, NowPlaying $np, $np_old = null, $is_standalone = true)
|
||||
{
|
||||
$this->station = $station;
|
||||
$this->np = $np;
|
||||
$this->is_standalone = $is_standalone;
|
||||
|
||||
$to_trigger = ['all'];
|
||||
|
||||
if ($np_old instanceof NowPlaying) {
|
||||
if ($np_old->now_playing->song->id !== $np->now_playing->song->id) {
|
||||
$to_trigger[] = 'song_changed';
|
||||
}
|
||||
|
||||
if ($np_old->listeners->current > $np->listeners->current) {
|
||||
$to_trigger[] = 'listener_lost';
|
||||
} elseif ($np_old->listeners->current < $np->listeners->current) {
|
||||
$to_trigger[] = 'listener_gained';
|
||||
}
|
||||
|
||||
if ($np_old->live->is_live === false && $np->live->is_live === true) {
|
||||
$to_trigger[] = 'live_connect';
|
||||
} elseif ($np_old->live->is_live === true && $np->live->is_live === false) {
|
||||
$to_trigger[] = 'live_disconnect';
|
||||
}
|
||||
}
|
||||
|
||||
$this->triggers = $to_trigger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Station
|
||||
*/
|
||||
public function getStation(): Station
|
||||
{
|
||||
return $this->station;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NowPlaying
|
||||
*/
|
||||
public function getNowPlaying(): NowPlaying
|
||||
{
|
||||
return $this->np;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getTriggers(): array
|
||||
{
|
||||
return $this->triggers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $trigger_name
|
||||
* @return bool
|
||||
*/
|
||||
public function hasTrigger($trigger_name): bool
|
||||
{
|
||||
return in_array($trigger_name, $this->triggers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isStandalone(): bool
|
||||
{
|
||||
return $this->is_standalone;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
namespace App\EventHandler;
|
||||
|
||||
use App\Event\GenerateRawNowPlaying;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class DefaultNowPlaying implements EventSubscriberInterface
|
||||
{
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
if (APP_TESTING_MODE) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
GenerateRawNowPlaying::NAME => [
|
||||
['loadRawFromFrontend', 10],
|
||||
['addToRawFromRemotes', 0],
|
||||
['cleanUpRawOutput', -10],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function loadRawFromFrontend(GenerateRawNowPlaying $event)
|
||||
{
|
||||
$np_raw = $event->getFrontend()->getNowPlaying($event->getPayload(), $event->includeClients());
|
||||
|
||||
$event->setRawResponse($np_raw);
|
||||
}
|
||||
|
||||
public function addToRawFromRemotes(GenerateRawNowPlaying $event)
|
||||
{
|
||||
$np_raw = $event->getRawResponse();
|
||||
|
||||
// Loop through all remotes and update NP data accordingly.
|
||||
foreach($event->getRemotes() as $remote_adapter) {
|
||||
$remote_adapter->updateNowPlaying($np_raw, $event->includeClients());
|
||||
}
|
||||
|
||||
$event->setRawResponse($np_raw);
|
||||
}
|
||||
|
||||
public function cleanUpRawOutput(GenerateRawNowPlaying $event)
|
||||
{
|
||||
$np_raw = $event->getRawResponse();
|
||||
|
||||
array_walk($np_raw['current_song'], function(&$value) {
|
||||
$value = htmlspecialchars_decode($value);
|
||||
$value = trim($value);
|
||||
});
|
||||
|
||||
$event->setRawResponse($np_raw);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
namespace App\EventHandler;
|
||||
|
||||
use App\Event\BuildRoutes;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class DefaultRoutes implements EventSubscriberInterface
|
||||
{
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
BuildRoutes::NAME => [
|
||||
['addDefaultMiddleware', 1],
|
||||
['addDefaultRoutes', 0],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected $routes_path;
|
||||
|
||||
public function __construct($routes_path)
|
||||
{
|
||||
$this->routes_path = $routes_path;
|
||||
}
|
||||
|
||||
public function addDefaultMiddleware(BuildRoutes $event)
|
||||
{
|
||||
$app = $event->getApp();
|
||||
|
||||
// Get the current user entity object and assign it into the request if it exists.
|
||||
$app->add(\App\Middleware\GetCurrentUser::class);
|
||||
|
||||
// Inject the application router into the request object.
|
||||
$app->add(\App\Middleware\EnableRouter::class);
|
||||
|
||||
// Inject the session manager into the request object.
|
||||
$app->add(\App\Middleware\EnableSession::class);
|
||||
|
||||
// Check HTTPS setting and enforce Content Security Policy accordingly.
|
||||
$app->add(\App\Middleware\EnforceSecurity::class);
|
||||
|
||||
// Remove trailing slash from all URLs when routing.
|
||||
$app->add(\App\Middleware\RemoveSlashes::class);
|
||||
}
|
||||
|
||||
public function addDefaultRoutes(BuildRoutes $event)
|
||||
{
|
||||
call_user_func(include($this->routes_path), $event->getApp());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
namespace App\EventHandler;
|
||||
|
||||
use App\Event\BuildView;
|
||||
use Slim\Container;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class DefaultView implements EventSubscriberInterface
|
||||
{
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
BuildView::NAME => [
|
||||
['addViewFunctions', 1],
|
||||
['addViewData', 0],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/** @var Container */
|
||||
protected $di;
|
||||
|
||||
public function __construct(Container $di)
|
||||
{
|
||||
$this->di = $di;
|
||||
}
|
||||
|
||||
public function addViewFunctions(BuildView $event)
|
||||
{
|
||||
$view = $event->getView();
|
||||
|
||||
$view->registerFunction('service', function($service) {
|
||||
return $this->di->get($service);
|
||||
});
|
||||
|
||||
$view->registerFunction('escapeJs', function($string) {
|
||||
return json_encode($string);
|
||||
});
|
||||
|
||||
$view->registerFunction('mailto', function ($address, $link_text = null) {
|
||||
$address = substr(chunk_split(bin2hex(" $address"), 2, ";&#x"), 3, -3);
|
||||
$link_text = $link_text ?? $address;
|
||||
|
||||
return '<a href="mailto:' . $address . '">' . $link_text . '</a>';
|
||||
});
|
||||
|
||||
$view->registerFunction('pluralize', function ($word, $num = 0) {
|
||||
if ((int)$num === 1) {
|
||||
return $word;
|
||||
} else {
|
||||
return \Doctrine\Common\Inflector\Inflector::pluralize($word);
|
||||
}
|
||||
});
|
||||
|
||||
$view->registerFunction('truncate', function ($text, $length = 80) {
|
||||
return \App\Utilities::truncate_text($text, $length);
|
||||
});
|
||||
}
|
||||
|
||||
public function addViewData(BuildView $event)
|
||||
{
|
||||
/** @var \App\Session $session */
|
||||
$session = $this->di[\App\Session::class];
|
||||
|
||||
$event->getView()->addData([
|
||||
'app_settings' => $this->di['app_settings'],
|
||||
'router' => $this->di['router'],
|
||||
'request' => $this->di['request'],
|
||||
'assets' => $this->di[\App\Assets::class],
|
||||
'auth' => $this->di[\App\Auth::class],
|
||||
'acl' => $this->di[\App\Acl::class],
|
||||
'flash' => $session->getFlash(),
|
||||
'customization' => $this->di[\App\Customization::class],
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
namespace App\EventHandler;
|
||||
|
||||
use App\Entity;
|
||||
use App\Event\SendWebhooks;
|
||||
use App\Exception;
|
||||
use App\Webhook\Connector;
|
||||
use Monolog\Handler\TestHandler;
|
||||
use Monolog\Logger;
|
||||
use Pimple\Psr11\ServiceLocator;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class DefaultWebhooks implements EventSubscriberInterface
|
||||
{
|
||||
/** @var Logger */
|
||||
protected $logger;
|
||||
|
||||
/** @var ServiceLocator */
|
||||
protected $connectors;
|
||||
|
||||
public function __construct(Logger $logger, ServiceLocator $connectors)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->connectors = $connectors;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
if (APP_TESTING_MODE) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
SendWebhooks::NAME => [
|
||||
['dispatchWebhooks', 0],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function dispatchWebhooks(SendWebhooks $event)
|
||||
{
|
||||
// Compile list of connectors for the station. Always dispatch to the local websocket receiver.
|
||||
$connectors = [];
|
||||
|
||||
if ($event->isStandalone()) {
|
||||
$connectors[] = [
|
||||
'type' => 'local',
|
||||
'triggers' => [],
|
||||
'config' => [],
|
||||
];
|
||||
}
|
||||
|
||||
// Assemble list of webhooks for the station
|
||||
$station_webhooks = $event->getStation()->getWebhooks();
|
||||
|
||||
if ($station_webhooks->count() > 0) {
|
||||
foreach($station_webhooks as $webhook) {
|
||||
/** @var Entity\StationWebhook $webhook */
|
||||
if ($webhook->isEnabled()) {
|
||||
$connectors[] = [
|
||||
'type' => $webhook->getType(),
|
||||
'triggers' => $webhook->getTriggers() ?: [],
|
||||
'config' => $webhook->getConfig() ?: [],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->logger->debug('Triggering events: '.implode(', ', $event->getTriggers()));
|
||||
|
||||
// Trigger all appropriate webhooks.
|
||||
foreach($connectors as $connector) {
|
||||
if (!$this->connectors->has($connector['type'])) {
|
||||
$this->logger->error(sprintf('Webhook connector "%s" does not exist; skipping.', $connector['type']));
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var Connector\ConnectorInterface $connector_obj */
|
||||
$connector_obj = $this->connectors->get($connector['type']);
|
||||
|
||||
if ($connector_obj->shouldDispatch($event, (array)$connector['triggers'])) {
|
||||
$this->logger->debug(sprintf('Dispatching connector "%s".', $connector['type']));
|
||||
|
||||
$connector_obj->dispatch($event, (array)$connector['config']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a "test" dispatch of the web hook, regardless of whether it is currently enabled, and
|
||||
* return any logging information this yields.
|
||||
*
|
||||
* @param Entity\Station $station
|
||||
* @param Entity\StationWebhook $webhook
|
||||
* @return TestHandler
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testDispatch(Entity\Station $station, Entity\StationWebhook $webhook)
|
||||
{
|
||||
$webhook_type = $webhook->getType();
|
||||
$webhook_config = $webhook->getConfig();
|
||||
|
||||
if (!$this->connectors->has($webhook_type)) {
|
||||
throw new Exception(sprintf('Webhook connector "%s" does not exist; skipping.', $webhook_type));
|
||||
}
|
||||
|
||||
$handler = new TestHandler(Logger::DEBUG, false);
|
||||
$this->logger->pushHandler($handler);
|
||||
|
||||
/** @var Connector\ConnectorInterface $connector_obj */
|
||||
$connector_obj = $this->connectors->get($webhook_type);
|
||||
|
||||
$np = $station->getNowplaying();
|
||||
|
||||
$event = new SendWebhooks($station, $np);
|
||||
$connector_obj->dispatch($event, $webhook_config);
|
||||
|
||||
$this->logger->popHandler();
|
||||
|
||||
return $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly access a webhook connector of the specified type.
|
||||
*
|
||||
* @param $type
|
||||
* @return Connector\ConnectorInterface
|
||||
*/
|
||||
public function getConnector($type): Connector\ConnectorInterface
|
||||
{
|
||||
if ($this->connectors->has($type)) {
|
||||
return $this->connectors->get($type);
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException('Invalid web hook connector type specified.');
|
||||
}
|
||||
}
|
|
@ -28,7 +28,6 @@ class EnableView
|
|||
{
|
||||
$this->view->addData([
|
||||
'request' => $request,
|
||||
'user' => $request->getAttribute(Request::ATTRIBUTE_USER),
|
||||
]);
|
||||
|
||||
$request = $request->withAttribute(Request::ATTRIBUTE_VIEW, $this->view);
|
||||
|
|
|
@ -3,9 +3,11 @@ namespace App\Middleware;
|
|||
|
||||
use App\Auth;
|
||||
use App\Customization;
|
||||
use App\Event\BuildView;
|
||||
use App\Http\Request;
|
||||
use App\Http\Response;
|
||||
use App\Entity;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
|
||||
/**
|
||||
* Get the current user entity object and assign it into the request if it exists.
|
||||
|
@ -18,10 +20,14 @@ class GetCurrentUser
|
|||
/** @var Customization */
|
||||
protected $customization;
|
||||
|
||||
public function __construct(Auth $auth, Customization $customization)
|
||||
/** @var EventDispatcher */
|
||||
protected $dispatcher;
|
||||
|
||||
public function __construct(Auth $auth, Customization $customization, EventDispatcher $dispatcher)
|
||||
{
|
||||
$this->auth = $auth;
|
||||
$this->customization = $customization;
|
||||
$this->dispatcher = $dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -39,6 +45,12 @@ class GetCurrentUser
|
|||
$this->customization->setUser($user);
|
||||
$request = $this->customization->init($request);
|
||||
|
||||
$this->dispatcher->addListener(BuildView::NAME, function(BuildView $event) use ($user) {
|
||||
$event->getView()->addData([
|
||||
'user' => $user,
|
||||
]);
|
||||
});
|
||||
|
||||
$request = $request
|
||||
->withAttribute(Request::ATTRIBUTE_USER, $user)
|
||||
->withAttribute('is_logged_in', ($user instanceof Entity\User));
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
namespace App;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
use Slim\Container;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
class Plugins
|
||||
{
|
||||
/** @var array An array of all plugins and their capabilities. */
|
||||
protected $plugins = [];
|
||||
|
||||
public function __construct($base_dir)
|
||||
{
|
||||
$this->loadDirectory($base_dir);
|
||||
}
|
||||
|
||||
public function loadDirectory($dir): void
|
||||
{
|
||||
$plugins = (new Finder())
|
||||
->ignoreUnreadableDirs()
|
||||
->directories()
|
||||
->in($dir);
|
||||
|
||||
foreach($plugins as $plugin_dir) {
|
||||
/** @var SplFileInfo $plugin_dir */
|
||||
$plugin_prefix = $plugin_dir->getRelativePathname();
|
||||
$plugin_namespace = 'Plugin\\'.\Doctrine\Common\Inflector\Inflector::classify($plugin_prefix).'\\';
|
||||
|
||||
$this->plugins[$plugin_prefix] = [
|
||||
'namespace' => $plugin_namespace,
|
||||
'path' => $plugin_dir->getPathname(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add plugin namespace classes (and any Composer dependencies) to the global include list.
|
||||
*
|
||||
* @param ClassLoader $autoload
|
||||
*/
|
||||
public function registerAutoloaders(ClassLoader $autoload): void
|
||||
{
|
||||
foreach($this->plugins as $plugin) {
|
||||
$plugin_path = $plugin['path'];
|
||||
|
||||
if (file_exists($plugin_path.'/vendor/autoload.php')) {
|
||||
require($plugin_path.'/vendor/autoload.php');
|
||||
}
|
||||
|
||||
$autoload->addPsr4($plugin['namespace'], $plugin_path.'/src');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register or override any services contained in the global Dependency Injection container.
|
||||
*
|
||||
* @param Container $di
|
||||
* @param array $settings
|
||||
*/
|
||||
public function registerServices(Container $di, array $settings): void
|
||||
{
|
||||
foreach($this->plugins as $plugin) {
|
||||
$plugin_path = $plugin['path'];
|
||||
|
||||
if (file_exists($plugin_path . '/services.php')) {
|
||||
call_user_func(include($plugin_path . '/services.php'), $di, $settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register custom events that the plugin overrides with the Event Dispatcher.
|
||||
*
|
||||
* @param EventDispatcher $dispatcher
|
||||
* @param Container $di
|
||||
* @param array $settings
|
||||
*/
|
||||
public function registerEvents(EventDispatcher $dispatcher, Container $di, array $settings): void
|
||||
{
|
||||
foreach($this->plugins as $plugin) {
|
||||
$plugin_path = $plugin['path'];
|
||||
|
||||
if (file_exists($plugin_path . '/events.php')) {
|
||||
call_user_func(include($plugin_path . '/events.php'), $dispatcher, $di, $settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,7 +33,8 @@ class MiddlewareProvider implements ServiceProviderInterface
|
|||
$di[Middleware\GetCurrentUser::class] = function($di) {
|
||||
return new Middleware\GetCurrentUser(
|
||||
$di[App\Auth::class],
|
||||
$di[App\Customization::class]
|
||||
$di[App\Customization::class],
|
||||
$di[\Symfony\Component\EventDispatcher\EventDispatcher::class]
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -57,8 +57,8 @@ class SyncProvider implements ServiceProviderInterface
|
|||
$di[\App\Radio\AutoDJ::class],
|
||||
$di[\App\Cache::class],
|
||||
$di[\InfluxDB\Database::class],
|
||||
$di[\App\Webhook\Dispatcher::class],
|
||||
$di[\Doctrine\ORM\EntityManager::class],
|
||||
$di[\Symfony\Component\EventDispatcher\EventDispatcher::class],
|
||||
$di[\Monolog\Logger::class]
|
||||
);
|
||||
};
|
||||
|
|
|
@ -2,15 +2,17 @@
|
|||
namespace App\Sync\Task;
|
||||
|
||||
use App\Cache;
|
||||
use App\Event\GenerateRawNowPlaying;
|
||||
use App\Event\SendWebhooks;
|
||||
use App\Radio\AutoDJ;
|
||||
use App\ApiUtilities;
|
||||
use App\Radio\Adapters;
|
||||
use App\Radio\Frontend\FrontendAbstract;
|
||||
use App\Webhook\Dispatcher;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use InfluxDB\Database;
|
||||
use App\Entity;
|
||||
use Monolog\Logger;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
|
||||
class NowPlaying extends TaskAbstract
|
||||
{
|
||||
|
@ -32,8 +34,8 @@ class NowPlaying extends TaskAbstract
|
|||
/** @var Logger */
|
||||
protected $logger;
|
||||
|
||||
/** @var Dispatcher */
|
||||
protected $webhook_dispatcher;
|
||||
/** @var EventDispatcher */
|
||||
protected $event_dispatcher;
|
||||
|
||||
/** @var ApiUtilities */
|
||||
protected $api_utils;
|
||||
|
@ -54,14 +56,15 @@ class NowPlaying extends TaskAbstract
|
|||
protected $analytics_level;
|
||||
|
||||
/**
|
||||
* @param EntityManager $em
|
||||
* @param Database $influx
|
||||
* @param Cache $cache
|
||||
* @param Adapters $adapters
|
||||
* @param Dispatcher $webhook_dispatcher
|
||||
* @param ApiUtilities $api_utils
|
||||
* @param AutoDJ $autodj
|
||||
* @param Cache $cache
|
||||
* @param Database $influx
|
||||
* @param EntityManager $em
|
||||
* @param EventDispatcher $event_dispatcher
|
||||
* @param Logger $logger
|
||||
*
|
||||
* @see \App\Provider\SyncProvider
|
||||
*/
|
||||
public function __construct(
|
||||
|
@ -70,8 +73,8 @@ class NowPlaying extends TaskAbstract
|
|||
AutoDJ $autodj,
|
||||
Cache $cache,
|
||||
Database $influx,
|
||||
Dispatcher $webhook_dispatcher,
|
||||
EntityManager $em,
|
||||
EventDispatcher $event_dispatcher,
|
||||
Logger $logger)
|
||||
{
|
||||
$this->adapters = $adapters;
|
||||
|
@ -79,9 +82,9 @@ class NowPlaying extends TaskAbstract
|
|||
$this->autodj = $autodj;
|
||||
$this->cache = $cache;
|
||||
$this->em = $em;
|
||||
$this->event_dispatcher = $event_dispatcher;
|
||||
$this->influx = $influx;
|
||||
$this->logger = $logger;
|
||||
$this->webhook_dispatcher = $webhook_dispatcher;
|
||||
|
||||
$this->history_repo = $this->em->getRepository(Entity\SongHistory::class);
|
||||
$this->song_repo = $this->em->getRepository(Entity\Song::class);
|
||||
|
@ -198,27 +201,13 @@ class NowPlaying extends TaskAbstract
|
|||
/** @var Entity\Api\NowPlaying|null $np_old */
|
||||
$np_old = $station->getNowplaying();
|
||||
|
||||
// Build the new "raw" NowPlaying data.
|
||||
$event = new GenerateRawNowPlaying($station, $frontend_adapter, $remote_adapters, $payload, $include_clients);
|
||||
$this->event_dispatcher->dispatch(GenerateRawNowPlaying::NAME, $event);
|
||||
$np_raw = $event->getRawResponse();
|
||||
|
||||
$np = new Entity\Api\NowPlaying;
|
||||
$np->station = $station->api($frontend_adapter, $remote_adapters);
|
||||
|
||||
// Build the new "raw" NowPlaying data from the adapters.
|
||||
if (APP_TESTING_MODE) {
|
||||
$np_raw = \NowPlaying\Adapter\AdapterAbstract::NOWPLAYING_EMPTY;
|
||||
} else {
|
||||
$np_raw = $frontend_adapter->getNowPlaying($payload, $include_clients);
|
||||
|
||||
// Loop through all remotes and update NP data accordingly.
|
||||
foreach($remote_adapters as $remote_adapter) {
|
||||
$remote_adapter->updateNowPlaying($np_raw, $include_clients);
|
||||
}
|
||||
|
||||
array_walk($np_raw['current_song'], function(&$value) {
|
||||
$value = htmlspecialchars_decode($value);
|
||||
$value = trim($value);
|
||||
});
|
||||
}
|
||||
|
||||
// Start to convert the "raw" NowPlaying data into the proper API entities.
|
||||
$np->listeners = new Entity\Api\NowPlayingListeners($np_raw['listeners']);
|
||||
|
||||
if (empty($np_raw['current_song']['text'])) {
|
||||
|
@ -295,8 +284,9 @@ class NowPlaying extends TaskAbstract
|
|||
$this->em->persist($station);
|
||||
$this->em->flush();
|
||||
|
||||
$np_old = ($np_old instanceof Entity\Api\NowPlaying) ? $np_old : $np;
|
||||
$this->webhook_dispatcher->dispatch($station, $np_old, $np, ($payload !== null));
|
||||
// Trigger the dispatching of webhooks.
|
||||
$webhook_event = new SendWebhooks($station, $np, $np_old, ($payload !== null));
|
||||
$this->event_dispatcher->dispatch(SendWebhooks::NAME, $webhook_event);
|
||||
|
||||
$this->logger->popProcessor();
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
namespace App\Webhook\Connector;
|
||||
|
||||
use App\Entity;
|
||||
use App\Event\SendWebhooks;
|
||||
use App\Utilities;
|
||||
use GuzzleHttp\Client;
|
||||
use Monolog\Logger;
|
||||
|
@ -20,14 +21,14 @@ abstract class AbstractConnector implements ConnectorInterface
|
|||
$this->http_client = $http_client;
|
||||
}
|
||||
|
||||
public function shouldDispatch(array $current_events, array $triggers): bool
|
||||
public function shouldDispatch(SendWebhooks $event, array $triggers): bool
|
||||
{
|
||||
if (empty($triggers)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach($triggers as $trigger) {
|
||||
if (in_array($trigger, $current_events)) {
|
||||
if ($event->hasTrigger($trigger)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
namespace App\Webhook\Connector;
|
||||
|
||||
use App\Entity;
|
||||
use App\Event\SendWebhooks;
|
||||
|
||||
interface ConnectorInterface
|
||||
{
|
||||
|
@ -9,18 +9,17 @@ interface ConnectorInterface
|
|||
* Return a boolean indicating whether this connector should dispatch, given the current events
|
||||
* that are set to be triggered, and the configured triggers for this connector.
|
||||
*
|
||||
* @param array $current_events The events that are currently being triggered.
|
||||
* @param SendWebhooks $event The current webhook dispatching event being evaluated.
|
||||
* @param array|null $triggers The configured triggers for this connector.
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldDispatch(array $current_events, array $triggers): bool;
|
||||
public function shouldDispatch(SendWebhooks $event, array $triggers): bool;
|
||||
|
||||
/**
|
||||
* Trigger the webhook for the specified station, now playing entry, and specified configuration.
|
||||
*
|
||||
* @param Entity\Station $station
|
||||
* @param Entity\Api\NowPlaying $np_new
|
||||
* @param array $config
|
||||
* @param SendWebhooks $event The details of the event that triggered the webhook.
|
||||
* @param array $config The specific settings associated with this webhook.
|
||||
*/
|
||||
public function dispatch(Entity\Station $station, Entity\Api\NowPlaying $np_new, array $config): void;
|
||||
public function dispatch(SendWebhooks $event, array $config): void;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
namespace App\Webhook\Connector;
|
||||
|
||||
use App\Entity;
|
||||
use App\Event\SendWebhooks;
|
||||
use GuzzleHttp\Exception\TransferException;
|
||||
use Monolog\Logger;
|
||||
|
||||
|
@ -60,12 +61,7 @@ use Monolog\Logger;
|
|||
|
||||
class Discord extends AbstractConnector
|
||||
{
|
||||
/**
|
||||
* @param Entity\Station $station
|
||||
* @param Entity\Api\NowPlaying $np
|
||||
* @param array $config
|
||||
*/
|
||||
public function dispatch(Entity\Station $station, Entity\Api\NowPlaying $np, array $config): void
|
||||
public function dispatch(SendWebhooks $event, array $config): void
|
||||
{
|
||||
$webhook_url = $this->_getValidUrl($config['webhook_url'] ?? '');
|
||||
|
||||
|
@ -84,7 +80,7 @@ class Discord extends AbstractConnector
|
|||
'footer' => $config['footer'] ?? '',
|
||||
];
|
||||
|
||||
$vars = $this->_replaceVariables($raw_vars, $np);
|
||||
$vars = $this->_replaceVariables($raw_vars, $event->getNowPlaying());
|
||||
|
||||
// Compose webhook
|
||||
$embed = [
|
||||
|
|
|
@ -2,17 +2,13 @@
|
|||
namespace App\Webhook\Connector;
|
||||
|
||||
use App\Entity;
|
||||
use App\Event\SendWebhooks;
|
||||
use GuzzleHttp\Exception\TransferException;
|
||||
use Monolog\Logger;
|
||||
|
||||
class Generic extends AbstractConnector
|
||||
{
|
||||
/**
|
||||
* @param Entity\Station $station
|
||||
* @param Entity\Api\NowPlaying $np
|
||||
* @param array $config
|
||||
*/
|
||||
public function dispatch(Entity\Station $station, Entity\Api\NowPlaying $np, array $config): void
|
||||
public function dispatch(SendWebhooks $event, array $config): void
|
||||
{
|
||||
$webhook_url = $this->_getValidUrl($config['webhook_url'] ?? '');
|
||||
|
||||
|
@ -26,7 +22,7 @@ class Generic extends AbstractConnector
|
|||
'headers' => [
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
'json' => $np,
|
||||
'json' => $event->getNowPlaying(),
|
||||
];
|
||||
|
||||
if (!empty($config['basic_auth_username']) && !empty($config['basic_auth_password'])) {
|
||||
|
|
|
@ -3,6 +3,7 @@ namespace App\Webhook\Connector;
|
|||
|
||||
use App\Cache;
|
||||
use App\Entity;
|
||||
use App\Event\SendWebhooks;
|
||||
use GuzzleHttp\Client;
|
||||
use InfluxDB\Database;
|
||||
use Monolog\Logger;
|
||||
|
@ -27,33 +28,21 @@ class Local extends AbstractConnector
|
|||
$this->settings_repo = $settings_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $current_events
|
||||
* @param array|null $triggers
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldDispatch(array $current_events, array $triggers): bool
|
||||
public function shouldDispatch(SendWebhooks $event, array $triggers): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Entity\Station $station
|
||||
* @param Entity\Api\NowPlaying $np
|
||||
* @param array $config
|
||||
* @throws Database\Exception
|
||||
* @throws \InfluxDB\Exception
|
||||
*/
|
||||
public function dispatch(Entity\Station $station, Entity\Api\NowPlaying $np, array $config): void
|
||||
public function dispatch(SendWebhooks $event, array $config): void
|
||||
{
|
||||
$this->logger->debug('Writing entry to InfluxDB...');
|
||||
|
||||
// Post statistics to InfluxDB.
|
||||
$influx_point = new \InfluxDB\Point(
|
||||
'station.' . $station->getId() . '.listeners',
|
||||
'station.' . $event->getStation()->getId() . '.listeners',
|
||||
(int)$np->listeners->current,
|
||||
[],
|
||||
['station' => $station->getId()],
|
||||
['station' => $event->getStation()->getId()],
|
||||
time()
|
||||
);
|
||||
|
||||
|
@ -67,7 +56,7 @@ class Local extends AbstractConnector
|
|||
if ($np_full) {
|
||||
foreach($np_full as &$np_row) {
|
||||
/** @var Entity\Api\NowPlaying $np_row */
|
||||
if ($np_row->station->id === $station->getId()) {
|
||||
if ($np_row->station->id === $event->getStation()->getId()) {
|
||||
$np_row = $np;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
namespace App\Webhook\Connector;
|
||||
|
||||
use App\Entity;
|
||||
use App\Event\SendWebhooks;
|
||||
use GuzzleHttp\Exception\TransferException;
|
||||
|
||||
/**
|
||||
|
@ -11,12 +11,7 @@ use GuzzleHttp\Exception\TransferException;
|
|||
*/
|
||||
class Telegram extends AbstractConnector
|
||||
{
|
||||
/**
|
||||
* @param Entity\Station $station
|
||||
* @param Entity\Api\NowPlaying $np
|
||||
* @param array $config
|
||||
*/
|
||||
public function dispatch(Entity\Station $station, Entity\Api\NowPlaying $np, array $config): void
|
||||
public function dispatch(SendWebhooks $event, array $config): void
|
||||
{
|
||||
$bot_token = $config['bot_token'] ?? '';
|
||||
$chat_id = $config['chat_id'] ?? '';
|
||||
|
@ -28,7 +23,7 @@ class Telegram extends AbstractConnector
|
|||
|
||||
$messages = $this->_replaceVariables([
|
||||
'text' => $config['text'],
|
||||
], $np);
|
||||
], $event->getNowPlaying());
|
||||
|
||||
try {
|
||||
$api_url = (!empty($config['api'])) ? rtrim($config['api'], '/') : 'https://api.telegram.org';
|
||||
|
|
|
@ -2,27 +2,18 @@
|
|||
namespace App\Webhook\Connector;
|
||||
|
||||
use App\Entity;
|
||||
use App\Event\SendWebhooks;
|
||||
use GuzzleHttp\Exception\TransferException;
|
||||
use Monolog\Logger;
|
||||
|
||||
class TuneIn extends AbstractConnector
|
||||
{
|
||||
/**
|
||||
* @param array $current_events
|
||||
* @param array|null $triggers
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldDispatch(array $current_events, array $triggers): bool
|
||||
public function shouldDispatch(SendWebhooks $event, array $triggers): bool
|
||||
{
|
||||
return in_array('song_changed', $current_events);
|
||||
return $event->hasTrigger('song_changed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Entity\Station $station
|
||||
* @param Entity\Api\NowPlaying $np
|
||||
* @param array $config
|
||||
*/
|
||||
public function dispatch(Entity\Station $station, Entity\Api\NowPlaying $np, array $config): void
|
||||
public function dispatch(SendWebhooks $event, array $config): void
|
||||
{
|
||||
if (empty($config['partner_id']) || empty($config['partner_key']) || empty($config['station_id'])) {
|
||||
$this->logger->error('Webhook '.$this->_getName().' is missing necessary configuration. Skipping...');
|
||||
|
@ -32,6 +23,8 @@ class TuneIn extends AbstractConnector
|
|||
$this->logger->debug('Dispatching TuneIn AIR API call...');
|
||||
|
||||
try {
|
||||
$np = $event->getNowPlaying();
|
||||
|
||||
$response = $this->http_client->get('http://air.radiotime.com/Playing.ashx', [
|
||||
'query' => [
|
||||
'partnerId' => $config['partner_id'],
|
||||
|
|
|
@ -2,18 +2,14 @@
|
|||
namespace App\Webhook\Connector;
|
||||
|
||||
use App\Entity;
|
||||
use App\Event\SendWebhooks;
|
||||
use GuzzleHttp\Exception\TransferException;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use Monolog\Logger;
|
||||
|
||||
class Twitter extends AbstractConnector
|
||||
{
|
||||
/**
|
||||
* @param Entity\Station $station
|
||||
* @param Entity\Api\NowPlaying $np
|
||||
* @param array $config
|
||||
*/
|
||||
public function dispatch(Entity\Station $station, Entity\Api\NowPlaying $np, array $config): void
|
||||
public function dispatch(SendWebhooks $event, array $config): void
|
||||
{
|
||||
if (empty($config['consumer_key'])
|
||||
|| empty($config['consumer_secret'])
|
||||
|
@ -40,7 +36,7 @@ class Twitter extends AbstractConnector
|
|||
'message' => $config['message'] ?? '',
|
||||
];
|
||||
|
||||
$vars = $this->_replaceVariables($raw_vars, $np);
|
||||
$vars = $this->_replaceVariables($raw_vars, $event->getNowPlaying());
|
||||
|
||||
// Dispatch webhook
|
||||
$this->logger->debug('Posting to Twitter...');
|
||||
|
|
|
@ -3,6 +3,7 @@ namespace App\Webhook;
|
|||
|
||||
use App\Config;
|
||||
use App\Entity;
|
||||
use App\Event\SendWebhooks;
|
||||
use App\Exception;
|
||||
use App\Provider\WebhookProvider;
|
||||
use Monolog\Handler\TestHandler;
|
||||
|
@ -31,12 +32,9 @@ class Dispatcher
|
|||
/**
|
||||
* Determine which webhooks to dispatch for a given change in Now Playing data, and dispatch them.
|
||||
*
|
||||
* @param Entity\Station $station
|
||||
* @param Entity\Api\NowPlaying $np_old
|
||||
* @param Entity\Api\NowPlaying $np_new
|
||||
* @param boolean $is_standalone
|
||||
* @param SendWebhooks $event
|
||||
*/
|
||||
public function dispatch(Entity\Station $station, Entity\Api\NowPlaying $np_old, Entity\Api\NowPlaying $np_new, $is_standalone = true): void
|
||||
public function dispatch(SendWebhooks $event): void
|
||||
{
|
||||
if (APP_TESTING_MODE) {
|
||||
$this->logger->info('In testing mode; no webhooks dispatched.');
|
||||
|
@ -46,7 +44,7 @@ class Dispatcher
|
|||
// Compile list of connectors for the station. Always dispatch to the local websocket receiver.
|
||||
$connectors = [];
|
||||
|
||||
if ($is_standalone) {
|
||||
if ($event->isStandalone()) {
|
||||
$connectors[] = [
|
||||
'type' => 'local',
|
||||
'triggers' => [],
|
||||
|
@ -55,7 +53,7 @@ class Dispatcher
|
|||
}
|
||||
|
||||
// Assemble list of webhooks for the station
|
||||
$station_webhooks = $station->getWebhooks();
|
||||
$station_webhooks = $event->getStation()->getWebhooks();
|
||||
|
||||
if ($station_webhooks->count() > 0) {
|
||||
foreach($station_webhooks as $webhook) {
|
||||
|
@ -70,26 +68,7 @@ class Dispatcher
|
|||
}
|
||||
}
|
||||
|
||||
// Determine which events should be triggered as a result of this change.
|
||||
$to_trigger = ['all'];
|
||||
|
||||
if ($np_old->now_playing->song->id !== $np_new->now_playing->song->id) {
|
||||
$to_trigger[] = 'song_changed';
|
||||
}
|
||||
|
||||
if ($np_old->listeners->current > $np_new->listeners->current) {
|
||||
$to_trigger[] = 'listener_lost';
|
||||
} elseif ($np_old->listeners->current < $np_new->listeners->current) {
|
||||
$to_trigger[] = 'listener_gained';
|
||||
}
|
||||
|
||||
if ($np_old->live->is_live === false && $np_new->live->is_live === true) {
|
||||
$to_trigger[] = 'live_connect';
|
||||
} elseif ($np_old->live->is_live === true && $np_new->live->is_live === false) {
|
||||
$to_trigger[] = 'live_disconnect';
|
||||
}
|
||||
|
||||
$this->logger->debug('Triggering events: '.implode(', ', $to_trigger));
|
||||
$this->logger->debug('Triggering events: '.implode(', ', $event->getTriggers()));
|
||||
|
||||
// Trigger all appropriate webhooks.
|
||||
foreach($connectors as $connector) {
|
||||
|
@ -101,10 +80,10 @@ class Dispatcher
|
|||
/** @var Connector\ConnectorInterface $connector_obj */
|
||||
$connector_obj = $this->connectors->get($connector['type']);
|
||||
|
||||
if ($connector_obj->shouldDispatch($to_trigger, (array)$connector['triggers'])) {
|
||||
if ($connector_obj->shouldDispatch($event, (array)$connector['triggers'])) {
|
||||
$this->logger->debug(sprintf('Dispatching connector "%s".', $connector['type']));
|
||||
|
||||
$connector_obj->dispatch($station, $np_new, (array)$connector['config']);
|
||||
$connector_obj->dispatch($event, (array)$connector['config']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -135,7 +114,8 @@ class Dispatcher
|
|||
|
||||
$np = $station->getNowplaying();
|
||||
|
||||
$connector_obj->dispatch($station, $np, $webhook_config);
|
||||
$event = new SendWebhooks($station, $np);
|
||||
$connector_obj->dispatch($event, $webhook_config);
|
||||
|
||||
$this->logger->popHandler();
|
||||
|
||||
|
|
Loading…
Reference in New Issue