Add files via upload

This commit is contained in:
ArkHost
2025-05-31 00:44:18 +02:00
committed by GitHub
commit 0b742d0e39
6 changed files with 494 additions and 0 deletions
+274
View File
@@ -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

+28
View File
@@ -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/"
}
]
}