Notifly in Drupal
In Drupal, the easiest approach is to create a custom module notifly_alerts —
one file and an info.yml. No composer dependencies are required;
the built-in \Drupal::httpClient() is used.
Module structure
Section titled “Module structure”modules/custom/notifly_alerts/├── notifly_alerts.info.yml├── notifly_alerts.module└── src/ └── Notifly.phpnotifly_alerts.info.yml:
name: 'Notifly Alerts'type: moduledescription: 'Push-уведомления через сервер Notifly.'core_version_requirement: ^9 || ^10package: Customsrc/Notifly.php:
<?php
namespace Drupal\notifly_alerts;
class Notifly { public static function send(string $title, string $message, int $priority = 5): void { $config = \Drupal::config('notifly_alerts.settings'); $url = $config->get('url') ?: getenv('NOTIFLY_URL'); $token = $config->get('token') ?: getenv('NOTIFLY_TOKEN'); if (!$url || !$token) { return; } try { \Drupal::httpClient()->post("$url/message?token=$token", [ 'json' => [ 'title' => mb_substr($title, 0, 200), 'message' => mb_substr($message, 0, 1500), 'priority' => $priority, ], 'timeout' => 5, ]); } catch (\Throwable $e) { // Never break the main Drupal request because of a notification. } }}notifly_alerts.module:
<?php
use Drupal\notifly_alerts\Notifly;use Drupal\node\NodeInterface;use Drupal\user\UserInterface;use Drupal\Core\Logger\RfcLogLevel;
/** * A new node has been published. */function notifly_alerts_entity_insert($entity) { if ($entity instanceof NodeInterface) { Notifly::send( "📝 Новый узел: {$entity->getTitle()}", "Тип: {$entity->bundle()}\n" . "Автор: {$entity->getOwner()->getAccountName()}\n" . "URL: " . $entity->toUrl('canonical', ['absolute' => TRUE])->toString(), 4 ); }}
/** * User registration. */function notifly_alerts_user_insert(UserInterface $account) { Notifly::send( "👤 Регистрация: {$account->getAccountName()}", "Email: {$account->getEmail()}\n" . "Roles: " . implode(', ', $account->getRoles()), 5 );}
/** * PHP / watchdog errors at level ERROR and above. */function notifly_alerts_logger_factory_alter(&$factory) { // see implementation below — a separate logger service is required}Watchdog → Notifly
Section titled “Watchdog → Notifly”To catch all events, add a custom logger. Create
notifly_alerts.services.yml:
services: logger.notifly: class: Drupal\notifly_alerts\Logger\NotiflyLogger arguments: ['@logger.log_message_parser'] tags: - { name: logger }And src/Logger/NotiflyLogger.php:
<?php
namespace Drupal\notifly_alerts\Logger;
use Drupal\Core\Logger\LogMessageParserInterface;use Drupal\Core\Logger\RfcLoggerTrait;use Drupal\notifly_alerts\Notifly;use Psr\Log\LoggerInterface;
class NotiflyLogger implements LoggerInterface { use RfcLoggerTrait;
public function __construct(private LogMessageParserInterface $parser) {}
public function log($level, string|\Stringable $message, array $context = []): void { // 0=Emergency, 3=Error. if ($level > 3) return;
$vars = $this->parser->parseMessagePlaceholders($message, $context); $body = strtr((string) $message, $vars);
Notifly::send( sprintf('🐞 %s [%s]', $context['channel'] ?? 'php', match ((int)$level) { 0,1 => 'CRITICAL', 2 => 'CRITICAL', 3 => 'ERROR', default => 'WARN', }), $body . "\n\nRequest: " . ($context['request_uri'] ?? '-'), $level <= 2 ? 10 : 8 ); }}After adding — drush cr. All watchdog events at ERROR/CRITICAL/EMERGENCY
levels will be sent to Notifly.
Configuration via the UI (optional)
Section titled “Configuration via the UI (optional)”Create config/install/notifly_alerts.settings.yml:
url: 'https://your-notifly.example.com'token: 'AGdjfk_L.dKe8q'And a settings page in the admin area — that’s the standard Drupal ConfigForm pattern.
Spam protection
Section titled “Spam protection”You can use the built-in cache.default:
$cache_id = 'notifly_throttle:' . md5($title);if (\Drupal::cache()->get($cache_id)) return;\Drupal::cache()->set($cache_id, 1, time() + 60);Notifly::send(/* ... */);Benefits
Section titled “Benefits”- Full visibility of watchdog in your pocket. No need to go to Reports → Recent log.
- Nodes and registrations in real time. Useful for moderation.
- A cheap alternative to external error services.
What to improve next
Section titled “What to improve next”- Separate Notifly channels for content and technical events.
- Include the node/user ID in
extrasso the Notifly client can provide deep-linking.