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