Files
Paymenter-Version-Tracks/extensions/Others/DiscordNotifications/DiscordNotifications.php
Muhammad Tamir b3933b9960 v1.4.0
2025-11-14 10:59:24 +07:00

376 lines
12 KiB
PHP

<?php
namespace Paymenter\Extensions\Others\DiscordNotifications;
use App\Admin\Resources\InvoiceResource;
use App\Admin\Resources\OrderResource;
use App\Admin\Resources\ServiceResource;
use App\Admin\Resources\TicketResource;
use App\Admin\Resources\UserResource;
use App\Classes\Extension\Extension;
use App\Events\Invoice;
use App\Events\Invoice\Paid;
use App\Events\Order\Finalized;
use App\Events\Order\Updated;
use App\Events\Service;
use App\Events\Ticket;
use App\Events\TicketMessage;
use App\Events\User;
use App\Events\User\Created;
use App\Models\Order;
use Exception;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Http;
class DiscordNotifications extends Extension
{
private const events = [
'Order Created' => Finalized::class,
'Order Updated' => Updated::class,
'User Created' => Created::class,
'User Updated' => User\Updated::class,
'Invoice Created' => Invoice\Finalized::class,
'Invoice Updated' => Invoice\Updated::class,
'Invoice Paid' => Paid::class,
'Ticket Created' => Ticket\Created::class,
'Ticket Updated' => Ticket\Updated::class,
'Ticket Replied' => TicketMessage\Created::class,
'Service Created' => Service\Created::class,
'Service Updated' => Service\Updated::class,
];
/**
* Get all the configuration for the extension
*
* @param array $values
* @return array
*/
public function getConfig($values = [])
{
return [
[
'name' => 'webhook_url',
'type' => 'text',
'label' => 'Webhook URL',
'required' => true,
],
[
'name' => 'ping_type',
'type' => 'select',
'label' => 'Ping Type',
'description' => 'The type of user/role to ping',
'required' => false,
'options' => [
[
'label' => 'None',
'value' => 'none',
],
[
'label' => 'User',
'value' => 'user',
],
[
'label' => 'Role',
'value' => 'role',
],
],
],
[
'name' => 'ping_id',
'type' => 'text',
'label' => 'Ping ID',
'description' => 'The ID of the user/role to ping',
'required' => false,
],
[
'name' => 'events',
'type' => 'select',
'multiple' => true,
'label' => 'Events',
'description' => 'The events to send notifications for',
'required' => true,
'database_type' => 'array',
'options' => array_keys(self::events),
],
];
}
public function boot()
{
foreach ($this->config('events') as $eventType) {
if (in_array($eventType, array_keys(self::events))) {
Event::listen(
self::events[$eventType],
function ($event) use ($eventType) {
try {
$this->sendNotification($event, $eventType);
} catch (Exception $e) {
// Log the error
if (config('settings.debug', false)) {
throw $e;
}
}
}
);
}
}
}
private function updatedEvent($event, $model)
{
$changedFields = [];
$changedFields[] = [
'name' => 'ID',
'value' => $model === 'user'
? $event->{$model}->name
: $this->mapId($event->{$model}->id, $model . '_id'),
'inline' => true,
];
foreach ($event->{$model}->getChanges() as $field => $value) {
if (!in_array($field, ['created_at', 'updated_at', 'password', 'remember_token'])) {
if (!is_string($value)) {
$value = json_encode($value);
}
// Replace _ with space and capitalize the first letter
$changedFields[] = [
'name' => ucfirst(str_replace('_', ' ', $field)),
'value' => $event->{$model}->getOriginal($field) . ' -> ' . $value,
'inline' => true,
];
}
if ($field === 'password') {
$changedFields[] = [
'name' => 'Password',
'value' => '********',
'inline' => true,
];
}
}
return $changedFields;
}
private function createdEvent($event, $model, $fields = [])
{
if (count($fields) === 0) {
return [];
}
$efields = [];
foreach ($fields as $value) {
$name = ucfirst(str_replace('_', ' ', $value));
if (str_contains($value, '_id') || $value === 'id') {
if ($value === 'id') {
$value = $this->mapId($event->{$model}->{$value}, $model . '_id');
} elseif ($value === 'user_id') {
$value = '[User ' . $event->{$model}->user->name . '](' . UserResource::getUrl('edit', ['record' => $event->{$model}->user_id]) . ')';
} else {
$value = $this->mapId($event->{$model}->{$value}, $value);
}
} else {
$value = $event->{$model}->{$value};
}
// Replace _ with space and capitalize the first letter
$efields[] = [
'name' => $name,
'value' => (string) $value,
'inline' => true,
];
}
return $efields;
}
private function mapId($id, $name)
{
// Get the route name for the model
$resources = [
'order_id' => OrderResource::class,
'invoice_id' => InvoiceResource::class,
'ticket_id' => TicketResource::class,
'service_id' => ServiceResource::class,
];
// Check if the name is in the resources array
if (array_key_exists($name, $resources)) {
return '[' . ucfirst($resources[$name]::getModelLabel()) . ' #' . $id . '](' . $resources[$name]::getUrl('edit', ['record' => $id]) . ')';
}
// If no route name is found, return the ID
return $id;
}
private function sendNotification($event, $eventType)
{
$fields = [
'User Updated' => fn ($event) => $this->updatedEvent($event, 'user'),
'User Created' => fn ($event) => $this->userCreated($event->user),
'Order Created' => fn ($event) => $this->orderCreated($event->order),
'Order Updated' => fn ($event) => $this->updatedEvent($event, 'order'),
'Invoice Created' => fn ($event) => $this->invoiceCreated($event->invoice),
'Invoice Updated' => fn ($event) => $this->updatedEvent($event, 'invoice'),
'Invoice Paid' => fn ($event) => $this->createdEvent($event, 'invoice', ['id']),
'Ticket Created' => fn ($event) => $this->createdEvent($event, 'ticket', ['id', 'user_id']),
'Ticket Updated' => fn ($event) => $this->updatedEvent($event, 'ticket'),
'Ticket Replied' => fn ($event) => $this->createdEvent($event, 'ticketMessage', ['ticket_id', 'message']),
'Service Created' => fn ($event) => $this->serviceCreated($event->service),
'Service Updated' => fn ($event) => $this->updatedEvent($event, 'service'),
];
// Check if the event type is valid
if (!array_key_exists($eventType, $fields)) {
return;
}
$fields = $fields[$eventType]($event);
$webhookUrl = $this->config('webhook_url');
$pingType = $this->config('ping_type');
$pingId = $this->config('ping_id');
$message = [
'content' => ' ',
'embeds' => [
[
'title' => ucfirst(str_replace('_', ' ', $eventType)),
'description' => "A new event has occurred: {$eventType}",
'fields' => $fields,
'color' => 0x00FF00,
'footer' => [
'text' => 'Paymenter Notifications',
],
'timestamp' => now()->toIso8601String(),
],
],
];
if ($pingType !== 'none' && $pingId) {
$message['content'] = ($pingType === 'user') ? "<@{$pingId}>" : "<@&{$pingId}>";
}
// Send the notification to Discord
Http::post($webhookUrl, $message)->json();
}
private function userCreated(\App\Models\User $user)
{
$fields = [
[
'name' => 'ID',
'value' => $this->mapId($user->id, 'user_id'),
'inline' => true,
],
[
'name' => 'Name',
'value' => $user->name,
'inline' => true,
],
[
'name' => 'Email',
'value' => $user->email,
'inline' => true,
],
];
// Add properties
foreach ($user->properties as $key => $value) {
$fields[] = [
'name' => ucfirst(str_replace('_', ' ', $key)),
'value' => (string) $value,
'inline' => true,
];
}
return $fields;
}
private function orderCreated(Order $order)
{
$fields = [
[
'name' => 'ID',
'value' => $this->mapId($order->id, 'order_id'),
'inline' => true,
],
[
'name' => 'User',
'value' => '[User ' . $order->user->name . '](' . UserResource::getUrl('edit', ['record' => $order->user_id]) . ')',
'inline' => true,
],
[
'name' => 'Total',
'value' => (string) $order->formattedTotal,
'inline' => true,
],
];
foreach ($order->services as $service) {
$fields[] = [
'name' => $service->product->name,
'value' => '[Service #' . $service->id . '](' . ServiceResource::getUrl('edit', ['record' => $service->id]) . ') (' . $service->formattedPrice . ')',
'inline' => true,
];
}
return $fields;
}
private function serviceCreated(\App\Models\Service $service)
{
$fields = [
[
'name' => 'ID',
'value' => $this->mapId($service->id, 'service_id'),
'inline' => true,
],
[
'name' => 'User',
'value' => '[User ' . $service->user->name . '](' . UserResource::getUrl('edit', ['record' => $service->user_id]) . ')',
'inline' => true,
],
[
'name' => 'Product',
'value' => $service->product->name,
'inline' => true,
],
[
'name' => 'Price',
'value' => (string) $service->formattedPrice,
'inline' => true,
],
];
return $fields;
}
private function invoiceCreated(\App\Models\Invoice $invoice)
{
$fields = [
[
'name' => 'ID',
'value' => $this->mapId($invoice->id, 'invoice_id'),
'inline' => true,
],
[
'name' => 'User',
'value' => '[User ' . $invoice->user->name . '](' . UserResource::getUrl('edit', ['record' => $invoice->user_id]) . ')',
'inline' => true,
],
[
'name' => 'Total',
'value' => (string) $invoice->formattedTotal,
'inline' => true,
],
];
foreach ($invoice->items as $item) {
$fields[] = [
'name' => $item->description,
'value' => $item->quantity . ' x ' . $item->formattedPrice,
'inline' => true,
];
}
return $fields;
}
}