feat: add discord notifications
This commit is contained in:
parent
df1b9e7319
commit
f9a2ff6d90
@ -8,26 +8,30 @@
|
|||||||
|
|
||||||
class DiscordSettings extends Component
|
class DiscordSettings extends Component
|
||||||
{
|
{
|
||||||
public Team $model;
|
public Team $team;
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'model.discord_enabled' => 'nullable|boolean',
|
'team.discord_enabled' => 'nullable|boolean',
|
||||||
'model.discord_webhook_url' => 'required|url',
|
'team.discord_webhook_url' => 'required|url',
|
||||||
'model.discord_notifications_test' => 'nullable|boolean',
|
'team.discord_notifications_test' => 'nullable|boolean',
|
||||||
'model.discord_notifications_deployments' => 'nullable|boolean',
|
'team.discord_notifications_deployments' => 'nullable|boolean',
|
||||||
'model.discord_notifications_status_changes' => 'nullable|boolean',
|
'team.discord_notifications_status_changes' => 'nullable|boolean',
|
||||||
'model.discord_notifications_database_backups' => 'nullable|boolean',
|
'team.discord_notifications_database_backups' => 'nullable|boolean',
|
||||||
];
|
];
|
||||||
protected $validationAttributes = [
|
protected $validationAttributes = [
|
||||||
'model.discord_webhook_url' => 'Discord Webhook',
|
'team.discord_webhook_url' => 'Discord Webhook',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->team = auth()->user()->currentTeam();
|
||||||
|
}
|
||||||
public function instantSave()
|
public function instantSave()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->submit();
|
$this->submit();
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
ray($e->getMessage());
|
ray($e->getMessage());
|
||||||
$this->model->discord_enabled = false;
|
$this->team->discord_enabled = false;
|
||||||
$this->validate();
|
$this->validate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,8 +45,8 @@ public function submit()
|
|||||||
|
|
||||||
public function saveModel()
|
public function saveModel()
|
||||||
{
|
{
|
||||||
$this->model->save();
|
$this->team->save();
|
||||||
if (is_a($this->model, Team::class)) {
|
if (is_a($this->team, Team::class)) {
|
||||||
refreshSession();
|
refreshSession();
|
||||||
}
|
}
|
||||||
$this->emit('success', 'Settings saved.');
|
$this->emit('success', 'Settings saved.');
|
||||||
@ -50,7 +54,7 @@ public function saveModel()
|
|||||||
|
|
||||||
public function sendTestNotification()
|
public function sendTestNotification()
|
||||||
{
|
{
|
||||||
$this->model->notify(new Test);
|
$this->team->notify(new Test());
|
||||||
$this->emit('success', 'Test notification sent.');
|
$this->emit('success', 'Test notification sent.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ class EmailSettings extends Component
|
|||||||
|
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
|
$this->team = auth()->user()->currentTeam();
|
||||||
['sharedEmailEnabled' => $this->sharedEmailEnabled] = $this->team->limits;
|
['sharedEmailEnabled' => $this->sharedEmailEnabled] = $this->team->limits;
|
||||||
$this->emails = auth()->user()->email;
|
$this->emails = auth()->user()->email;
|
||||||
}
|
}
|
||||||
|
62
app/Http/Livewire/Notifications/TelegramSettings.php
Normal file
62
app/Http/Livewire/Notifications/TelegramSettings.php
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Livewire\Notifications;
|
||||||
|
|
||||||
|
use App\Models\Team;
|
||||||
|
use App\Notifications\Test;
|
||||||
|
use Livewire\Component;
|
||||||
|
|
||||||
|
class TelegramSettings extends Component
|
||||||
|
{
|
||||||
|
public Team $team;
|
||||||
|
protected $rules = [
|
||||||
|
'team.telegram_enabled' => 'nullable|boolean',
|
||||||
|
'team.telegram_token' => 'required|string',
|
||||||
|
'team.telegram_chat_id' => 'required|string',
|
||||||
|
'team.telegram_notifications_test' => 'nullable|boolean',
|
||||||
|
'team.telegram_notifications_deployments' => 'nullable|boolean',
|
||||||
|
'team.telegram_notifications_status_changes' => 'nullable|boolean',
|
||||||
|
'team.telegram_notifications_database_backups' => 'nullable|boolean',
|
||||||
|
];
|
||||||
|
protected $validationAttributes = [
|
||||||
|
'team.telegram_token' => 'Token',
|
||||||
|
'team.telegram_chat_id' => 'Chat ID',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->team = auth()->user()->currentTeam();
|
||||||
|
}
|
||||||
|
public function instantSave()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->submit();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
ray($e->getMessage());
|
||||||
|
$this->team->telegram_enabled = false;
|
||||||
|
$this->validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function submit()
|
||||||
|
{
|
||||||
|
$this->resetErrorBag();
|
||||||
|
$this->validate();
|
||||||
|
$this->saveModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveModel()
|
||||||
|
{
|
||||||
|
$this->team->save();
|
||||||
|
if (is_a($this->team, Team::class)) {
|
||||||
|
refreshSession();
|
||||||
|
}
|
||||||
|
$this->emit('success', 'Settings saved.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sendTestNotification()
|
||||||
|
{
|
||||||
|
$this->team->notify(new Test());
|
||||||
|
$this->emit('success', 'Test notification sent.');
|
||||||
|
}
|
||||||
|
}
|
72
app/Jobs/SendMessageToTelegramJob.php
Normal file
72
app/Jobs/SendMessageToTelegramJob.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Str;
|
||||||
|
|
||||||
|
class SendMessageToTelegramJob implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of times the job may be attempted.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $tries = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum number of unhandled exceptions to allow before failing.
|
||||||
|
*/
|
||||||
|
public int $maxExceptions = 3;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
public string $text,
|
||||||
|
public array $buttons,
|
||||||
|
public string $token,
|
||||||
|
public string $chatId,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*/
|
||||||
|
public function handle(): void
|
||||||
|
{
|
||||||
|
$url = 'https://api.telegram.org/bot' . $this->token . '/sendMessage';
|
||||||
|
$inlineButtons = [];
|
||||||
|
if (!empty($this->buttons)) {
|
||||||
|
foreach ($this->buttons as $button) {
|
||||||
|
$buttonUrl = data_get($button, 'url');
|
||||||
|
if ($buttonUrl && Str::contains($buttonUrl, 'http://localhost')) {
|
||||||
|
$buttonUrl = str_replace('http://localhost', config('app.url'), $buttonUrl);
|
||||||
|
}
|
||||||
|
$inlineButtons[] = [
|
||||||
|
'text' => $button['text'],
|
||||||
|
'url' => $buttonUrl,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$payload = [
|
||||||
|
'parse_mode' => 'markdown',
|
||||||
|
'reply_markup' => json_encode([
|
||||||
|
'inline_keyboard' => [
|
||||||
|
[...$inlineButtons],
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
'chat_id' => $this->chatId,
|
||||||
|
'text' => $this->text,
|
||||||
|
];
|
||||||
|
ray($payload);
|
||||||
|
$response = Http::post($url, $payload);
|
||||||
|
if ($response->failed()) {
|
||||||
|
throw new \Exception('Telegram notification failed with ' . $response->status() . ' status code.' . $response->body());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,14 @@ public function routeNotificationForDiscord()
|
|||||||
return data_get($this, 'discord_webhook_url', null);
|
return data_get($this, 'discord_webhook_url', null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function routeNotificationForTelegram()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
"token" => data_get($this, 'telegram_token', null),
|
||||||
|
"chat_id" => data_get($this, 'telegram_chat_id', null)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
public function getRecepients($notification)
|
public function getRecepients($notification)
|
||||||
{
|
{
|
||||||
$recipients = data_get($notification, 'emails', null);
|
$recipients = data_get($notification, 'emails', null);
|
||||||
|
@ -43,19 +43,7 @@ public function __construct(Application $application, string $deployment_uuid, ?
|
|||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
$channels = [];
|
return setNotificationChannels($notifiable, 'deployments');
|
||||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
|
||||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
|
||||||
$isSubscribedToEmailEvent = data_get($notifiable, 'smtp_notifications_deployments');
|
|
||||||
$isSubscribedToDiscordEvent = data_get($notifiable, 'discord_notifications_deployments');
|
|
||||||
|
|
||||||
if ($isEmailEnabled && $isSubscribedToEmailEvent) {
|
|
||||||
$channels[] = EmailChannel::class;
|
|
||||||
}
|
|
||||||
if ($isDiscordEnabled && $isSubscribedToDiscordEvent) {
|
|
||||||
$channels[] = DiscordChannel::class;
|
|
||||||
}
|
|
||||||
return $channels;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
@ -89,4 +77,19 @@ public function toDiscord(): string
|
|||||||
}
|
}
|
||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
|
public function toTelegram(): array
|
||||||
|
{
|
||||||
|
if ($this->preview) {
|
||||||
|
$message = '❌ Pull request #' . $this->preview->pull_request_id . ' of **' . $this->application_name . '** (' . $this->preview->fqdn . ') deployment failed: ';
|
||||||
|
} else {
|
||||||
|
$message = '❌ Deployment failed of **' . $this->application_name . '** (' . $this->fqdn . '): ';
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
"message" => $message,
|
||||||
|
"buttons" => [
|
||||||
|
"text" => "View Deployment Logs",
|
||||||
|
"url" => $this->deployment_url
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,19 +43,7 @@ public function __construct(Application $application, string $deployment_uuid, A
|
|||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
$channels = [];
|
return setNotificationChannels($notifiable, 'deployments');
|
||||||
$isEmailEnabled = data_get($notifiable, 'smtp_enabled');
|
|
||||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
|
||||||
$isSubscribedToEmailEvent = data_get($notifiable, 'smtp_notifications_deployments');
|
|
||||||
$isSubscribedToDiscordEvent = data_get($notifiable, 'discord_notifications_deployments');
|
|
||||||
|
|
||||||
if ($isEmailEnabled && $isSubscribedToEmailEvent) {
|
|
||||||
$channels[] = EmailChannel::class;
|
|
||||||
}
|
|
||||||
if ($isDiscordEnabled && $isSubscribedToDiscordEvent) {
|
|
||||||
$channels[] = DiscordChannel::class;
|
|
||||||
}
|
|
||||||
return $channels;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
@ -99,4 +87,34 @@ public function toDiscord(): string
|
|||||||
}
|
}
|
||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
|
public function toTelegram(): array
|
||||||
|
{
|
||||||
|
if ($this->preview) {
|
||||||
|
$message = '✅ New PR' . $this->preview->pull_request_id . ' version successfully deployed of ' . $this->application_name . '';
|
||||||
|
if ($this->preview->fqdn) {
|
||||||
|
$buttons[] = [
|
||||||
|
"text" => "Open Application",
|
||||||
|
"url" => $this->preview->fqdn
|
||||||
|
];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$message = '✅ New version successfully deployed of ' . $this->application_name . '';
|
||||||
|
if ($this->fqdn) {
|
||||||
|
$buttons[] = [
|
||||||
|
"text" => "Open Application",
|
||||||
|
"url" => $this->fqdn
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$buttons[] = [
|
||||||
|
"text" => "Deployment logs",
|
||||||
|
"url" => $this->deployment_url
|
||||||
|
];
|
||||||
|
return [
|
||||||
|
"message" => $message,
|
||||||
|
"buttons" => [
|
||||||
|
...$buttons
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,19 +37,7 @@ public function __construct($application)
|
|||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
$channels = [];
|
return setNotificationChannels($notifiable, 'status_changes');
|
||||||
$isEmailEnabled = data_get($notifiable, 'smtp_enabled');
|
|
||||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
|
||||||
$isSubscribedToEmailEvent = data_get($notifiable, 'smtp_notifications_status_changes');
|
|
||||||
$isSubscribedToDiscordEvent = data_get($notifiable, 'discord_notifications_status_changes');
|
|
||||||
|
|
||||||
if ($isEmailEnabled && $isSubscribedToEmailEvent) {
|
|
||||||
$channels[] = EmailChannel::class;
|
|
||||||
}
|
|
||||||
if ($isDiscordEnabled && $isSubscribedToDiscordEvent) {
|
|
||||||
$channels[] = DiscordChannel::class;
|
|
||||||
}
|
|
||||||
return $channels;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
@ -70,7 +58,20 @@ public function toDiscord(): string
|
|||||||
$message = '⛔ ' . $this->application_name . ' has been stopped.
|
$message = '⛔ ' . $this->application_name . ' has been stopped.
|
||||||
|
|
||||||
';
|
';
|
||||||
$message .= '[Application URL](' . $this->application_url . ')';
|
$message .= '[Open Application in Coolify](' . $this->application_url . ')';
|
||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
|
public function toTelegram(): array
|
||||||
|
{
|
||||||
|
$message = '⛔ ' . $this->application_name . ' has been stopped.';
|
||||||
|
return [
|
||||||
|
"message" => $message,
|
||||||
|
"buttons" => [
|
||||||
|
[
|
||||||
|
"text" => "Open Application in Coolify",
|
||||||
|
"url" => $this->application_url
|
||||||
|
]
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
9
app/Notifications/Channels/SendsTelegram.php
Normal file
9
app/Notifications/Channels/SendsTelegram.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications\Channels;
|
||||||
|
|
||||||
|
interface SendsTelegram
|
||||||
|
{
|
||||||
|
public function routeNotificationForTelegram();
|
||||||
|
|
||||||
|
}
|
25
app/Notifications/Channels/TelegramChannel.php
Normal file
25
app/Notifications/Channels/TelegramChannel.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Notifications\Channels;
|
||||||
|
|
||||||
|
use App\Jobs\SendMessageToTelegramJob;
|
||||||
|
|
||||||
|
class TelegramChannel
|
||||||
|
{
|
||||||
|
public function send($notifiable, $notification): void
|
||||||
|
{
|
||||||
|
$data = $notification->toTelegram($notifiable);
|
||||||
|
$telegramData = $notifiable->routeNotificationForTelegram();
|
||||||
|
|
||||||
|
$message = data_get($data, 'message');
|
||||||
|
$buttons = data_get($data, 'buttons', []);
|
||||||
|
ray($message, $buttons);
|
||||||
|
$telegramToken = data_get($telegramData, 'token');
|
||||||
|
$chatId = data_get($telegramData, 'chat_id');
|
||||||
|
|
||||||
|
if (!$telegramToken || !$chatId || !$message) {
|
||||||
|
throw new \Exception('Telegram token, chat id and message are required');
|
||||||
|
}
|
||||||
|
dispatch(new SendMessageToTelegramJob($message, $buttons, $telegramToken, $chatId));
|
||||||
|
}
|
||||||
|
}
|
@ -25,19 +25,7 @@ public function __construct(ScheduledDatabaseBackup $backup, public $database, p
|
|||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
$channels = [];
|
return setNotificationChannels($notifiable, 'database_backups');
|
||||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
|
||||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
|
||||||
$isSubscribedToEmailEvent = data_get($notifiable, 'smtp_notifications_database_backups');
|
|
||||||
$isSubscribedToDiscordEvent = data_get($notifiable, 'discord_notifications_database_backups');
|
|
||||||
|
|
||||||
if ($isEmailEnabled && $isSubscribedToEmailEvent) {
|
|
||||||
$channels[] = EmailChannel::class;
|
|
||||||
}
|
|
||||||
if ($isDiscordEnabled && $isSubscribedToDiscordEvent) {
|
|
||||||
$channels[] = DiscordChannel::class;
|
|
||||||
}
|
|
||||||
return $channels;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
@ -56,4 +44,11 @@ public function toDiscord(): string
|
|||||||
{
|
{
|
||||||
return "❌ Database backup for {$this->name} with frequency of {$this->frequency} was FAILED.\n\nReason: {$this->output}";
|
return "❌ Database backup for {$this->name} with frequency of {$this->frequency} was FAILED.\n\nReason: {$this->output}";
|
||||||
}
|
}
|
||||||
|
public function toTelegram(): array
|
||||||
|
{
|
||||||
|
$message = "❌ Database backup for {$this->name} with frequency of {$this->frequency} was FAILED.\n\nReason: {$this->output}";
|
||||||
|
return [
|
||||||
|
"message" => $message,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,19 +25,7 @@ public function __construct(ScheduledDatabaseBackup $backup, public $database)
|
|||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
$channels = [];
|
return setNotificationChannels($notifiable, 'database_backups');
|
||||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
|
||||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
|
||||||
$isSubscribedToEmailEvent = data_get($notifiable, 'smtp_notifications_database_backups');
|
|
||||||
$isSubscribedToDiscordEvent = data_get($notifiable, 'discord_notifications_database_backups');
|
|
||||||
|
|
||||||
if ($isEmailEnabled && $isSubscribedToEmailEvent) {
|
|
||||||
$channels[] = EmailChannel::class;
|
|
||||||
}
|
|
||||||
if ($isDiscordEnabled && $isSubscribedToDiscordEvent) {
|
|
||||||
$channels[] = DiscordChannel::class;
|
|
||||||
}
|
|
||||||
return $channels;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
@ -55,4 +43,11 @@ public function toDiscord(): string
|
|||||||
{
|
{
|
||||||
return "✅ Database backup for {$this->name} with frequency of {$this->frequency} was successful.";
|
return "✅ Database backup for {$this->name} with frequency of {$this->frequency} was successful.";
|
||||||
}
|
}
|
||||||
|
public function toTelegram(): array
|
||||||
|
{
|
||||||
|
$message = "✅ Database backup for {$this->name} with frequency of {$this->frequency} was successful.";
|
||||||
|
return [
|
||||||
|
"message" => $message,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Notifications\Internal;
|
namespace App\Notifications\Internal;
|
||||||
|
|
||||||
use App\Notifications\Channels\DiscordChannel;
|
use App\Notifications\Channels\DiscordChannel;
|
||||||
|
use App\Notifications\Channels\TelegramChannel;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Notifications\Notification;
|
use Illuminate\Notifications\Notification;
|
||||||
@ -17,11 +18,17 @@ public function __construct(public string $message)
|
|||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
return [DiscordChannel::class];
|
return [TelegramChannel::class, DiscordChannel::class];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toDiscord(): string
|
public function toDiscord(): string
|
||||||
{
|
{
|
||||||
return $this->message;
|
return $this->message;
|
||||||
}
|
}
|
||||||
|
public function toTelegram(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
"message" => $this->message,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,19 +22,7 @@ public function __construct(public Server $server)
|
|||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
$channels = [];
|
return setNotificationChannels($notifiable, 'status_changes');
|
||||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
|
||||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
|
||||||
$isSubscribedToEmailEvent = data_get($notifiable, 'smtp_notifications_status_changes');
|
|
||||||
$isSubscribedToDiscordEvent = data_get($notifiable, 'discord_notifications_status_changes');
|
|
||||||
|
|
||||||
// if ($isEmailEnabled && $isSubscribedToEmailEvent) {
|
|
||||||
// $channels[] = EmailChannel::class;
|
|
||||||
// }
|
|
||||||
if ($isDiscordEnabled && $isSubscribedToDiscordEvent) {
|
|
||||||
$channels[] = DiscordChannel::class;
|
|
||||||
}
|
|
||||||
return $channels;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
@ -55,4 +43,10 @@ public function toDiscord(): string
|
|||||||
$message = '⛔ Server \'' . $this->server->name . '\' is unreachable (could be a temporary issue). If you receive this more than twice in a row, please check your server.';
|
$message = '⛔ Server \'' . $this->server->name . '\' is unreachable (could be a temporary issue). If you receive this more than twice in a row, please check your server.';
|
||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
|
public function toTelegram(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
"message" => '⛔ Server \'' . $this->server->name . '\' is unreachable (could be a temporary issue). If you receive this more than twice in a row, please check your server.'
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use App\Notifications\Channels\DiscordChannel;
|
use App\Notifications\Channels\DiscordChannel;
|
||||||
use App\Notifications\Channels\EmailChannel;
|
use App\Notifications\Channels\EmailChannel;
|
||||||
|
use App\Notifications\Channels\TelegramChannel;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Notifications\Messages\MailMessage;
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
@ -19,18 +20,7 @@ public function __construct(public string|null $emails = null)
|
|||||||
|
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
$channels = [];
|
return setNotificationChannels($notifiable, 'test');
|
||||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
|
||||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
|
||||||
|
|
||||||
if ($isDiscordEnabled && empty($this->emails)) {
|
|
||||||
$channels[] = DiscordChannel::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($isEmailEnabled && !empty($this->emails)) {
|
|
||||||
$channels[] = EmailChannel::class;
|
|
||||||
}
|
|
||||||
return $channels;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toMail(): MailMessage
|
public function toMail(): MailMessage
|
||||||
@ -48,4 +38,16 @@ public function toDiscord(): string
|
|||||||
$message .= '[Go to your dashboard](' . base_url() . ')';
|
$message .= '[Go to your dashboard](' . base_url() . ')';
|
||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
|
public function toTelegram(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
"message" => 'This is a test Telegram notification from Coolify.',
|
||||||
|
"buttons" => [
|
||||||
|
[
|
||||||
|
"text" => "Go to your dashboard",
|
||||||
|
"url" => 'https://coolify.io'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
use App\Models\InstanceSettings;
|
use App\Models\InstanceSettings;
|
||||||
use App\Models\Team;
|
use App\Models\Team;
|
||||||
|
use App\Notifications\Channels\DiscordChannel;
|
||||||
|
use App\Notifications\Channels\EmailChannel;
|
||||||
|
use App\Notifications\Channels\TelegramChannel;
|
||||||
use App\Notifications\Internal\GeneralNotification;
|
use App\Notifications\Internal\GeneralNotification;
|
||||||
use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
|
use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
|
||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
@ -242,10 +245,9 @@ function validate_cron_expression($expression_to_validate): bool
|
|||||||
function send_internal_notification(string $message): void
|
function send_internal_notification(string $message): void
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
ray('Sending internal notification... 📬 ' . $message);
|
|
||||||
$baseUrl = config('app.name');
|
$baseUrl = config('app.name');
|
||||||
$team = Team::find(0);
|
$team = Team::find(0);
|
||||||
$team->notify(new GeneralNotification("👀 Internal notifications from {$baseUrl}: " . $message));
|
$team->notify(new GeneralNotification("👀 {$baseUrl}: " . $message));
|
||||||
} catch (\Throwable $th) {
|
} catch (\Throwable $th) {
|
||||||
ray($th->getMessage());
|
ray($th->getMessage());
|
||||||
}
|
}
|
||||||
@ -270,3 +272,23 @@ function isEmailEnabled($notifiable)
|
|||||||
{
|
{
|
||||||
return data_get($notifiable, 'smtp_enabled') || data_get($notifiable, 'resend_enabled') || data_get($notifiable, 'use_instance_email_settings');
|
return data_get($notifiable, 'smtp_enabled') || data_get($notifiable, 'resend_enabled') || data_get($notifiable, 'use_instance_email_settings');
|
||||||
}
|
}
|
||||||
|
function setNotificationChannels($notifiable, $event)
|
||||||
|
{
|
||||||
|
$channels = [];
|
||||||
|
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||||
|
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||||
|
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||||
|
$isSubscribedToDiscordEvent = data_get($notifiable, "discord_notifications_$event");
|
||||||
|
$isSubscribedToTelegramEvent = data_get($notifiable, "telegram_notifications_$event");
|
||||||
|
|
||||||
|
if ($isDiscordEnabled && $isSubscribedToDiscordEvent) {
|
||||||
|
$channels[] = DiscordChannel::class;
|
||||||
|
}
|
||||||
|
if ($isEmailEnabled) {
|
||||||
|
$channels[] = EmailChannel::class;
|
||||||
|
}
|
||||||
|
if ($isTelegramEnabled && $isSubscribedToTelegramEvent) {
|
||||||
|
$channels[] = TelegramChannel::class;
|
||||||
|
}
|
||||||
|
return $channels;
|
||||||
|
}
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('teams', function (Blueprint $table) {
|
||||||
|
$table->boolean('telegram_enabled')->default(false);
|
||||||
|
$table->text('telegram_token')->nullable();
|
||||||
|
$table->text('telegram_chat_id')->nullable();
|
||||||
|
$table->boolean('telegram_notifications_test')->default(true);
|
||||||
|
$table->boolean('telegram_notifications_deployments')->default(true);
|
||||||
|
$table->boolean('telegram_notifications_status_changes')->default(true);
|
||||||
|
$table->boolean('telegram_notifications_database_backups')->default(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('teams', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('telegram_enabled');
|
||||||
|
$table->dropColumn('telegram_token');
|
||||||
|
$table->dropColumn('telegram_chat_id');
|
||||||
|
$table->dropColumn('telegram_notifications_test');
|
||||||
|
$table->dropColumn('telegram_notifications_deployments');
|
||||||
|
$table->dropColumn('telegram_notifications_status_changes');
|
||||||
|
$table->dropColumn('telegram_notifications_database_backups');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
@ -5,8 +5,8 @@
|
|||||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="absolute hidden text-xs rounded group-hover:block border-coolgray-400 bg-coolgray-500">
|
<div class="absolute z-40 hidden text-xs rounded group-hover:block border-coolgray-400 bg-coolgray-500">
|
||||||
<div class="p-4 card-body">
|
<div class="p-4">
|
||||||
{!! $helper !!}
|
{!! $helper !!}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<x-forms.button type="submit">
|
<x-forms.button type="submit">
|
||||||
Save
|
Save
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@if ($model->discord_enabled)
|
@if ($team->discord_enabled)
|
||||||
<x-forms.button class="text-white normal-case btn btn-xs no-animation btn-primary"
|
<x-forms.button class="text-white normal-case btn btn-xs no-animation btn-primary"
|
||||||
wire:click="sendTestNotification">
|
wire:click="sendTestNotification">
|
||||||
Send Test Notifications
|
Send Test Notifications
|
||||||
@ -13,26 +13,26 @@
|
|||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="w-48">
|
<div class="w-48">
|
||||||
<x-forms.checkbox instantSave id="model.discord_enabled" label="Notification Enabled" />
|
<x-forms.checkbox instantSave id="team.discord_enabled" label="Notification Enabled" />
|
||||||
</div>
|
</div>
|
||||||
<x-forms.input type="password"
|
<x-forms.input type="password"
|
||||||
helper="Generate a webhook in Discord.<br>Example: https://discord.com/api/webhooks/...." required
|
helper="Generate a webhook in Discord.<br>Example: https://discord.com/api/webhooks/...." required
|
||||||
id="model.discord_webhook_url" label="Webhook" />
|
id="team.discord_webhook_url" label="Webhook" />
|
||||||
</form>
|
</form>
|
||||||
@if (data_get($model, 'discord_enabled'))
|
@if (data_get($team, 'discord_enabled'))
|
||||||
<h3 class="mt-4">Subscribe to events</h3>
|
<h3 class="mt-4">Subscribe to events</h3>
|
||||||
<div class="w-64">
|
<div class="w-64">
|
||||||
@if (isDev())
|
@if (isDev())
|
||||||
<x-forms.checkbox instantSave="saveModel" id="model.discord_notifications_test" label="Test" />
|
<x-forms.checkbox instantSave="saveModel" id="team.discord_notifications_test" label="Test" />
|
||||||
@endif
|
@endif
|
||||||
<h4 class="mt-4">General</h4>
|
<h4 class="mt-4">General</h4>
|
||||||
<x-forms.checkbox instantSave="saveModel" id="model.discord_notifications_status_changes"
|
<x-forms.checkbox instantSave="saveModel" id="team.discord_notifications_status_changes"
|
||||||
label="Container Status Changes" />
|
label="Container Status Changes" />
|
||||||
<h4 class="mt-4">Applications</h4>
|
<h4 class="mt-4">Applications</h4>
|
||||||
<x-forms.checkbox instantSave="saveModel" id="model.discord_notifications_deployments"
|
<x-forms.checkbox instantSave="saveModel" id="team.discord_notifications_deployments"
|
||||||
label="Deployments" />
|
label="Deployments" />
|
||||||
<h4 class="mt-4">Databases</h4>
|
<h4 class="mt-4">Databases</h4>
|
||||||
<x-forms.checkbox instantSave="saveModel" id="model.discord_notifications_database_backups"
|
<x-forms.checkbox instantSave="saveModel" id="team.discord_notifications_database_backups"
|
||||||
label="Backup Statuses" />
|
label="Backup Statuses" />
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
<div>
|
||||||
|
<form wire:submit.prevent='submit' class="flex flex-col">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<h2>Telegram</h2>
|
||||||
|
<x-forms.button type="submit">
|
||||||
|
Save
|
||||||
|
</x-forms.button>
|
||||||
|
@if ($team->telegram_enabled)
|
||||||
|
<x-forms.button class="text-white normal-case btn btn-xs no-animation btn-primary"
|
||||||
|
wire:click="sendTestNotification">
|
||||||
|
Send Test Notifications
|
||||||
|
</x-forms.button>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div class="w-48">
|
||||||
|
<x-forms.checkbox instantSave id="team.telegram_enabled" label="Notification Enabled" />
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<x-forms.input type="password" helper="Get it from the <a class='inline-block text-white underline' href='https://t.me/botfather' target='_blank'>BotFather Bot</a> on Telegram." required
|
||||||
|
id="team.telegram_token" label="Token" />
|
||||||
|
<x-forms.input type="password" helper="Recommended to add your bot to a group chat and add its Chat ID here." required
|
||||||
|
id="team.telegram_chat_id" label="Chat ID" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
@if (data_get($team, 'telegram_enabled'))
|
||||||
|
<h3 class="mt-4">Subscribe to events</h3>
|
||||||
|
<div class="w-64">
|
||||||
|
@if (isDev())
|
||||||
|
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_test" label="Test" />
|
||||||
|
@endif
|
||||||
|
<h4 class="mt-4">General</h4>
|
||||||
|
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_status_changes"
|
||||||
|
label="Container Status Changes" />
|
||||||
|
<h4 class="mt-4">Applications</h4>
|
||||||
|
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_deployments"
|
||||||
|
label="Deployments" />
|
||||||
|
<h4 class="mt-4">Databases</h4>
|
||||||
|
<x-forms.checkbox instantSave="saveModel" id="team.telegram_notifications_database_backups"
|
||||||
|
label="Backup Statuses" />
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
@ -1,23 +1,25 @@
|
|||||||
<x-layout>
|
<x-layout>
|
||||||
<x-team.navbar/>
|
<x-team.navbar />
|
||||||
<h2 class="pb-4">Notifications</h2>
|
<h2 class="pb-4">Notifications</h2>
|
||||||
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'email' }" class="flex h-full">
|
<div x-data="{ activeTab: window.location.hash ? window.location.hash.substring(1) : 'email' }" class="flex h-full">
|
||||||
<div class="flex flex-col gap-4 min-w-fit">
|
<div class="flex flex-col gap-4 min-w-fit">
|
||||||
<a :class="activeTab === 'email' && 'text-white'"
|
<a :class="activeTab === 'email' && 'text-white'"
|
||||||
@click.prevent="activeTab = 'email'; window.location.hash = 'email'" href="#">Email</a>
|
@click.prevent="activeTab = 'email'; window.location.hash = 'email'" href="#">Email</a>
|
||||||
|
<a :class="activeTab === 'Telegram' && 'text-white'"
|
||||||
|
@click.prevent="activeTab = 'telegram'; window.location.hash = 'telegram'" href="#">Telegram</a>
|
||||||
<a :class="activeTab === 'discord' && 'text-white'"
|
<a :class="activeTab === 'discord' && 'text-white'"
|
||||||
@click.prevent="activeTab = 'discord'; window.location.hash = 'discord'" href="#">Discord</a>
|
@click.prevent="activeTab = 'discord'; window.location.hash = 'discord'" href="#">Discord</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full pl-8">
|
<div class="w-full pl-8">
|
||||||
<div x-cloak x-show="activeTab === 'email'" class="h-full">
|
<div x-cloak x-show="activeTab === 'email'" class="h-full">
|
||||||
<livewire:notifications.email-settings :team="auth()
|
<livewire:notifications.email-settings />
|
||||||
->user()
|
</div>
|
||||||
->currentTeam()" />
|
<div x-cloak x-show="activeTab === 'telegram'" class="h-full">
|
||||||
|
<livewire:notifications.telegram-settings />
|
||||||
</div>
|
</div>
|
||||||
<div x-cloak x-show="activeTab === 'discord'">
|
<div x-cloak x-show="activeTab === 'discord'">
|
||||||
<livewire:notifications.discord-settings :model="auth()
|
<livewire:notifications.discord-settings />
|
||||||
->user()
|
|
||||||
->currentTeam()" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</x-layout>
|
</x-layout>
|
||||||
|
Loading…
Reference in New Issue
Block a user