513 lines
16 KiB
PHP
513 lines
16 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Services;
|
||
|
||
use App\Models\Setting;
|
||
use Carbon\Carbon;
|
||
use Illuminate\Support\Facades\Http;
|
||
use Illuminate\Support\Facades\Log;
|
||
|
||
class Discord
|
||
{
|
||
protected ?string $token;
|
||
protected ?string $group;
|
||
|
||
// Discord embed colors
|
||
const COLOR_SUCCESS = 3066993; // Green
|
||
const COLOR_ERROR = 15158332; // Red
|
||
const COLOR_INFO = 3447003; // Blue
|
||
const COLOR_WARNING = 16776960; // Yellow
|
||
const COLOR_VIP = 16711935; // Purple
|
||
|
||
public function __construct()
|
||
{
|
||
$record = Setting::where('key', 'setting')->first();
|
||
if ($record) {
|
||
$setting = json_decode($record->value);
|
||
$this->token = $setting->discord_webhook_id ?? null;
|
||
$this->group = $setting->discord_webhook_token ?? null;
|
||
} else {
|
||
$this->token = null;
|
||
$this->group = null;
|
||
}
|
||
}
|
||
|
||
protected function sendDiscordMessage(array $payload)
|
||
{
|
||
if (!$this->token || !$this->group) {
|
||
Log::channel('Custom')->warning('Discord notification not sent: Missing webhook ID or token');
|
||
return null;
|
||
}
|
||
|
||
$webhookUrl = "https://discord.com/api/webhooks/{$this->token}/{$this->group}";
|
||
|
||
try {
|
||
return Http::post($webhookUrl, $payload);
|
||
} catch (\Exception $exception) {
|
||
Log::channel('Custom')->error('Failed to send Discord notification: ' . $exception->getMessage());
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Send simple message with embed
|
||
*/
|
||
public function SendMessage($message = '', $color = self::COLOR_INFO, $title = null)
|
||
{
|
||
$embed = [
|
||
'description' => $message,
|
||
'color' => $color,
|
||
'timestamp' => now()->toIso8601String(),
|
||
];
|
||
|
||
if ($title) {
|
||
$embed['title'] = $title;
|
||
}
|
||
|
||
$payload = [
|
||
'embeds' => [$embed]
|
||
];
|
||
|
||
return $this->sendDiscordMessage($payload);
|
||
}
|
||
|
||
/**
|
||
* Send error message with embed
|
||
*/
|
||
public function NotifyDiscordFailure($reason, $data = null)
|
||
{
|
||
$fields = [
|
||
[
|
||
'name' => '🚫 Lý do lỗi',
|
||
'value' => $reason,
|
||
'inline' => false,
|
||
],
|
||
];
|
||
|
||
if ($data) {
|
||
$fields[] = [
|
||
'name' => '📄 Dữ liệu liên quan',
|
||
'value' => "```json\n" . substr(json_encode($data, JSON_PRETTY_PRINT), 0, 1000) . "\n```",
|
||
'inline' => false,
|
||
];
|
||
}
|
||
|
||
$embed = [
|
||
'title' => '🔴 Gửi Discord thất bại',
|
||
'color' => self::COLOR_ERROR,
|
||
'fields' => $fields,
|
||
'timestamp' => now()->toIso8601String(),
|
||
];
|
||
|
||
Log::channel('Custom')->error('NotifyDiscordFailure: ' . $reason, ['data' => $data]);
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* Send VPS information with enhanced embed
|
||
*/
|
||
public function SendInformation($product = [], $user = '', $password = '', $port = 22)
|
||
{
|
||
$fields = [
|
||
[
|
||
'name' => '🌐 Địa chỉ IP',
|
||
'value' => isset($product->IpAddress->ip) ? $product->IpAddress->ip : 'N/A',
|
||
'inline' => true,
|
||
],
|
||
[
|
||
'name' => '👤 Tài khoản',
|
||
'value' => $user ?: 'root',
|
||
'inline' => true,
|
||
],
|
||
[
|
||
'name' => '🔌 Cổng SSH',
|
||
'value' => (string) $port,
|
||
'inline' => true,
|
||
],
|
||
[
|
||
'name' => '🔐 Mật khẩu',
|
||
'value' => "||{$password}||", // Spoiler text
|
||
'inline' => true,
|
||
],
|
||
[
|
||
'name' => '⏰ Hết hạn',
|
||
'value' => isset($product->next_due_date) ?
|
||
Carbon::parse($product->next_due_date)->format('d/m/Y H:i') : 'Không giới hạn',
|
||
'inline' => true,
|
||
],
|
||
[
|
||
'name' => '💻 Hệ điều hành',
|
||
'value' => (isset($product->Os->OsGroup->name) ? $product->Os->OsGroup->name : '') . ' ' . (isset($product->Os->version) ? $product->Os->version : ''),
|
||
'inline' => true,
|
||
],
|
||
];
|
||
|
||
// Add SSH command
|
||
$ipAddress = isset($product->IpAddress->ip) ? $product->IpAddress->ip : 'IP_ADDRESS';
|
||
$sshCommand = "ssh {$user}@{$ipAddress}";
|
||
if ($port != 22) {
|
||
$sshCommand .= " -p {$port}";
|
||
}
|
||
|
||
$embed = [
|
||
'title' => '🎉 VPS đã được khởi tạo thành công!',
|
||
'description' => "**Đơn hàng:** #" . (isset($product->order_id) ? $product->order_id : '') . "\n**SSH Command:**\n```bash\n{$sshCommand}\n```",
|
||
'color' => self::COLOR_SUCCESS,
|
||
'fields' => $fields,
|
||
'footer' => [
|
||
'text' => 'VPS Management System',
|
||
'icon_url' => 'https://cdn-icons-png.flaticon.com/512/868/868681.png'
|
||
],
|
||
'timestamp' => now()->toIso8601String(),
|
||
];
|
||
|
||
return $this->sendDiscordMessage(['embeds' => [$embed]]);
|
||
}
|
||
|
||
/**
|
||
* Send error message with enhanced formatting
|
||
*/
|
||
public function SendErrorMessage($data, $context = null)
|
||
{
|
||
$content = is_string($data) ? $data : json_encode($data, JSON_PRETTY_PRINT);
|
||
|
||
$fields = [];
|
||
if ($context) {
|
||
$fields[] = [
|
||
'name' => '📍 Ngữ cảnh',
|
||
'value' => $context,
|
||
'inline' => false,
|
||
];
|
||
}
|
||
|
||
$fields[] = [
|
||
'name' => '⚠️ Chi tiết lỗi',
|
||
'value' => "```json\n" . substr($content, 0, 1000) . "\n```",
|
||
'inline' => false,
|
||
];
|
||
|
||
$embed = [
|
||
'title' => '🚨 Lỗi hệ thống',
|
||
'description' => '**Admin:** Đã xảy ra lỗi trong quá trình xử lý dịch vụ',
|
||
'color' => self::COLOR_ERROR,
|
||
'fields' => $fields,
|
||
'footer' => [
|
||
'text' => 'Error Notification System'
|
||
],
|
||
'timestamp' => now()->toIso8601String(),
|
||
];
|
||
|
||
return $this->sendDiscordMessage(['embeds' => [$embed]]);
|
||
}
|
||
|
||
/**
|
||
* Send new user registration notification
|
||
*/
|
||
public function SendNewUserRegistration($email, $code, $verificationLink)
|
||
{
|
||
$fields = [
|
||
[
|
||
'name' => '📧 Email đăng ký',
|
||
'value' => $email,
|
||
'inline' => true,
|
||
],
|
||
[
|
||
'name' => '🔢 Mã xác thực',
|
||
'value' => "`{$code}`",
|
||
'inline' => true,
|
||
],
|
||
[
|
||
'name' => '🔗 Link xác thực',
|
||
'value' => "[Nhấn để xác thực]({$verificationLink})",
|
||
'inline' => false,
|
||
],
|
||
];
|
||
|
||
$embed = [
|
||
'title' => '🎉 Thành viên mới đăng ký!',
|
||
'description' => '**Có người dùng mới vừa tạo tài khoản**',
|
||
'color' => self::COLOR_SUCCESS,
|
||
'fields' => $fields,
|
||
'thumbnail' => [
|
||
'url' => 'https://cdn-icons-png.flaticon.com/512/1077/1077114.png'
|
||
],
|
||
'timestamp' => now()->toIso8601String(),
|
||
];
|
||
|
||
return $this->sendDiscordMessage(['embeds' => [$embed]]);
|
||
}
|
||
|
||
/**
|
||
* Send customer recharge notification
|
||
*/
|
||
public function SendCustomerRecharge($customer, $amount, $method = null)
|
||
{
|
||
$customerName = trim("{$customer->firstname} {$customer->lastname}");
|
||
|
||
$fields = [
|
||
[
|
||
'name' => '👤 Khách hàng',
|
||
'value' => $customerName ?: 'N/A',
|
||
'inline' => true,
|
||
],
|
||
[
|
||
'name' => '📧 Email',
|
||
'value' => isset($customer->email) ? $customer->email : 'N/A',
|
||
'inline' => true,
|
||
],
|
||
[
|
||
'name' => '💰 Số tiền nạp',
|
||
'value' => '**' . number_format($amount, 0, ',', '.') . ' VNĐ**',
|
||
'inline' => true,
|
||
],
|
||
];
|
||
|
||
if ($method) {
|
||
$fields[] = [
|
||
'name' => '💳 Phương thức',
|
||
'value' => $method,
|
||
'inline' => true,
|
||
];
|
||
}
|
||
|
||
$fields[] = [
|
||
'name' => '🕒 Thời gian',
|
||
'value' => Carbon::now()->format('d/m/Y H:i:s'),
|
||
'inline' => true,
|
||
];
|
||
|
||
$embed = [
|
||
'title' => '💎 Khách hàng nạp tiền thành công',
|
||
'description' => "**{$customerName}** vừa nạp tiền vào tài khoản",
|
||
'color' => self::COLOR_VIP,
|
||
'fields' => $fields,
|
||
'thumbnail' => [
|
||
'url' => 'https://cdn-icons-png.flaticon.com/512/3135/3135715.png'
|
||
],
|
||
'timestamp' => now()->toIso8601String(),
|
||
];
|
||
|
||
return $this->sendDiscordMessage(['embeds' => [$embed]]);
|
||
}
|
||
|
||
/**
|
||
* Send job execution notification
|
||
*/
|
||
public function SendJobRunInfo($jobName, $status, $additionalInfo = null, $duration = null)
|
||
{
|
||
$statusEmoji = match (strtolower($status)) {
|
||
'success', 'completed' => '✅',
|
||
'failed', 'error' => '❌',
|
||
'running', 'processing' => '⏳',
|
||
'warning' => '⚠️',
|
||
default => '📊'
|
||
};
|
||
|
||
$color = match (strtolower($status)) {
|
||
'success', 'completed' => self::COLOR_SUCCESS,
|
||
'failed', 'error' => self::COLOR_ERROR,
|
||
'warning' => self::COLOR_WARNING,
|
||
default => self::COLOR_INFO
|
||
};
|
||
|
||
$fields = [
|
||
[
|
||
'name' => '⏱️ Thời gian chạy',
|
||
'value' => Carbon::now()->format('d/m/Y H:i:s'),
|
||
'inline' => true,
|
||
],
|
||
[
|
||
'name' => '📊 Trạng thái',
|
||
'value' => "{$statusEmoji} {$status}",
|
||
'inline' => true,
|
||
],
|
||
];
|
||
|
||
if ($duration) {
|
||
$fields[] = [
|
||
'name' => '⏲️ Thời gian thực thi',
|
||
'value' => $duration,
|
||
'inline' => true,
|
||
];
|
||
}
|
||
|
||
if ($additionalInfo) {
|
||
$info = is_string($additionalInfo) ? $additionalInfo : json_encode($additionalInfo, JSON_PRETTY_PRINT);
|
||
$fields[] = [
|
||
'name' => '📋 Thông tin chi tiết',
|
||
'value' => "```\n" . substr($info, 0, 1000) . "\n```",
|
||
'inline' => false,
|
||
];
|
||
}
|
||
|
||
$embed = [
|
||
'title' => "⚙️ Job Execution: {$jobName}",
|
||
'color' => $color,
|
||
'fields' => $fields,
|
||
'timestamp' => now()->toIso8601String(),
|
||
];
|
||
|
||
return $this->sendDiscordMessage(['embeds' => [$embed]]);
|
||
}
|
||
|
||
/**
|
||
* Send order paid notification with enhanced formatting
|
||
*/
|
||
public function SendOrderPaidNotification($invoice)
|
||
{
|
||
$invoiceItem = $invoice->invoiceItem()->with([
|
||
'vps',
|
||
'hosting',
|
||
'os.osGroup',
|
||
'addons.addonItems'
|
||
])->first();
|
||
|
||
if (!$invoiceItem) {
|
||
return $this->SendErrorMessage('Không tìm thấy invoice item từ invoice ID: ' . $invoice->id, 'Order Paid Process');
|
||
}
|
||
|
||
$product = $invoiceItem->product;
|
||
$os = $invoiceItem->os;
|
||
$osGroup = isset($os->osGroup) ? $os->osGroup : null;
|
||
$addons = $invoiceItem->addons;
|
||
$customer = $invoice->customer;
|
||
|
||
$fields = [
|
||
[
|
||
'name' => '🧾 Mã đơn hàng',
|
||
'value' => "**#{$invoice->order_num}**",
|
||
'inline' => true,
|
||
],
|
||
[
|
||
'name' => '📦 Sản phẩm',
|
||
'value' => "#" . (isset($product->id) ? $product->id : '') . " - " . (isset($product->name) ? $product->name : ''),
|
||
'inline' => true,
|
||
],
|
||
[
|
||
'name' => '💰 Tổng thanh toán',
|
||
'value' => '**' . number_format($invoice->total, 0, ',', '.') . ' VNĐ**',
|
||
'inline' => true,
|
||
],
|
||
[
|
||
'name' => '👤 Khách hàng',
|
||
'value' => trim((isset($customer->firstname) ? $customer->firstname : '') . ' ' . (isset($customer->lastname) ? $customer->lastname : '')),
|
||
'inline' => true,
|
||
],
|
||
[
|
||
'name' => '📧 Email',
|
||
'value' => isset($customer->email) ? $customer->email : 'N/A',
|
||
'inline' => true,
|
||
],
|
||
[
|
||
'name' => '🕒 Thời gian thanh toán',
|
||
'value' => Carbon::parse($invoice->date_paid)->format('d/m/Y H:i:s'),
|
||
'inline' => true,
|
||
],
|
||
];
|
||
|
||
if ($osGroup || $os) {
|
||
$fields[] = [
|
||
'name' => '🖥️ Hệ điều hành',
|
||
'value' => (isset($osGroup->name) ? $osGroup->name : '') . ' ' . (isset($os->version) ? $os->version : ''),
|
||
'inline' => true,
|
||
];
|
||
}
|
||
|
||
// Add addons if exist
|
||
if ($addons && $addons->count()) {
|
||
$addonsText = "";
|
||
foreach ($addons as $addon) {
|
||
$addonsText .= "• {$addon->addonItems->name} (x{$addon->qty}) - " . number_format($addon->price, 0, ',', '.') . " VNĐ\n";
|
||
}
|
||
$fields[] = [
|
||
'name' => '➕ Dịch vụ bổ sung',
|
||
'value' => $addonsText,
|
||
'inline' => false,
|
||
];
|
||
}
|
||
|
||
$embed = [
|
||
'title' => '💵 Đơn hàng được thanh toán thành công!',
|
||
'description' => "**Khách hàng:** " . (isset($customer->firstname) ? $customer->firstname : '') . " " . (isset($customer->lastname) ? $customer->lastname : '') . "\n**Đơn hàng:** #{$invoice->order_num}",
|
||
'color' => self::COLOR_SUCCESS,
|
||
'fields' => $fields,
|
||
'thumbnail' => [
|
||
'url' => 'https://cdn-icons-png.flaticon.com/512/3135/3135673.png'
|
||
],
|
||
'footer' => [
|
||
'text' => 'Order Management System'
|
||
],
|
||
'timestamp' => now()->toIso8601String(),
|
||
];
|
||
|
||
return $this->sendDiscordMessage(['embeds' => [$embed]]);
|
||
}
|
||
|
||
/**
|
||
* Send service renewal notification
|
||
*/
|
||
public function SendServiceRenewal($service, $period)
|
||
{
|
||
$fields = [
|
||
[
|
||
'name' => '🏷️ Dịch vụ',
|
||
'value' => isset($service->name) ? $service->name : 'N/A',
|
||
'inline' => true,
|
||
],
|
||
[
|
||
'name' => '📅 Gia hạn thêm',
|
||
'value' => $period,
|
||
'inline' => true,
|
||
],
|
||
[
|
||
'name' => '📅 Ngày hết hạn mới',
|
||
'value' => Carbon::parse($service->next_due_date)->format('d/m/Y'),
|
||
'inline' => true,
|
||
],
|
||
];
|
||
|
||
$embed = [
|
||
'title' => '🔄 Dịch vụ được gia hạn',
|
||
'color' => self::COLOR_INFO,
|
||
'fields' => $fields,
|
||
'timestamp' => now()->toIso8601String(),
|
||
];
|
||
|
||
return $this->sendDiscordMessage(['embeds' => [$embed]]);
|
||
}
|
||
|
||
/**
|
||
* Send system maintenance notification
|
||
*/
|
||
public function SendMaintenanceNotification($title, $description, $startTime = null, $endTime = null)
|
||
{
|
||
$fields = [];
|
||
|
||
if ($startTime) {
|
||
$fields[] = [
|
||
'name' => '🕐 Bắt đầu',
|
||
'value' => Carbon::parse($startTime)->format('d/m/Y H:i:s'),
|
||
'inline' => true,
|
||
];
|
||
}
|
||
|
||
if ($endTime) {
|
||
$fields[] = [
|
||
'name' => '🕑 Kết thúc dự kiến',
|
||
'value' => Carbon::parse($endTime)->format('d/m/Y H:i:s'),
|
||
'inline' => true,
|
||
];
|
||
}
|
||
|
||
$embed = [
|
||
'title' => "🔧 {$title}",
|
||
'description' => $description,
|
||
'color' => self::COLOR_WARNING,
|
||
'fields' => $fields,
|
||
'timestamp' => now()->toIso8601String(),
|
||
];
|
||
|
||
return $this->sendDiscordMessage(['embeds' => [$embed]]);
|
||
}
|
||
} |