mirror of
https://gitlab.com/ArkHost/WHMCS-Discord-Notifications.git
synced 2026-07-23 23:35:55 +02:00
Add files via upload
This commit is contained in:
@@ -0,0 +1,274 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace WHMCS\Module\Notification\Discord;
|
||||||
|
|
||||||
|
use WHMCS\Module\Contracts\NotificationModuleInterface;
|
||||||
|
use WHMCS\Module\Notification\DescriptionTrait;
|
||||||
|
use WHMCS\Notification\Contracts\NotificationInterface;
|
||||||
|
use WHMCS\Config\Setting;
|
||||||
|
use WHMCS\Exception;
|
||||||
|
|
||||||
|
// Include the required classes
|
||||||
|
require_once __DIR__ . '/lib/Embed.php';
|
||||||
|
require_once __DIR__ . '/lib/Field.php';
|
||||||
|
require_once __DIR__ . '/lib/Message.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification module for delivering notifications via Discord
|
||||||
|
*/
|
||||||
|
class Discord implements NotificationModuleInterface
|
||||||
|
{
|
||||||
|
use DescriptionTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->setDisplayName('Discord')
|
||||||
|
->setLogoFileName('logo.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Settings required for module configuration
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function settings()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'webhookURL' => [
|
||||||
|
'FriendlyName' => 'Discord Webhook URL',
|
||||||
|
'Type' => 'text',
|
||||||
|
'Description' => 'The Discord Webhook URL that notifications should be sent to.',
|
||||||
|
],
|
||||||
|
'companyName' => [
|
||||||
|
'FriendlyName' => 'Company Name (Optional)',
|
||||||
|
'Type' => 'text',
|
||||||
|
'Description' => 'This will be the name of the user that sends the message in the Discord channel.',
|
||||||
|
'Placeholder' => Setting::getValue('CompanyName'),
|
||||||
|
],
|
||||||
|
'discordColor' => [
|
||||||
|
'FriendlyName' => 'Message Colour (Optional)',
|
||||||
|
'Type' => 'text',
|
||||||
|
'Description' => 'This is the side line colour for the message. The color code format within this script is standard hex. Exclude the beginning # character if one is present.',
|
||||||
|
'Placeholder' => '73CB0B',
|
||||||
|
],
|
||||||
|
'discordGroupID' => [
|
||||||
|
'FriendlyName' => 'Notification Role ID (Optional)',
|
||||||
|
'Type' => 'text',
|
||||||
|
'Description' => 'If you\'d like to have a specific group pinged on each message, please place the ID here. An example of a group ID is: 343029528563548162 (Only include the numerical ID and no other formatting)',
|
||||||
|
],
|
||||||
|
'discordWebHookAvatar' => [
|
||||||
|
'FriendlyName' => 'Webhook Avatar Image (Optional)',
|
||||||
|
'Type' => 'text',
|
||||||
|
'Description' => 'Your desired Webhook Avatar. Please make sure you enter a direct link to the image (E.G. https://example.com/avatar.png )',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate settings for notification module
|
||||||
|
*
|
||||||
|
* This method will be invoked prior to saving any settings via the UI.
|
||||||
|
*
|
||||||
|
* @param array $settings Settings present in configuration modal
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
* @throws Exception webhookURL value is missing
|
||||||
|
*/
|
||||||
|
public function testConnection($settings): bool
|
||||||
|
{
|
||||||
|
$webhookURL = $settings['webhookURL'] ?? '';
|
||||||
|
|
||||||
|
if (empty($webhookURL)) {
|
||||||
|
throw new Exception("You must provide a webhook URL");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate webhook URL format
|
||||||
|
if (!filter_var($webhookURL, FILTER_VALIDATE_URL) ||
|
||||||
|
!preg_match('/^https:\/\/discord(app)?\.com\/api\/webhooks\//', $webhookURL)) {
|
||||||
|
throw new Exception("Invalid Discord webhook URL format");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The individual customisable settings for a notification.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function notificationSettings()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'message' => [
|
||||||
|
'FriendlyName' => 'Customise Message (Optional)',
|
||||||
|
'Type' => 'text',
|
||||||
|
'Description' => 'Allows you to customise the primary display message shown in the notification.',
|
||||||
|
],
|
||||||
|
'webhookURL' => [
|
||||||
|
'FriendlyName' => 'Discord Webhook URL (Optional)',
|
||||||
|
'Type' => 'text',
|
||||||
|
'Description' => 'Override the Discord Webhook URL for this notification to send to a different channel.',
|
||||||
|
],
|
||||||
|
'discordColor' => [
|
||||||
|
'FriendlyName' => 'Message Colour (Optional)',
|
||||||
|
'Type' => 'text',
|
||||||
|
'Description' => 'Override the side line colour for the message. The color code format within this script is standard hex. Exclude the beginning # character if one is present.',
|
||||||
|
'Placeholder' => '73CB0B',
|
||||||
|
],
|
||||||
|
'discordGroupID' => [
|
||||||
|
'FriendlyName' => 'Notification Role ID (Optional)',
|
||||||
|
'Type' => 'text',
|
||||||
|
'Description' => 'If you\'d like to have a specific group pinged for this message type, please place the ID here (this is in addition to the module setting). An example of a group ID is: 343029528563548162 (Only include the numerical ID and no other formatting)',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The option values available for a 'dynamic' Type notification setting
|
||||||
|
*/
|
||||||
|
public function getDynamicField($fieldName, $settings)
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deliver notification
|
||||||
|
*
|
||||||
|
* This method is invoked when rule criteria are met.
|
||||||
|
*
|
||||||
|
* @param NotificationInterface $notification A notification to send
|
||||||
|
* @param array $moduleSettings Configured settings of the notification module
|
||||||
|
* @param array $notificationSettings Configured notification settings set by the triggered rule
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function sendNotification(NotificationInterface $notification, $moduleSettings, $notificationSettings)
|
||||||
|
{
|
||||||
|
// Determine message body
|
||||||
|
$messageBody = $notification->getMessage();
|
||||||
|
if (!empty($notificationSettings["message"])) {
|
||||||
|
$messageBody = $notificationSettings["message"];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine webhook URL
|
||||||
|
if (!empty($notificationSettings["webhookURL"])) {
|
||||||
|
$webhookURL = $notificationSettings["webhookURL"];
|
||||||
|
} else {
|
||||||
|
$webhookURL = $moduleSettings["webhookURL"];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine color
|
||||||
|
if (!empty($notificationSettings["discordColor"])) {
|
||||||
|
$discordColor = hexdec($notificationSettings["discordColor"]);
|
||||||
|
} elseif (!empty($moduleSettings["discordColor"])) {
|
||||||
|
$discordColor = hexdec($moduleSettings["discordColor"]);
|
||||||
|
} else {
|
||||||
|
$discordColor = 7588619; // Default blue color
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create embed
|
||||||
|
$embed = (new Embed())
|
||||||
|
->title(\WHMCS\Input\Sanitize::decode($notification->getTitle()))
|
||||||
|
->url($notification->getUrl())
|
||||||
|
->description($messageBody)
|
||||||
|
->timestamp(date(\DateTime::ISO8601))
|
||||||
|
->color($discordColor)
|
||||||
|
->footer("WHMCS Notification");
|
||||||
|
|
||||||
|
// Add notification attributes as fields
|
||||||
|
foreach ($notification->getAttributes() as $attribute) {
|
||||||
|
$value = $attribute->getValue();
|
||||||
|
if ($attribute->getUrl()) {
|
||||||
|
$value = "[" . $value . "](" . $attribute->getUrl() . ")";
|
||||||
|
}
|
||||||
|
$embed->addField((new Field())
|
||||||
|
->name($attribute->getLabel())
|
||||||
|
->value($value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare role mentions
|
||||||
|
$discordGroupID = '';
|
||||||
|
$discordGroupIDNotificationSpecific = '';
|
||||||
|
|
||||||
|
if (!empty($moduleSettings["discordGroupID"])) {
|
||||||
|
$discordGroupID = "<@&" . $moduleSettings["discordGroupID"] . ">";
|
||||||
|
}
|
||||||
|
if (!empty($notificationSettings["discordGroupID"])) {
|
||||||
|
$discordGroupIDNotificationSpecific = "<@&" . $notificationSettings["discordGroupID"] . ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare other settings
|
||||||
|
$companyName = $moduleSettings["companyName"] ?? '';
|
||||||
|
$discordWebHookAvatar = $moduleSettings["discordWebHookAvatar"] ?? '';
|
||||||
|
|
||||||
|
// Create message
|
||||||
|
$message = (new Message())
|
||||||
|
->content($discordGroupID . $discordGroupIDNotificationSpecific)
|
||||||
|
->username($companyName)
|
||||||
|
->avatarUrl($discordWebHookAvatar)
|
||||||
|
->embed($embed);
|
||||||
|
|
||||||
|
// Send the notification
|
||||||
|
$this->call($moduleSettings, $webhookURL, $message->toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send HTTP request to Discord webhook
|
||||||
|
*
|
||||||
|
* @param array $settings Module settings
|
||||||
|
* @param string $url Webhook URL
|
||||||
|
* @param array $postdata Data to send
|
||||||
|
* @param bool $throwOnError Whether to throw exception on error
|
||||||
|
* @return mixed
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function call(array $settings, $url, array $postdata = array(), $throwOnError = true)
|
||||||
|
{
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $url);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, 1);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
|
||||||
|
'Content-Type: application/json'
|
||||||
|
));
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($postdata));
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
|
||||||
|
curl_setopt($ch, CURLOPT_USERAGENT, 'WHMCS Discord Notification Module');
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
$curlError = curl_error($ch);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
// Handle cURL errors
|
||||||
|
if ($response === false || !empty($curlError)) {
|
||||||
|
logModuleCall('discord', 'Notification Sending Failed - cURL Error', $postdata, $curlError, null);
|
||||||
|
if ($throwOnError) {
|
||||||
|
throw new Exception("cURL Error: " . $curlError);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$decoded = json_decode($response, true);
|
||||||
|
|
||||||
|
// Discord webhook returns 204 on success
|
||||||
|
if ($httpCode == 204) {
|
||||||
|
logModuleCall('discord', 'Notification Successfully Sent', $postdata, $response, $decoded);
|
||||||
|
} else {
|
||||||
|
logModuleCall('discord', 'Notification Sending Failed', $postdata, $response, $decoded);
|
||||||
|
if ($throwOnError) {
|
||||||
|
$errorMessage = "HTTP Error " . $httpCode;
|
||||||
|
if (isset($decoded['message'])) {
|
||||||
|
$errorMessage .= ": " . $decoded['message'];
|
||||||
|
}
|
||||||
|
throw new Exception($errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $decoded;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace WHMCS\Module\Notification\Discord;
|
||||||
|
|
||||||
|
class Embed
|
||||||
|
{
|
||||||
|
public $title = "";
|
||||||
|
public $url = "";
|
||||||
|
public $description = "";
|
||||||
|
public $fields = null;
|
||||||
|
public $timestamp = "";
|
||||||
|
public $color = "";
|
||||||
|
public $footer = array();
|
||||||
|
|
||||||
|
public function title($title)
|
||||||
|
{
|
||||||
|
$this->title = trim($title);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url($url)
|
||||||
|
{
|
||||||
|
$this->url = trim($url);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function description($description)
|
||||||
|
{
|
||||||
|
$this->description = trim($description);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function timestamp($timestamp)
|
||||||
|
{
|
||||||
|
$this->timestamp = trim($timestamp);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function color($color)
|
||||||
|
{
|
||||||
|
$this->color = $color;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function footer($footertext, $icon = "")
|
||||||
|
{
|
||||||
|
$this->footer["text"] = trim($footertext);
|
||||||
|
if (!empty($icon)) {
|
||||||
|
$this->footer["icon_url"] = trim($icon);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addField(Field $field)
|
||||||
|
{
|
||||||
|
if ($this->fields === null) {
|
||||||
|
$this->fields = array();
|
||||||
|
}
|
||||||
|
$this->fields[] = $field;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray()
|
||||||
|
{
|
||||||
|
$embed = [];
|
||||||
|
|
||||||
|
if (!empty($this->title)) {
|
||||||
|
$embed["title"] = $this->title;
|
||||||
|
}
|
||||||
|
if (!empty($this->url)) {
|
||||||
|
$embed["url"] = $this->url;
|
||||||
|
}
|
||||||
|
if (!empty($this->description)) {
|
||||||
|
$embed["description"] = $this->description;
|
||||||
|
}
|
||||||
|
if (!empty($this->timestamp)) {
|
||||||
|
$embed["timestamp"] = $this->timestamp;
|
||||||
|
}
|
||||||
|
if (!empty($this->color)) {
|
||||||
|
$embed["color"] = $this->color;
|
||||||
|
}
|
||||||
|
if (!empty($this->footer)) {
|
||||||
|
$embed["footer"] = $this->footer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($this->fields)) {
|
||||||
|
$embed["fields"] = array_map(function (Field $field) {
|
||||||
|
return $field->toArray();
|
||||||
|
}, $this->fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $embed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace WHMCS\Module\Notification\Discord;
|
||||||
|
|
||||||
|
class Field
|
||||||
|
{
|
||||||
|
public $name = "";
|
||||||
|
public $value = "";
|
||||||
|
public $inline = true;
|
||||||
|
|
||||||
|
public function name($name)
|
||||||
|
{
|
||||||
|
$this->name = trim($name);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function value($value)
|
||||||
|
{
|
||||||
|
$this->value = trim($value);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function inline($inline)
|
||||||
|
{
|
||||||
|
$this->inline = (bool) $inline;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => $this->name,
|
||||||
|
'value' => $this->value,
|
||||||
|
'inline' => $this->inline
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace WHMCS\Module\Notification\Discord;
|
||||||
|
|
||||||
|
class Message
|
||||||
|
{
|
||||||
|
public $content = "";
|
||||||
|
public $username = "";
|
||||||
|
public $avatarUrl = "";
|
||||||
|
public $embeds = array();
|
||||||
|
|
||||||
|
public function content($content)
|
||||||
|
{
|
||||||
|
$this->content = trim($content);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function username($username)
|
||||||
|
{
|
||||||
|
$this->username = trim($username);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function avatarUrl($avatarUrl)
|
||||||
|
{
|
||||||
|
$this->avatarUrl = trim($avatarUrl);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function embed($embed)
|
||||||
|
{
|
||||||
|
$this->embeds[] = $embed;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toArray()
|
||||||
|
{
|
||||||
|
$message = array();
|
||||||
|
|
||||||
|
if (!empty($this->content)) {
|
||||||
|
$message["content"] = $this->content;
|
||||||
|
}
|
||||||
|
if (!empty($this->avatarUrl)) {
|
||||||
|
$message["avatar_url"] = $this->avatarUrl;
|
||||||
|
}
|
||||||
|
if (!empty($this->username)) {
|
||||||
|
$message["username"] = $this->username;
|
||||||
|
}
|
||||||
|
if (!empty($this->embeds)) {
|
||||||
|
$message["embeds"] = array_map(function($embed) {
|
||||||
|
return $embed instanceof Embed ? $embed->toArray() : $embed;
|
||||||
|
}, $this->embeds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $message;
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"schema": "1.0",
|
||||||
|
"type": "whmcs-notifications",
|
||||||
|
"name": "discord",
|
||||||
|
"license": "proprietary",
|
||||||
|
"category": "notifications",
|
||||||
|
"description": {
|
||||||
|
"name": "Discord",
|
||||||
|
"tagline": "Send notifications to your Discord Server.",
|
||||||
|
"long": "Get real-time notifications directly via Discord.\n\nCreate powerful event driven notification rules that help keep you and your team in the loop using a wide range of event triggers and conditional criteria.",
|
||||||
|
"features": [
|
||||||
|
"Event driven notifications",
|
||||||
|
"Direct into a Discord Channel"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"logo": {
|
||||||
|
"filename": "logo.png"
|
||||||
|
},
|
||||||
|
"support": {
|
||||||
|
"homepage": "https://docs.whmcs.com/Notification_Modules"
|
||||||
|
},
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "WHMCS Development Team",
|
||||||
|
"homepage": "https://www.whmcs.com/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user