Compare commits

..

12 Commits

Author SHA1 Message Date
Flatlogic Bot
c8f71ad732 Autosave: 20260217-115844 2026-02-17 11:58:44 +00:00
Flatlogic Bot
bc6c1b2066 Autosave: 20260217-074955 2026-02-17 07:49:55 +00:00
Flatlogic Bot
c0a959cfc7 avV 2026-02-17 02:17:41 +00:00
Flatlogic Bot
6327e98567 V5 2026-02-17 02:15:27 +00:00
Flatlogic Bot
ad181c4cc0 V4+ 2026-02-17 02:01:39 +00:00
Flatlogic Bot
63ba3694d3 V3 2026-02-16 22:45:34 +00:00
Flatlogic Bot
fe99f09665 Autosave: 20260216-163403 2026-02-16 16:34:03 +00:00
Flatlogic Bot
12a425c3fc V4 2026-02-16 16:26:45 +00:00
Flatlogic Bot
989cc90797 V3 2026-02-16 16:25:35 +00:00
Flatlogic Bot
2c48546b22 V2 2026-02-16 16:20:35 +00:00
Flatlogic Bot
91498377d0 V2 2026-02-16 16:08:24 +00:00
Flatlogic Bot
b1733995d3 V1 2026-02-16 16:06:28 +00:00
13 changed files with 1606 additions and 138 deletions

18
api/reset_bot.php Normal file
View File

@ -0,0 +1,18 @@
<?php
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo json_encode(['success' => false, 'error' => 'Invalid request method']);
exit;
}
$output = [];
$return_var = 0;
// Using sudo to run as ubuntu user who owns the pm2 process
exec("sudo -u ubuntu /usr/bin/pm2 restart discord-bot 2>&1", $output, $return_var);
if ($return_var === 0) {
echo json_encode(['success' => true, 'message' => 'Bot restarted successfully']);
} else {
echo json_encode(['success' => false, 'error' => 'Failed to restart bot', 'details' => implode("\n", $output)]);
}

BIN
assets/audio/sahur.mp3 Normal file

Binary file not shown.

177
audio_assets.php Normal file
View File

@ -0,0 +1,177 @@
<?php
require_once 'db/config.php';
$message = '';
$messageType = '';
// Handle File Upload
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['audio_file'])) {
$file = $_FILES['audio_file'];
$filename = 'sahur.mp3'; // Force the name as requested, or we could use $file['name']
$targetDir = 'assets/audio/';
if (!is_dir($targetDir)) {
mkdir($targetDir, 0775, true);
}
$targetFile = $targetDir . $filename;
$fileType = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
// Basic validation
if ($fileType != "mp3") {
$message = "Maaf, hanya file MP3 yang diizinkan.";
$messageType = "error";
} elseif ($file['size'] > 5000000) { // 5MB limit
$message = "File terlalu besar. Maksimal 5MB.";
$messageType = "error";
} else {
if (move_uploaded_file($file['tmp_name'], $targetFile)) {
// Update database record
try {
$stmt = db()->prepare("INSERT INTO audio_assets (filename, display_name) VALUES (?, ?) ON DUPLICATE KEY UPDATE updated_at = CURRENT_TIMESTAMP");
$stmt->execute([$filename, 'Sahur Notification']);
$message = "File $filename berhasil diunggah/diperbarui!";
$messageType = "success";
} catch (Exception $e) {
$message = "Gagal menyimpan ke database: " . $e->getMessage();
$messageType = "error";
}
} else {
$message = "Terjadi kesalahan saat mengunggah file.";
$messageType = "error";
}
}
}
// Fetch current assets
$assets = db()->query("SELECT * FROM audio_assets")->fetchAll();
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Audio Assets Manager - CMS</title>
<style>
:root {
--primary: #007bff;
--success: #28a745;
--error: #dc3545;
--bg: #f8f9fa;
--card: #ffffff;
--text: #333;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: var(--bg);
color: var(--text);
margin: 0;
padding: 20px;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.container {
background: var(--card);
padding: 2rem;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(0,0,0,0.05);
max-width: 500px;
width: 100%;
}
h1 { font-size: 1.5rem; margin-bottom: 1.5rem; text-align: center; }
.alert {
padding: 10px;
border-radius: 6px;
margin-bottom: 1rem;
font-size: 0.9rem;
}
.alert-success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.alert-error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
.upload-zone {
border: 2px dashed #ddd;
padding: 2rem;
text-align: center;
border-radius: 8px;
transition: border-color 0.3s;
cursor: pointer;
}
.upload-zone:hover { border-color: var(--primary); }
.file-input { display: none; }
.btn {
background: var(--primary);
color: white;
border: none;
padding: 12px 20px;
border-radius: 6px;
width: 100%;
font-weight: 600;
cursor: pointer;
margin-top: 1rem;
transition: opacity 0.3s;
}
.btn:hover { opacity: 0.9; }
.asset-list { margin-top: 2rem; }
.asset-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background: #f1f3f5;
border-radius: 6px;
margin-bottom: 0.5rem;
}
.asset-info small { color: #666; display: block; }
audio { width: 100%; margin-top: 10px; height: 35px; }
.back-link { display: block; text-align: center; margin-top: 1rem; color: #666; text-decoration: none; font-size: 0.9rem; }
</style>
</head>
<body>
<div class="container">
<h1>Panel Audio Assets</h1>
<?php if ($message): ?>
<div class="alert alert-<?php echo $messageType; ?>">
<?php echo $message; ?>
</div>
<?php endif; ?>
<form action="" method="POST" enctype="multipart/form-data">
<div class="upload-zone" onclick="document.getElementById('audio_file').click()">
<p>Klik di sini untuk pilih file MP3 (sahur.mp3)</p>
<input type="file" name="audio_file" id="audio_file" class="file-input" accept="audio/mpeg" required>
<div id="file-name" style="margin-top: 10px; font-weight: bold; color: var(--primary);"></div>
</div>
<button type="submit" class="btn">Unggah & Perbarui</button>
</form>
<div class="asset-list">
<h3>File Terdaftar:</h3>
<?php foreach ($assets as $asset): ?>
<div class="asset-item">
<div class="asset-info">
<strong><?php echo htmlspecialchars($asset['display_name']); ?></strong>
<small>Nama file: <?php echo htmlspecialchars($asset['filename']); ?></small>
<small>Terakhir diupdate: <?php echo $asset['updated_at']; ?></small>
</div>
</div>
<audio controls src="assets/audio/<?php echo $asset['filename']; ?>?v=<?php echo time(); ?>"></audio>
<?php endforeach; ?>
</div>
<a href="index.php" class="back-link">&larr; Kembali ke Dashboard</a>
</div>
<script>
document.getElementById('audio_file').onchange = function() {
document.getElementById('file-name').textContent = this.files[0].name;
};
</script>
</body>
</html>

19
bot.log Normal file
View File

@ -0,0 +1,19 @@
Bot logged in as AsepXiaoQin#6954
(node:11761) DeprecationWarning: The ready event has been renamed to clientReady to distinguish it from the gateway READY event and will only emit under that name in v15. Please use clientReady instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
Slash commands registered.
Command dijalankan... /testsahur
Command dijalankan... /join
/home/ubuntu/executor/workspace/node_modules/@discordjs/voice/dist/index.js:529
throw new Error(`No compatible encryption modes. Available include: ${options.join(", ")}`);
^
Error: No compatible encryption modes. Available include: aead_aes256_gcm_rtpsize, aead_xchacha20_poly1305_rtpsize
at chooseEncryptionMode (/home/ubuntu/executor/workspace/node_modules/@discordjs/voice/dist/index.js:529:11)
at /home/ubuntu/executor/workspace/node_modules/@discordjs/voice/dist/index.js:721:21
Emitted 'error' event on VoiceConnection instance at:
at VoiceConnection.onNetworkingError (/home/ubuntu/executor/workspace/node_modules/@discordjs/voice/dist/index.js:1914:10)
at Networking.emit (node:events:518:28)
at /home/ubuntu/executor/workspace/node_modules/@discordjs/voice/dist/index.js:729:32
Node.js v22.18.0

7
data/config.json Normal file
View File

@ -0,0 +1,7 @@
{
"discord_token": "MTQ3MTkwOTE5Mzg4Njg1OTI5NA.GCEdpc.jMIxPFsquVAhp88x3dO-yWUFI7e1u1r8oIZTcw",
"guild_id": "1428530728706117632",
"voice_channel_id": "1457687430189682781",
"alarm_time": "18:55",
"last_voice_channel": null
}

View File

@ -0,0 +1,13 @@
-- migrations/001_create_bot_settings.sql
CREATE TABLE IF NOT EXISTS bot_settings (
id INT AUTO_INCREMENT PRIMARY KEY,
setting_key VARCHAR(50) UNIQUE NOT NULL,
setting_value TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
INSERT IGNORE INTO bot_settings (setting_key, setting_value) VALUES
('discord_token', ''),
('guild_id', ''),
('voice_channel_id', ''),
('last_voice_channel', '');

View File

@ -0,0 +1,10 @@
-- Create audio_assets table
CREATE TABLE IF NOT EXISTS audio_assets (
id INT AUTO_INCREMENT PRIMARY KEY,
filename VARCHAR(255) NOT NULL UNIQUE,
display_name VARCHAR(255) NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- Pre-fill with sahur.mp3 if not exists
INSERT IGNORE INTO audio_assets (filename, display_name) VALUES ('sahur.mp3', 'Sahur Notification');

View File

@ -0,0 +1,3 @@
-- migrations/003_add_alarm_settings.sql
INSERT IGNORE INTO bot_settings (setting_key, setting_value) VALUES
('alarm_time', '');

124
index.js Normal file
View File

@ -0,0 +1,124 @@
const { Client, GatewayIntentBits, SlashCommandBuilder, Routes } = require('discord.js');
const { joinVoiceChannel, createAudioPlayer, createAudioResource, StreamType, getVoiceConnection } = require('@discordjs/voice');
const fs = require('fs');
const path = require('path');
const { REST } = require('@discordjs/rest');
// Load Config
const config = JSON.parse(fs.readFileSync('./data/config.json', 'utf8'));
const token = config.discord_token;
const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildVoiceStates]
});
const player = createAudioPlayer();
player.on('error', err => {
console.log('LOG ERROR AUDIO:', err.message);
});
player.on('stateChange', (old, current) => {
console.log('Status Audio Saat Ini:', current.status);
});
client.on('ready', async () => {
console.log(`Bot Minimalis Ready: ${client.user.tag}`);
const commands = [
new SlashCommandBuilder().setName('join').setDescription('Bot join ke Voice Channel'),
new SlashCommandBuilder().setName('testsahur').setDescription('Tes putar sahur.mp3')
].map(cmd => cmd.toJSON());
const rest = new REST({ version: '10' }).setToken(token);
try {
await rest.put(Routes.applicationCommands(client.user.id), { body: commands });
console.log('Slash Commands registered.');
} catch (error) {
console.error('Error registering commands:', error);
}
});
client.on('interactionCreate', async interaction => {
if (!interaction.isChatInputCommand()) return;
// Fix untuk /join
if (interaction.commandName === 'join') {
// Gunakan Defer Reply di awal
await interaction.deferReply();
try {
const channel = interaction.member.voice.channel;
if (!channel) return interaction.editReply('Masuk ke VC dulu!');
joinVoiceChannel({
channelId: channel.id,
guildId: interaction.guildId,
adapterCreator: interaction.guild.voiceAdapterCreator,
selfDeaf: false,
selfMute: false
});
await interaction.editReply('Berhasil join ke Voice Channel!');
} catch (error) {
// Anti-Stuck: Tangkap error agar tidak diam saja
await interaction.editReply('Error: ' + error.message);
}
}
// Fix untuk /testsahur
if (interaction.commandName === 'testsahur') {
// Gunakan Defer Reply di awal
await interaction.deferReply();
try {
const channel = interaction.member.voice.channel;
if (!channel) return interaction.editReply('Masuk ke VC dulu!');
const connection = joinVoiceChannel({
channelId: channel.id,
guildId: interaction.guildId,
adapterCreator: interaction.guild.voiceAdapterCreator,
selfDeaf: false,
selfMute: false
});
// Direct Path
const filePath = './assets/audio/sahur.mp3';
// Verify File
if (!fs.existsSync(filePath)) {
return interaction.editReply('Error: File tidak ditemukan di assets/audio!');
}
// Hapus Stream Kompleks & Gunakan Jalur Langsung
const resource = createAudioResource('./assets/audio/sahur.mp3', {
inputType: StreamType.Arbitrary,
inlineVolume: true
});
// Debug Status
console.log('Resource Readable:', resource.readable);
console.log('Connection Status:', connection.state.status);
// Kabel Utama (Subscribe)
const subscription = connection.subscribe(player);
player.play(resource);
await interaction.editReply('🔊 Sedang memutar sahur.mp3...');
} catch (error) {
// Anti-Stuck: Tangkap error agar tidak diam saja
await interaction.editReply('Error: ' + error.message);
}
}
// Interaction Timeout Fix: Jika belum dibalas atau di-defer
if (!interaction.replied && !interaction.deferred) {
try {
await interaction.reply('Sedang diproses...');
} catch (err) {
console.error('Fallback reply failed:', err.message);
}
}
});
client.login(token);

405
index.php
View File

@ -1,150 +1,279 @@
<?php <?php
declare(strict_types=1); require_once __DIR__ . '/db/config.php';
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION; function get_setting($key) {
$now = date('Y-m-d H:i:s'); $stmt = db()->prepare("SELECT setting_value FROM bot_settings WHERE setting_key = ?");
$stmt->execute([$key]);
return $stmt->fetchColumn() ?: '';
}
function save_setting($key, $value) {
$stmt = db()->prepare("UPDATE bot_settings SET setting_value = ? WHERE setting_key = ?");
$stmt->execute([$value, $key]);
}
$message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$token = $_POST['discord_token'] ?? '';
$guild = $_POST['guild_id'] ?? '';
$channel = $_POST['voice_channel_id'] ?? '';
$alarm = $_POST['alarm_time'] ?? '';
save_setting('discord_token', $token);
save_setting('guild_id', $guild);
save_setting('voice_channel_id', $channel);
save_setting('alarm_time', $alarm);
// Also update JSON for the bot
$config = [
'discord_token' => $token,
'guild_id' => $guild,
'voice_channel_id' => $channel,
'alarm_time' => $alarm,
'last_voice_channel' => json_decode(get_setting('last_voice_channel'), true) ?: null
];
if (!is_dir(__DIR__ . '/data')) mkdir(__DIR__ . '/data', 0775, true);
file_put_contents(__DIR__ . '/data/config.json', json_encode($config, JSON_PRETTY_PRINT));
$message = 'Configuration saved successfully!';
}
$token = get_setting('discord_token');
$guildId = get_setting('guild_id');
$voiceId = get_setting('voice_channel_id');
$alarmTime = get_setting('alarm_time');
?> ?>
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>New Style</title> <title>Discord Alarm Sahur Dashboard</title>
<?php <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
// Read project preview data from environment <link rel="preconnect" href="https://fonts.googleapis.com">
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? ''; <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
?> <style>
<?php if ($projectDescription): ?> :root {
<!-- Meta description --> --primary-bg: #f8f9fa;
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' /> --surface-bg: #ffffff;
<!-- Open Graph meta tags --> --border-color: #e5e5e5;
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" /> --text-main: #111111;
<!-- Twitter meta tags --> --text-muted: #666666;
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" /> --accent: #007aff;
<?php endif; ?> }
<?php if ($projectImageUrl): ?> body {
<!-- Open Graph image --> font-family: 'Inter', system-ui, -apple-system, sans-serif;
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" /> background-color: var(--primary-bg);
<!-- Twitter image --> color: var(--text-main);
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" /> padding: 2rem 1rem;
<?php endif; ?> }
<link rel="preconnect" href="https://fonts.googleapis.com"> .container { max-width: 600px; }
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> .card {
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet"> background: var(--surface-bg);
<style> border: 1px solid var(--border-color);
:root { border-radius: 4px;
--bg-color-start: #6a11cb; box-shadow: none;
--bg-color-end: #2575fc; margin-bottom: 1.5rem;
--text-color: #ffffff; }
--card-bg-color: rgba(255, 255, 255, 0.01); .card-header {
--card-border-color: rgba(255, 255, 255, 0.1); background: transparent;
} border-bottom: 1px solid var(--border-color);
body { padding: 1rem 1.25rem;
margin: 0; font-weight: 600;
font-family: 'Inter', sans-serif; }
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end)); .btn-primary {
color: var(--text-color); background-color: var(--text-main);
display: flex; border-color: var(--text-main);
justify-content: center; border-radius: 4px;
align-items: center; padding: 0.5rem 1rem;
min-height: 100vh; font-weight: 500;
text-align: center; }
overflow: hidden; .btn-primary:hover {
position: relative; background-color: #333;
} border-color: #333;
body::before { }
content: ''; .form-control {
position: absolute; border-radius: 4px;
top: 0; border: 1px solid var(--border-color);
left: 0; padding: 0.6rem;
width: 100%; }
height: 100%; .form-control:focus {
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>'); border-color: var(--accent);
animation: bg-pan 20s linear infinite; box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.1);
z-index: -1; }
} .status-dot {
@keyframes bg-pan { height: 10px;
0% { background-position: 0% 0%; } width: 10px;
100% { background-position: 100% 100%; } background-color: #ccc;
} border-radius: 50%;
main { display: inline-block;
padding: 2rem; margin-right: 5px;
} }
.card { .status-online { background-color: #28a745; }
background: var(--card-bg-color); .audio-preview {
border: 1px solid var(--card-border-color); background: #f1f1f1;
border-radius: 16px; padding: 10px;
padding: 2rem; border-radius: 4px;
backdrop-filter: blur(20px); display: flex;
-webkit-backdrop-filter: blur(20px); align-items: center;
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1); justify-content: space-between;
} }
.loader { h1 { font-size: 1.5rem; font-weight: 600; margin-bottom: 1.5rem; }
margin: 1.25rem auto 1.25rem; .hint { font-size: 0.85rem; color: var(--text-muted); margin-top: 0.25rem; }
width: 48px; </style>
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
</head> </head>
<body> <body>
<main>
<div class="container">
<?php
$bot_status = 'Offline';
$bot_running = false;
$pm2_output = [];
exec("sudo -u ubuntu /usr/bin/pm2 jlist", $pm2_output);
$pm2_data = json_decode(implode('', $pm2_output), true);
if ($pm2_data) {
foreach ($pm2_data as $proc) {
if ($proc['name'] === 'discord-bot') {
$bot_status = ucfirst($proc['pm2_env']['status']);
$bot_running = ($proc['pm2_env']['status'] === 'online');
break;
}
}
}
?>
<header class="d-flex justify-content-between align-items-center mb-4">
<h1>Alarm Sahur Bot</h1>
<div class="small text-muted">
<span class="status-dot <?= $bot_running ? 'status-online' : '' ?>"></span>
Bot: <strong><?= $bot_status ?></strong>
</div>
</header>
<?php if ($message): ?>
<div class="alert alert-success py-2 px-3 small border-0 rounded-1 mb-4" role="alert">
<?= htmlspecialchars($message) ?>
</div>
<?php endif; ?>
<div class="card"> <div class="card">
<h1>Analyzing your requirements and generating your website…</h1> <div class="card-header">Configuration</div>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes"> <div class="card-body">
<span class="sr-only">Loading…</span> <form method="POST">
</div> <div class="mb-3">
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p> <label class="form-label small fw-medium">Discord Bot Token</label>
<p class="hint">This page will update automatically as the plan is implemented.</p> <input type="password" name="discord_token" class="form-control" value="<?= htmlspecialchars($token) ?>" placeholder="MTAyMzQ1Njc4OTAxMjM0NTY3OA...">
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p> <div class="hint">Obtained from Discord Developer Portal.</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label small fw-medium">Guild ID</label>
<input type="text" name="guild_id" class="form-control" value="<?= htmlspecialchars($guildId) ?>" placeholder="1234567890...">
</div>
<div class="col-md-6 mb-3">
<label class="form-label small fw-medium">Voice Channel ID</label>
<input type="text" name="voice_channel_id" class="form-control" value="<?= htmlspecialchars($voiceId) ?>" placeholder="9876543210...">
</div>
</div>
<div class="mb-3">
<label class="form-label small fw-medium">Alarm Time (HH:MM)</label>
<input type="time" name="alarm_time" class="form-control" value="<?= htmlspecialchars($alarmTime) ?>">
<div class="hint">Bot will automatically join the voice channel and play audio at this time.</div>
</div>
<button type="submit" class="btn btn-primary w-100 mt-2">Save Configuration</button>
</form>
</div>
</div> </div>
</main>
<footer> <div class="card">
Page updated: <?= htmlspecialchars($now) ?> (UTC) <div class="card-header">Bot Controls</div>
</footer> <div class="card-body">
<p class="small text-muted mb-3">Use these controls to manage the bot process. If the bot is not responding, try resetting it.</p>
<button id="resetBotBtn" class="btn btn-outline-danger w-100 fw-medium">
<span id="btnText">Reset Bot Process</span>
<span id="btnLoader" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
</button>
<div id="resetStatus" class="hint mt-2 text-center d-none"></div>
</div>
</div>
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<span>Audio Asset</span>
<a href="audio_assets.php" class="btn btn-sm btn-outline-primary">Manage</a>
</div>
<div class="card-body">
<div class="audio-preview">
<span class="small fw-medium">sahur.mp3</span>
<audio id="sahurPlayer" src="assets/audio/sahur.mp3?v=<?= time() ?>"></audio>
<button class="btn btn-sm btn-outline-dark" onclick="document.getElementById('sahurPlayer').play()">Preview</button>
</div>
<div class="hint mt-2">File located at <code>assets/audio/sahur.mp3</code>.</div>
</div>
</div>
<div class="card">
<div class="card-header">Quick Start</div>
<div class="card-body small">
<ol class="ps-3 mb-0">
<li>Configure the Token and Channel IDs above.</li>
<li>Ensure <code>node_modules</code> are installed (run <code>npm install</code>).</li>
<li>Start the bot via <code>node index.js</code>.</li>
<li>Use <code>/testsahur</code> in your Discord server.</li>
</ol>
</div>
</div>
<footer class="text-center mt-5">
<p class="small text-muted">Flatlogic LAMP Engineer &copy; <?= date('Y') ?></p>
</footer>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.getElementById('resetBotBtn').addEventListener('click', function() {
const btn = this;
const btnText = document.getElementById('btnText');
const btnLoader = document.getElementById('btnLoader');
const statusDiv = document.getElementById('resetStatus');
if (!confirm('Are you sure you want to restart the bot process?')) return;
btn.disabled = true;
btnText.classList.add('d-none');
btnLoader.classList.remove('d-none');
statusDiv.classList.add('d-none');
fetch('api/reset_bot.php', {
method: 'POST'
})
.then(response => response.json())
.then(data => {
btn.disabled = false;
btnText.classList.remove('d-none');
btnLoader.classList.add('d-none');
statusDiv.classList.remove('d-none');
if (data.success) {
statusDiv.className = 'hint mt-2 text-center text-success';
statusDiv.textContent = data.message + '. Refreshing page...';
setTimeout(() => location.reload(), 2000);
} else {
statusDiv.className = 'hint mt-2 text-center text-danger';
statusDiv.textContent = 'Error: ' + (data.error || 'Unknown error');
console.error(data.details);
}
})
.catch(error => {
btn.disabled = false;
btnText.classList.remove('d-none');
btnLoader.classList.add('d-none');
statusDiv.classList.remove('d-none');
statusDiv.className = 'hint mt-2 text-center text-danger';
statusDiv.textContent = 'Network error occurred.';
console.error(error);
});
});
</script>
</body> </body>
</html> </html>

953
package-lock.json generated Normal file
View File

@ -0,0 +1,953 @@
{
"name": "alarm-sahur-bot",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "alarm-sahur-bot",
"version": "1.0.0",
"dependencies": {
"@discordjs/opus": "^0.10.0",
"@discordjs/voice": "^0.19.0",
"discord.js": "^14.15.3",
"ffmpeg-static": "^5.3.0",
"libsodium-wrappers": "^0.7.16",
"opusscript": "^0.1.1",
"play-dl": "^1.9.7"
}
},
"node_modules/@derhuerst/http-basic": {
"version": "8.2.4",
"license": "MIT",
"dependencies": {
"caseless": "^0.12.0",
"concat-stream": "^2.0.0",
"http-response-object": "^3.0.1",
"parse-cache-control": "^1.0.1"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@discordjs/builders": {
"version": "1.13.1",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/formatters": "^0.6.2",
"@discordjs/util": "^1.2.0",
"@sapphire/shapeshift": "^4.0.0",
"discord-api-types": "^0.38.33",
"fast-deep-equal": "^3.1.3",
"ts-mixer": "^6.0.4",
"tslib": "^2.6.3"
},
"engines": {
"node": ">=16.11.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/collection": {
"version": "1.5.3",
"license": "Apache-2.0",
"engines": {
"node": ">=16.11.0"
}
},
"node_modules/@discordjs/formatters": {
"version": "0.6.2",
"license": "Apache-2.0",
"dependencies": {
"discord-api-types": "^0.38.33"
},
"engines": {
"node": ">=16.11.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/node-pre-gyp": {
"version": "0.4.5",
"license": "BSD-3-Clause",
"dependencies": {
"detect-libc": "^2.0.0",
"https-proxy-agent": "^5.0.0",
"make-dir": "^3.1.0",
"node-fetch": "^2.6.7",
"nopt": "^5.0.0",
"npmlog": "^5.0.1",
"rimraf": "^3.0.2",
"semver": "^7.3.5",
"tar": "^6.1.11"
},
"bin": {
"node-pre-gyp": "bin/node-pre-gyp"
}
},
"node_modules/@discordjs/opus": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/@discordjs/opus/-/opus-0.10.0.tgz",
"integrity": "sha512-HHEnSNrSPmFEyndRdQBJN2YE6egyXS9JUnJWyP6jficK0Y+qKMEZXyYTgmzpjrxXP1exM/hKaNP7BRBUEWkU5w==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@discordjs/node-pre-gyp": "^0.4.5",
"node-addon-api": "^8.1.0"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/@discordjs/rest": {
"version": "2.6.0",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/collection": "^2.1.1",
"@discordjs/util": "^1.1.1",
"@sapphire/async-queue": "^1.5.3",
"@sapphire/snowflake": "^3.5.3",
"@vladfrangu/async_event_emitter": "^2.4.6",
"discord-api-types": "^0.38.16",
"magic-bytes.js": "^1.10.0",
"tslib": "^2.6.3",
"undici": "6.21.3"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/rest/node_modules/@discordjs/collection": {
"version": "2.1.1",
"license": "Apache-2.0",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/util": {
"version": "1.2.0",
"license": "Apache-2.0",
"dependencies": {
"discord-api-types": "^0.38.33"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/voice": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.19.0.tgz",
"integrity": "sha512-UyX6rGEXzVyPzb1yvjHtPfTlnLvB5jX/stAMdiytHhfoydX+98hfympdOwsnTktzr+IRvphxTbdErgYDJkEsvw==",
"license": "Apache-2.0",
"dependencies": {
"@types/ws": "^8.18.1",
"discord-api-types": "^0.38.16",
"prism-media": "^1.3.5",
"tslib": "^2.8.1",
"ws": "^8.18.3"
},
"engines": {
"node": ">=22.12.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/voice/node_modules/opusscript": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/opusscript/-/opusscript-0.0.8.tgz",
"integrity": "sha512-VSTi1aWFuCkRCVq+tx/BQ5q9fMnQ9pVZ3JU4UHKqTkf0ED3fKEPdr+gKAAl3IA2hj9rrP6iyq3hlcJq3HELtNQ==",
"license": "MIT",
"optional": true,
"peer": true
},
"node_modules/@discordjs/voice/node_modules/prism-media": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.3.5.tgz",
"integrity": "sha512-IQdl0Q01m4LrkN1EGIE9lphov5Hy7WWlH6ulf5QdGePLlPas9p2mhgddTEHrlaXYjjFToM1/rWuwF37VF4taaA==",
"license": "Apache-2.0",
"peerDependencies": {
"@discordjs/opus": ">=0.8.0 <1.0.0",
"ffmpeg-static": "^5.0.2 || ^4.2.7 || ^3.0.0 || ^2.4.0",
"node-opus": "^0.3.3",
"opusscript": "^0.0.8"
},
"peerDependenciesMeta": {
"@discordjs/opus": {
"optional": true
},
"ffmpeg-static": {
"optional": true
},
"node-opus": {
"optional": true
},
"opusscript": {
"optional": true
}
}
},
"node_modules/@discordjs/ws": {
"version": "1.2.3",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/collection": "^2.1.0",
"@discordjs/rest": "^2.5.1",
"@discordjs/util": "^1.1.0",
"@sapphire/async-queue": "^1.5.2",
"@types/ws": "^8.5.10",
"@vladfrangu/async_event_emitter": "^2.2.4",
"discord-api-types": "^0.38.1",
"tslib": "^2.6.2",
"ws": "^8.17.0"
},
"engines": {
"node": ">=16.11.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/ws/node_modules/@discordjs/collection": {
"version": "2.1.1",
"license": "Apache-2.0",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@sapphire/async-queue": {
"version": "1.5.5",
"license": "MIT",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@sapphire/shapeshift": {
"version": "4.0.0",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"lodash": "^4.17.21"
},
"engines": {
"node": ">=v16"
}
},
"node_modules/@sapphire/snowflake": {
"version": "3.5.3",
"license": "MIT",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@types/node": {
"version": "25.2.3",
"license": "MIT",
"dependencies": {
"undici-types": "~7.16.0"
}
},
"node_modules/@types/ws": {
"version": "8.18.1",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@vladfrangu/async_event_emitter": {
"version": "2.4.7",
"license": "MIT",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"license": "ISC"
},
"node_modules/agent-base": {
"version": "6.0.2",
"license": "MIT",
"dependencies": {
"debug": "4"
},
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/aproba": {
"version": "2.1.0",
"license": "ISC"
},
"node_modules/are-we-there-yet": {
"version": "2.0.0",
"license": "ISC",
"dependencies": {
"delegates": "^1.0.0",
"readable-stream": "^3.6.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"license": "MIT"
},
"node_modules/brace-expansion": {
"version": "1.1.12",
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"node_modules/buffer-from": {
"version": "1.1.2",
"license": "MIT"
},
"node_modules/caseless": {
"version": "0.12.0",
"license": "Apache-2.0"
},
"node_modules/chownr": {
"version": "2.0.0",
"license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/color-support": {
"version": "1.1.3",
"license": "ISC",
"bin": {
"color-support": "bin.js"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"license": "MIT"
},
"node_modules/concat-stream": {
"version": "2.0.0",
"engines": [
"node >= 6.0"
],
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.0.2",
"typedarray": "^0.0.6"
}
},
"node_modules/console-control-strings": {
"version": "1.1.0",
"license": "ISC"
},
"node_modules/debug": {
"version": "4.4.3",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/delegates": {
"version": "1.0.0",
"license": "MIT"
},
"node_modules/detect-libc": {
"version": "2.1.2",
"license": "Apache-2.0",
"engines": {
"node": ">=8"
}
},
"node_modules/discord-api-types": {
"version": "0.38.39",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.39.tgz",
"integrity": "sha512-XRdDQvZvID1XvcFftjSmd4dcmMi/RL/jSy5sduBDAvCGFcNFHThdIQXCEBDZFe52lCNEzuIL0QJoKYAmRmxLUA==",
"license": "MIT",
"workspaces": [
"scripts/actions/documentation"
]
},
"node_modules/discord.js": {
"version": "14.25.1",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/builders": "^1.13.0",
"@discordjs/collection": "1.5.3",
"@discordjs/formatters": "^0.6.2",
"@discordjs/rest": "^2.6.0",
"@discordjs/util": "^1.2.0",
"@discordjs/ws": "^1.2.3",
"@sapphire/snowflake": "3.5.3",
"discord-api-types": "^0.38.33",
"fast-deep-equal": "3.1.3",
"lodash.snakecase": "4.1.1",
"magic-bytes.js": "^1.10.0",
"tslib": "^2.6.3",
"undici": "6.21.3"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"license": "MIT"
},
"node_modules/env-paths": {
"version": "2.2.1",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"license": "MIT"
},
"node_modules/ffmpeg-static": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/ffmpeg-static/-/ffmpeg-static-5.3.0.tgz",
"integrity": "sha512-H+K6sW6TiIX6VGend0KQwthe+kaceeH/luE8dIZyOP35ik7ahYojDuqlTV1bOrtEwl01sy2HFNGQfi5IDJvotg==",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {
"@derhuerst/http-basic": "^8.2.0",
"env-paths": "^2.2.0",
"https-proxy-agent": "^5.0.0",
"progress": "^2.0.3"
},
"engines": {
"node": ">=16"
}
},
"node_modules/fs-minipass": {
"version": "2.1.0",
"license": "ISC",
"dependencies": {
"minipass": "^3.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/fs-minipass/node_modules/minipass": {
"version": "3.3.6",
"license": "ISC",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"license": "ISC"
},
"node_modules/gauge": {
"version": "3.0.2",
"license": "ISC",
"dependencies": {
"aproba": "^1.0.3 || ^2.0.0",
"color-support": "^1.1.2",
"console-control-strings": "^1.0.0",
"has-unicode": "^2.0.1",
"object-assign": "^4.1.1",
"signal-exit": "^3.0.0",
"string-width": "^4.2.3",
"strip-ansi": "^6.0.1",
"wide-align": "^1.1.2"
},
"engines": {
"node": ">=10"
}
},
"node_modules/glob": {
"version": "7.2.3",
"license": "ISC",
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.1.1",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
},
"engines": {
"node": "*"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/has-unicode": {
"version": "2.0.1",
"license": "ISC"
},
"node_modules/http-response-object": {
"version": "3.0.2",
"license": "MIT",
"dependencies": {
"@types/node": "^10.0.3"
}
},
"node_modules/http-response-object/node_modules/@types/node": {
"version": "10.17.60",
"license": "MIT"
},
"node_modules/https-proxy-agent": {
"version": "5.0.1",
"license": "MIT",
"dependencies": {
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/inflight": {
"version": "1.0.6",
"license": "ISC",
"dependencies": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"license": "ISC"
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/libsodium": {
"version": "0.7.16",
"license": "ISC"
},
"node_modules/libsodium-wrappers": {
"version": "0.7.16",
"resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.16.tgz",
"integrity": "sha512-Gtr/WBx4dKjvRL1pvfwZqu7gO6AfrQ0u9vFL+kXihtHf6NfkROR8pjYWn98MFDI3jN19Ii1ZUfPR9afGiPyfHg==",
"license": "ISC",
"dependencies": {
"libsodium": "^0.7.16"
}
},
"node_modules/lodash": {
"version": "4.17.23",
"license": "MIT"
},
"node_modules/lodash.snakecase": {
"version": "4.1.1",
"license": "MIT"
},
"node_modules/magic-bytes.js": {
"version": "1.13.0",
"license": "MIT"
},
"node_modules/make-dir": {
"version": "3.1.0",
"license": "MIT",
"dependencies": {
"semver": "^6.0.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/make-dir/node_modules/semver": {
"version": "6.3.1",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"license": "ISC",
"dependencies": {
"brace-expansion": "^1.1.7"
},
"engines": {
"node": "*"
}
},
"node_modules/minipass": {
"version": "5.0.0",
"license": "ISC",
"engines": {
"node": ">=8"
}
},
"node_modules/minizlib": {
"version": "2.1.2",
"license": "MIT",
"dependencies": {
"minipass": "^3.0.0",
"yallist": "^4.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/minizlib/node_modules/minipass": {
"version": "3.3.6",
"license": "ISC",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/mkdirp": {
"version": "1.0.4",
"license": "MIT",
"bin": {
"mkdirp": "bin/cmd.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/ms": {
"version": "2.1.3",
"license": "MIT"
},
"node_modules/node-addon-api": {
"version": "8.5.0",
"license": "MIT",
"engines": {
"node": "^18 || ^20 || >= 21"
}
},
"node_modules/node-fetch": {
"version": "2.7.0",
"license": "MIT",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/nopt": {
"version": "5.0.0",
"license": "ISC",
"dependencies": {
"abbrev": "1"
},
"bin": {
"nopt": "bin/nopt.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/npmlog": {
"version": "5.0.1",
"license": "ISC",
"dependencies": {
"are-we-there-yet": "^2.0.0",
"console-control-strings": "^1.1.0",
"gauge": "^3.0.0",
"set-blocking": "^2.0.0"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"license": "ISC",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/opusscript": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/opusscript/-/opusscript-0.1.1.tgz",
"integrity": "sha512-mL0fZZOUnXdZ78woRXp18lApwpp0lF5tozJOD1Wut0dgrA9WuQTgSels/CSmFleaAZrJi/nci5KOVtbuxeWoQA==",
"license": "MIT"
},
"node_modules/parse-cache-control": {
"version": "1.0.1"
},
"node_modules/path-is-absolute": {
"version": "1.0.1",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/play-audio": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/play-audio/-/play-audio-0.5.2.tgz",
"integrity": "sha512-ZAqHUKkQLix2Iga7pPbsf1LpUoBjcpwU93F1l3qBIfxYddQLhxS6GKmS0d3jV8kSVaUbr6NnOEcEMFvuX93SWQ==",
"license": "GPL-3.0"
},
"node_modules/play-dl": {
"version": "1.9.7",
"resolved": "https://registry.npmjs.org/play-dl/-/play-dl-1.9.7.tgz",
"integrity": "sha512-KpgerWxUCY4s9Mhze2qdqPhiqd8Ve6HufpH9mBH3FN+vux55qSh6WJKDabfie8IBHN7lnrAlYcT/UdGax58c2A==",
"license": "GPL-3.0",
"dependencies": {
"play-audio": "^0.5.2"
},
"engines": {
"node": ">=16.0.0"
}
},
"node_modules/progress": {
"version": "2.0.3",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/readable-stream": {
"version": "3.6.2",
"license": "MIT",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/rimraf": {
"version": "3.0.2",
"license": "ISC",
"dependencies": {
"glob": "^7.1.3"
},
"bin": {
"rimraf": "bin.js"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/semver": {
"version": "7.7.4",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/set-blocking": {
"version": "2.0.0",
"license": "ISC"
},
"node_modules/signal-exit": {
"version": "3.0.7",
"license": "ISC"
},
"node_modules/string_decoder": {
"version": "1.3.0",
"license": "MIT",
"dependencies": {
"safe-buffer": "~5.2.0"
}
},
"node_modules/string-width": {
"version": "4.2.3",
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/tar": {
"version": "6.2.1",
"license": "ISC",
"dependencies": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
"minipass": "^5.0.0",
"minizlib": "^2.1.1",
"mkdirp": "^1.0.3",
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"license": "MIT"
},
"node_modules/ts-mixer": {
"version": "6.0.4",
"license": "MIT"
},
"node_modules/tslib": {
"version": "2.8.1",
"license": "0BSD"
},
"node_modules/typedarray": {
"version": "0.0.6",
"license": "MIT"
},
"node_modules/undici": {
"version": "6.21.3",
"license": "MIT",
"engines": {
"node": ">=18.17"
}
},
"node_modules/undici-types": {
"version": "7.16.0",
"license": "MIT"
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"license": "MIT"
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"license": "BSD-2-Clause"
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"license": "MIT",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/wide-align": {
"version": "1.1.5",
"license": "ISC",
"dependencies": {
"string-width": "^1.0.2 || 2 || 3 || 4"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"license": "ISC"
},
"node_modules/ws": {
"version": "8.19.0",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/yallist": {
"version": "4.0.0",
"license": "ISC"
}
}
}

15
package.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "alarm-sahur-bot",
"version": "1.0.0",
"description": "Discord Alarm Sahur Bot",
"main": "index.js",
"dependencies": {
"@discordjs/opus": "^0.10.0",
"@discordjs/voice": "^0.19.0",
"discord.js": "^14.15.3",
"ffmpeg-static": "^5.3.0",
"libsodium-wrappers": "^0.7.16",
"opusscript": "^0.1.1",
"play-dl": "^1.9.7"
}
}

BIN
sahur.mp3 Normal file

Binary file not shown.