Autosave: 20260214-173759
This commit is contained in:
parent
6d183d7a92
commit
4cf7a4d459
63
assets/css/custom.css
Normal file
63
assets/css/custom.css
Normal file
@ -0,0 +1,63 @@
|
||||
body {
|
||||
background-color: #f6f8fa;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
color: #24292f;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background-color: #23272a !important;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 1px solid #d0d7de;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
border-bottom: 1px solid #d0d7de;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #5865f2;
|
||||
border-color: #5865f2;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #4752c4;
|
||||
border-color: #4752c4;
|
||||
}
|
||||
|
||||
.badge {
|
||||
font-weight: 600;
|
||||
padding: 0.5em 0.8em;
|
||||
}
|
||||
|
||||
#logList {
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.list-group-item:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.font-weight-bold {
|
||||
font-weight: 600;
|
||||
}
|
||||
85
assets/js/main.js
Normal file
85
assets/js/main.js
Normal file
@ -0,0 +1,85 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const configForm = document.getElementById('configForm');
|
||||
const toastElement = document.getElementById('liveToast');
|
||||
const toast = new bootstrap.Toast(toastElement);
|
||||
const toastMessage = document.getElementById('toastMessage');
|
||||
|
||||
function showToast(message, type = 'info') {
|
||||
toastMessage.textContent = message;
|
||||
toast.show();
|
||||
}
|
||||
|
||||
if (configForm) {
|
||||
configForm.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(configForm);
|
||||
|
||||
fetch('save_config.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showToast('Configuration saved successfully!', 'success');
|
||||
} else {
|
||||
showToast('Error: ' + data.error, 'danger');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showToast('An error occurred while saving.', 'danger');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const btnTestSahur = document.getElementById('btnTestSahur');
|
||||
if (btnTestSahur) {
|
||||
btnTestSahur.addEventListener('click', function() {
|
||||
fetch('test_sahur.php')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showToast('Sahur test triggered! Check logs.', 'success');
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const refreshLogs = document.getElementById('refreshLogs');
|
||||
if (refreshLogs) {
|
||||
refreshLogs.addEventListener('click', function() {
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
const btnStartBot = document.getElementById('btnStartBot');
|
||||
const btnStopBot = document.getElementById('btnStopBot');
|
||||
const btnRestartBot = document.getElementById('btnRestartBot');
|
||||
|
||||
if (btnStartBot) {
|
||||
btnStartBot.addEventListener('click', () => manageBot('start'));
|
||||
}
|
||||
if (btnStopBot) {
|
||||
btnStopBot.addEventListener('click', () => manageBot('stop'));
|
||||
}
|
||||
if (btnRestartBot) {
|
||||
btnRestartBot.addEventListener('click', () => manageBot('restart'));
|
||||
}
|
||||
|
||||
function manageBot(action) {
|
||||
fetch('manage_bot.php?action=' + action)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
showToast('Bot ' + action + 'ed successfully!', 'success');
|
||||
setTimeout(() => location.reload(), 2000);
|
||||
} else {
|
||||
showToast('Error: ' + data.error, 'danger');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showToast('An error occurred.', 'danger');
|
||||
});
|
||||
}
|
||||
});
|
||||
323
bot.log
Normal file
323
bot.log
Normal file
@ -0,0 +1,323 @@
|
||||
[2026-02-14T17:28:37.135927+00:00] DiscordPHP.DEBUG: Initializing DiscordPHP v10.46.0 (DiscordPHP-Http: v10.8.0 & Gateway: v10) on PHP 8.2.29
|
||||
[2026-02-14T17:28:37.889141+00:00] DiscordPHP.DEBUG: BUCKET getapplications/@me queued REQ GET applications/@me
|
||||
[2026-02-14T17:28:37.889286+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:28:38.136506+00:00] DiscordPHP.DEBUG: BUCKET getgateway/bot queued REQ GET gateway/bot
|
||||
[2026-02-14T17:28:38.140843+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":1,"empty":true}
|
||||
[2026-02-14T17:28:41.865360+00:00] DiscordPHP.DEBUG: REQ GET gateway/bot successful
|
||||
[2026-02-14T17:28:41.865528+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":1,"empty":true}
|
||||
[2026-02-14T17:28:41.866063+00:00] DiscordPHP.INFO: gateway retrieved and set {"gateway":"wss://gateway.discord.gg/?v=10&encoding=json&compress=zlib-stream","session":{"total":1000,"remaining":998,"reset_after":86385055,"max_concurrency":1}}
|
||||
[2026-02-14T17:28:41.866152+00:00] DiscordPHP.DEBUG: session data received {"session":{"total":1000,"remaining":998,"reset_after":86385055,"max_concurrency":1}}
|
||||
[2026-02-14T17:28:41.866191+00:00] DiscordPHP.INFO: starting connection to websocket {"gateway":"wss://gateway.discord.gg/?v=10&encoding=json&compress=zlib-stream"}
|
||||
[2026-02-14T17:28:41.870607+00:00] DiscordPHP.DEBUG: REQ GET applications/@me successful
|
||||
[2026-02-14T17:28:41.870704+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:28:43.869452+00:00] DiscordPHP.INFO: websocket connection has been created
|
||||
[2026-02-14T17:28:43.874079+00:00] DiscordPHP.INFO: received hello
|
||||
[2026-02-14T17:28:43.874196+00:00] DiscordPHP.DEBUG: sending heartbeat {"seq":null}
|
||||
[2026-02-14T17:28:43.874338+00:00] DiscordPHP.INFO: heartbeat timer initialized {"interval":41250.0}
|
||||
[2026-02-14T17:28:43.874396+00:00] DiscordPHP.INFO: identifying {"payload":{"op":2,"d":{"token":"*****","properties":{"os":"Linux","browser":"DiscordBot (https://github.com/discord-php/DiscordPHP-HTTP, v10.8.0)","device":"DiscordBot (https://github.com/discord-php/DiscordPHP-HTTP, v10.8.0)","referrer":"https://github.com/discord-php/DiscordPHP","referring_domain":"https://github.com/discord-php/DiscordPHP"},"compress":true,"intents":53608189}}}
|
||||
[2026-02-14T17:28:44.113377+00:00] DiscordPHP.DEBUG: received heartbeat ack {"response_time":239.02297019958496}
|
||||
[2026-02-14T17:28:44.113623+00:00] DiscordPHP.DEBUG: ready packet received
|
||||
[2026-02-14T17:28:44.113664+00:00] DiscordPHP.DEBUG: resume_gateway_url received {"url":"wss://gateway-us-east1-d.discord.gg"}
|
||||
[2026-02-14T17:28:44.113714+00:00] DiscordPHP.DEBUG: discord trace received {"trace":["[\"gateway-prd-arm-us-east1-d-vgmq\",{\"micros\":153465,\"calls\":[\"id_created\",{\"micros\":536,\"calls\":[]},\"session_lookup_time\",{\"micros\":584,\"calls\":[]},\"session_lookup_finished\",{\"micros\":11,\"calls\":[]},\"discord-sessions-prd-2-147\",{\"micros\":152016,\"calls\":[\"start_session\",{\"micros\":149046,\"calls\":[\"discord-api-rpc-66c79f4bd4-dx7sf\",{\"micros\":83446,\"calls\":[\"get_user\",{\"micros\":15531},\"get_guilds\",{\"micros\":34892},\"send_scheduled_deletion_message\",{\"micros\":14},\"guild_join_requests\",{\"micros\":128},\"authorized_ip_coro\",{\"micros\":11},\"pending_payments\",{\"micros\":1260},\"apex_experiments\",{\"micros\":53495},\"sessions_experiments\",{\"micros\":17},\"user_activities\",{\"micros\":4},\"played_application_ids\",{\"micros\":4},\"linked_users\",{\"micros\":3},\"ad_personalization_toggles_disabled\",{\"micros\":4},\"regional_feature_config\",{\"micros\":3}]}]},\"starting_guild_connect\",{\"micros\":31,\"calls\":[]},\"presence_started\",{\"micros\":333,\"calls\":[]},\"guilds_started\",{\"micros\":55,\"calls\":[]},\"lobbies_started\",{\"micros\":1,\"calls\":[]},\"guilds_connect\",{\"micros\":2,\"calls\":[]},\"presence_connect\",{\"micros\":2468,\"calls\":[]},\"connect_finished\",{\"micros\":2484,\"calls\":[]},\"build_ready\",{\"micros\":10,\"calls\":[]},\"clean_ready\",{\"micros\":1,\"calls\":[]},\"optimize_ready\",{\"micros\":53,\"calls\":[]},\"split_ready\",{\"micros\":0,\"calls\":[]}]}]}]"]}
|
||||
[2026-02-14T17:28:44.114816+00:00] DiscordPHP.DEBUG: client created and session id stored {"session_id":"84f756fec6c7d47088527fadb37c1adb","user":{"id":"1471909193886859294","username":"AsepSahur","discriminator":"6954","global_name":null,"avatar":"https://cdn.discordapp.com/avatars/1471909193886859294/8a88b0710fa41f7eef469c3dedc30e27.webp?size=1024","bot":true,"system":null,"mfa_enabled":false,"banner":null,"accent_color":null,"locale":null,"verified":true,"email":null,"flags":0,"premium_type":null,"public_flags":null,"avatar_decoration_data":null,"collectibles":null,"primary_guild":null}}
|
||||
[2026-02-14T17:28:44.117231+00:00] DiscordPHP.INFO: stored guilds {"count":0,"unavailable":1}
|
||||
[2026-02-14T17:28:44.638804+00:00] DiscordPHP.DEBUG: guild available {"guild":"1428530728706117632","unavailable":1}
|
||||
[2026-02-14T17:28:44.869243+00:00] DiscordPHP.INFO: all guilds are now available {"count":1}
|
||||
[2026-02-14T17:28:44.869441+00:00] DiscordPHP.INFO: loadAllMembers option is disabled, not setting chunking up
|
||||
[2026-02-14T17:28:44.870230+00:00] DiscordPHP.INFO: voice class initialized
|
||||
[2026-02-14T17:28:44.870298+00:00] DiscordPHP.INFO: client is ready
|
||||
[2026-02-14T17:28:44.870346+00:00] DiscordPHP.INFO: The 'ready' event is deprecated and will be removed in a future version of DiscordPHP. Please use 'init' instead.
|
||||
Bot is ready!
|
||||
[2026-02-14T17:28:45.618209+00:00] DiscordPHP.DEBUG: BUCKET postapplications/:application_id/commands queued REQ POST applications/1471909193886859294/commands
|
||||
[2026-02-14T17:28:45.622843+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:28:45.640624+00:00] DiscordPHP.DEBUG: BUCKET postapplications/:application_id/commands queued REQ POST applications/1471909193886859294/commands
|
||||
[2026-02-14T17:28:45.640794+00:00] DiscordPHP.DEBUG: BUCKET postapplications/:application_id/commands queued REQ POST applications/1471909193886859294/commands
|
||||
[2026-02-14T17:28:45.640890+00:00] DiscordPHP.DEBUG: BUCKET postapplications/:application_id/commands queued REQ POST applications/1471909193886859294/commands
|
||||
[2026-02-14T17:28:45.641057+00:00] DiscordPHP.DEBUG: BUCKET postapplications/:application_id/commands queued REQ POST applications/1471909193886859294/commands
|
||||
[2026-02-14T17:28:45.641160+00:00] DiscordPHP.DEBUG: BUCKET postapplications/:application_id/commands queued REQ POST applications/1471909193886859294/commands
|
||||
[2026-02-14T17:28:45.641270+00:00] DiscordPHP.DEBUG: BUCKET postapplications/:application_id/commands queued REQ POST applications/1471909193886859294/commands
|
||||
[2026-02-14T17:28:45.863705+00:00] DiscordPHP.INFO: received session id for voice session {"guild":"1428530728706117632","session_id":"84f756fec6c7d47088527fadb37c1adb"}
|
||||
[2026-02-14T17:28:45.864342+00:00] DiscordPHP.INFO: received token and endpoint for voice session {"guild":"1428530728706117632","token":"*****","endpoint":"c-fra20-5f509e9b.discord.media:2083"}
|
||||
Unhandled promise rejection with TypeError: explode(): Argument #2 ($string) must be of type string, null given in /home/ubuntu/executor/workspace/vendor/discord-php-helpers/voice/src/Discord/Voice/Processes/ProcessAbstract.php:62
|
||||
Stack trace:
|
||||
#0 /home/ubuntu/executor/workspace/vendor/discord-php-helpers/voice/src/Discord/Voice/Processes/ProcessAbstract.php(62): explode()
|
||||
#1 /home/ubuntu/executor/workspace/vendor/discord-php-helpers/voice/src/Discord/Voice/Processes/Ffmpeg.php(55): Discord\Voice\Processes\ProcessAbstract::checkForExecutable()
|
||||
#2 /home/ubuntu/executor/workspace/vendor/discord-php-helpers/voice/src/Discord/Voice/VoiceClient.php(344): Discord\Voice\Processes\Ffmpeg::checkForFFmpeg()
|
||||
#3 /home/ubuntu/executor/workspace/vendor/discord-php-helpers/voice/src/Discord/Voice/VoiceClient.php(1381): Discord\Voice\VoiceClient->start()
|
||||
#4 /home/ubuntu/executor/workspace/vendor/discord-php-helpers/voice/src/Discord/Voice/VoiceClient.php(1425): Discord\Voice\VoiceClient->boot()
|
||||
#5 /home/ubuntu/executor/workspace/vendor/discord-php-helpers/voice/src/Discord/Voice/Manager.php(214): Discord\Voice\VoiceClient->setData()
|
||||
#6 /home/ubuntu/executor/workspace/vendor/discord-php-helpers/voice/src/Discord/Voice/Manager.php(112): Discord\Voice\Manager->serverUpdate()
|
||||
#7 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): Discord\Voice\Manager->Discord\Voice\{closure}()
|
||||
#8 /home/ubuntu/executor/workspace/vendor/team-reflex/discord-php/src/Discord/Discord.php(913): Discord\Discord->emit()
|
||||
#9 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): Discord\Discord->Discord\{closure}()
|
||||
#10 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(174): React\Promise\Internal\FulfilledPromise->then()
|
||||
#11 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#12 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#13 /home/ubuntu/executor/workspace/vendor/react/promise/src/Deferred.php(45): React\Promise\Promise::React\Promise\{closure}()
|
||||
#14 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): React\Promise\Deferred->resolve()
|
||||
#15 /home/ubuntu/executor/workspace/vendor/team-reflex/discord-php/src/Discord/Discord.php(946): React\Promise\Internal\FulfilledPromise->then()
|
||||
#16 /home/ubuntu/executor/workspace/vendor/team-reflex/discord-php/src/Discord/Discord.php(794): Discord\Discord->handleDispatch()
|
||||
#17 /home/ubuntu/executor/workspace/vendor/team-reflex/discord-php/src/Discord/Discord.php(748): Discord\Discord->processWsMessage()
|
||||
#18 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): Discord\Discord->handleWsMessage()
|
||||
#19 /home/ubuntu/executor/workspace/vendor/ratchet/pawl/src/WebSocket.php(72): Ratchet\Client\WebSocket->emit()
|
||||
#20 /home/ubuntu/executor/workspace/vendor/ratchet/rfc6455/src/Messaging/MessageBuffer.php(233): Ratchet\Client\WebSocket->Ratchet\Client\{closure}()
|
||||
#21 /home/ubuntu/executor/workspace/vendor/ratchet/rfc6455/src/Messaging/MessageBuffer.php(179): Ratchet\RFC6455\Messaging\MessageBuffer->processData()
|
||||
#22 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): Ratchet\RFC6455\Messaging\MessageBuffer->onData()
|
||||
#23 /home/ubuntu/executor/workspace/vendor/react/stream/src/Util.php(71): Evenement\EventEmitter->emit()
|
||||
#24 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Stream\Util::React\Stream\{closure}()
|
||||
#25 /home/ubuntu/executor/workspace/vendor/react/stream/src/DuplexResourceStream.php(209): Evenement\EventEmitter->emit()
|
||||
#26 /home/ubuntu/executor/workspace/vendor/react/event-loop/src/StreamSelectLoop.php(246): React\Stream\DuplexResourceStream->handleData()
|
||||
#27 /home/ubuntu/executor/workspace/vendor/react/event-loop/src/StreamSelectLoop.php(213): React\EventLoop\StreamSelectLoop->waitForStreamActivity()
|
||||
#28 /home/ubuntu/executor/workspace/vendor/team-reflex/discord-php/src/Discord/Discord.php(1822): React\EventLoop\StreamSelectLoop->run()
|
||||
#29 /home/ubuntu/executor/workspace/bot.php(230): Discord\Discord->run()
|
||||
#30 {main}
|
||||
[2026-02-14T17:28:48.616884+00:00] DiscordPHP.DEBUG: REQ POST applications/1471909193886859294/commands successful
|
||||
[2026-02-14T17:28:48.617013+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:28:48.617133+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:28:48.863972+00:00] DiscordPHP.DEBUG: REQ POST applications/1471909193886859294/commands successful
|
||||
[2026-02-14T17:28:48.864090+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:28:48.864191+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:28:49.114102+00:00] DiscordPHP.DEBUG: REQ POST applications/1471909193886859294/commands successful
|
||||
[2026-02-14T17:28:49.114215+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:28:49.114312+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:28:49.364389+00:00] DiscordPHP.DEBUG: REQ POST applications/1471909193886859294/commands successful
|
||||
[2026-02-14T17:28:49.364510+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:28:49.364591+00:00] DiscordPHP.INFO: BUCKET postapplications/:application_id/commands expecting rate limit, timer interval 18740 ms
|
||||
[2026-02-14T17:29:04.628833+00:00] DiscordPHP.DEBUG: BUCKET postinteractions/:interaction_id/:interaction_token/callback queued REQ POST interactions/1472283518062297088/aW50ZXJhY3Rpb246MTQ3MjI4MzUxODA2MjI5NzA4ODpFUXVwaWRGbFVESXUzbFlOSENnWEc3ZVBxeThjMkIyVTF4OURwbTR2Wjlhb1hiZzRRSm12SVR3SzNYa05VS01SQ1UzYVhtYlJDZDdpN29aYWlhekpWb3RUQ3duUWhUYXZtaVRhMXBZZEtBVk80SklBQVZ3YmtCTDhncTg1Tmp2UA/callback
|
||||
[2026-02-14T17:29:04.628966+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:29:07.366211+00:00] DiscordPHP.WARNING: REQ POST interactions/1472283518062297088/aW50ZXJhY3Rpb246MTQ3MjI4MzUxODA2MjI5NzA4ODpFUXVwaWRGbFVESXUzbFlOSENnWEc3ZVBxeThjMkIyVTF4OURwbTR2Wjlhb1hiZzRRSm12SVR3SzNYa05VS01SQ1UzYVhtYlJDZDdpN29aYWlhekpWb3RUQ3duUWhUYXZtaVRhMXBZZEtBVk80SklBQVZ3YmtCTDhncTg1Tmp2UA/callback failed: Discord\Http\Exceptions\BadRequestException: Bad Request - {
|
||||
"message": "Interaction has already been acknowledged.",
|
||||
"code": 40060
|
||||
} in /home/ubuntu/executor/workspace/vendor/discord-php/http/src/Discord/HttpTrait.php:451
|
||||
Stack trace:
|
||||
#0 /home/ubuntu/executor/workspace/vendor/discord-php/http/src/Discord/HttpTrait.php(287): Discord\Http\Http->handleError()
|
||||
#1 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): Discord\Http\Http->Discord\Http\{closure}()
|
||||
#2 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(174): React\Promise\Internal\FulfilledPromise->then()
|
||||
#3 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#4 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#5 /home/ubuntu/executor/workspace/vendor/react/promise/src/Deferred.php(45): React\Promise\Promise::React\Promise\{closure}()
|
||||
#6 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/Transaction.php(90): React\Promise\Deferred->[2026-02-14T17:29:07.879358+00:00] DiscordPHP.DEBUG: sending heartbeat {"seq":10}
|
||||
[2026-02-14T17:29:08.114567+00:00] DiscordPHP.DEBUG: received heartbeat ack {"response_time":234.9832057952881}
|
||||
[2026-02-14T17:29:12.363130+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:29:14.363657+00:00] DiscordPHP.DEBUG: REQ POST applications/1471909193886859294/commands successful
|
||||
[2026-02-14T17:29:14.377102+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
[2026-02-14T17:29:22.619772+00:00] DiscordPHP.DEBUG: BUCKET postinteractions/:interaction_id/:interaction_token/callback queued REQ POST interactions/1472283593102327903/aW50ZXJhY3Rpb246MTQ3MjI4MzU5MzEwMjMyNzkwMzpiMHZYVExrTTl5eHE2TjRKZ2d6d1lETUc4a2xoc1dHaVM2OWlwMk9veUtESHIyR2VFaUxEOUdCNG16aEpsWll2eU9zNVdoR3F2dkxVWExrek42SkxCN3duR3FCYVp6WGhNb05oUUtSMGhRMDZhYjhPWjVjMzdPc3RLeTVKanpieg/callback
|
||||
[2026-02-14T17:29:22.619923+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:29:24.365387+00:00] DiscordPHP.WARNING: REQ POST interactions/1472283593102327903/aW50ZXJhY3Rpb246MTQ3MjI4MzU5MzEwMjMyNzkwMzpiMHZYVExrTTl5eHE2TjRKZ2d6d1lETUc4a2xoc1dHaVM2OWlwMk9veUtESHIyR2VFaUxEOUdCNG16aEpsWll2eU9zNVdoR3F2dkxVWExrek42SkxCN3duR3FCYVp6WGhNb05oUUtSMGhRMDZhYjhPWjVjMzdPc3RLeTVKanpieg/callback failed: Discord\Http\Exceptions\NotFoundException: Not Found - {
|
||||
"message": "Unknown interaction",
|
||||
"code": 10062
|
||||
} in /home/ubuntu/executor/workspace/vendor/discord-php/http/src/Discord/HttpTrait.php:457
|
||||
Stack trace:
|
||||
#0 /home/ubuntu/executor/workspace/vendor/discord-php/http/src/Discord/HttpTrait.php(287): Discord\Http\Http->handleError()
|
||||
#1 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): Discord\Http\Http->Discord\Http\{closure}()
|
||||
#2 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(174): React\Promise\Internal\FulfilledPromise->then()
|
||||
#3 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#4 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#5 /home/ubuntu/executor/workspace/vendor/react/promise/src/Deferred.php(45): React\Promise\Promise::React\Promise\{closure}()
|
||||
#6 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/Transaction.php(90): React\Promise\Deferred->resolve()
|
||||
#7 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): React\Http\Io\Transaction->React\Http\Io\{closure}()
|
||||
#8 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(174): React\Promise\Internal\FulfilledPromise->then()
|
||||
#9 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#10 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#11 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): React\Promise\Promise::React\Promise\{closure}()
|
||||
#12 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(181): React\Promise\Internal\FulfilledPromise->then()
|
||||
#13 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#14 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#15 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): React\Promise\Promise::React\Promise\{closure}()
|
||||
#16 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(178): React\Promise\Internal\FulfilledPromise->then()
|
||||
#17 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#18 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#19 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/Transaction.php(193): React\Promise\Promise::React\Promise\{closure}()
|
||||
#20 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Http\Io\Transaction->React\Http\Io\{closure}()
|
||||
#21 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/ReadableBodyStream.php(50): Evenement\EventEmitter->emit()
|
||||
#22 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/ReadableBodyStream.php(151): React\Http\Io\ReadableBodyStream->close()
|
||||
#23 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/ReadableBodyStream.php(33): React\Http\Io\ReadableBodyStream->handleEnd()
|
||||
#24 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Http\Io\ReadableBodyStream->React\Http\Io\{closure}()
|
||||
#25 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/CloseProtectionStream.php(96): Evenement\EventEmitter->emit()
|
||||
#26 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/ClientRequestStream.php(228): React\Http\Io\CloseProtectionStream->handleData()
|
||||
#27 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Http\Io\ClientRequestStream->handleData()
|
||||
#28 /home/ubuntu/executor/workspace/vendor/react/stream/src/Util.php(71): Evenement\EventEmitter->emit()
|
||||
#29 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Stream\Util::React\Stream\{closure}()
|
||||
#30 /home/ubuntu/executor/workspace/vendor/react/stream/src/DuplexResourceStream.php(209): Evenement\EventEmitter->emit()
|
||||
#31 /home/ubuntu/executor/workspace/vendor/react/event-loop/src/StreamSelectLoop.php(246): React\Stream\DuplexResourceStream->handleData()
|
||||
#32 /home/ubuntu/executor/workspace/vendor/react/event-loop/src/StreamSelectLoop.php(213): React\EventLoop\StreamSelectLoop->waitForStreamActivity()
|
||||
#33 /home/ubuntu/executor/workspace/vendor/team-reflex/discord-php/src/Discord/Discord.php(1822): React\EventLoop\StreamSelectLoop->run()
|
||||
#34 /home/ubuntu/executor/workspace/bot.php(230): Discord\Discord->run()
|
||||
#35 {main}
|
||||
[2026-02-14T17:29:24.365555+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
Unhandled promise rejection with Discord\Http\Exceptions\NotFoundException: Not Found - {
|
||||
"message": "Unknown interaction",
|
||||
"code": 10062
|
||||
} in /home/ubuntu/executor/workspace/vendor/discord-php/http/src/Discord/HttpTrait.php:457
|
||||
Stack trace:
|
||||
#0 /home/ubuntu/executor/workspace/vendor/discord-php/http/src/Discord/HttpTrait.php(287): Discord\Http\Http->handleError()
|
||||
#1 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): Discord\Http\Http->Discord\Http\{closure}()
|
||||
#2 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(174): React\Promise\Internal\FulfilledPromise->then()
|
||||
#3 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#4 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#5 /home/ubuntu/executor/workspace/vendor/react/promise/src/Deferred.php(45): React\Promise\Promise::React\Promise\{closure}()
|
||||
#6 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/Transaction.php(90): React\Promise\Deferred->resolve()
|
||||
#7 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): React\Http\Io\Transaction->React\Http\Io\{closure}()
|
||||
#8 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(174): React\Promise\Internal\FulfilledPromise->then()
|
||||
#9 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#10 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#11 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): React\Promise\Promise::React\Promise\{closure}()
|
||||
#12 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(181): React\Promise\Internal\FulfilledPromise->then()
|
||||
#13 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#14 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#15 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): React\Promise\Promise::React\Promise\{closure}()
|
||||
#16 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(178): React\Promise\Internal\FulfilledPromise->then()
|
||||
#17 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#18 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#19 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/Transaction.php(193): React\Promise\Promise::React\Promise\{closure}()
|
||||
#20 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Http\Io\Transaction->React\Http\Io\{closure}()
|
||||
#21 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/ReadableBodyStream.php(50): Evenement\EventEmitter->emit()
|
||||
#22 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/ReadableBodyStream.php(151): React\Http\Io\ReadableBodyStream->close()
|
||||
#23 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/ReadableBodyStream.php(33): React\Http\Io\ReadableBodyStream->handleEnd()
|
||||
#24 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Http\Io\ReadableBodyStream->React\Http\Io\{closure}()
|
||||
#25 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/CloseProtectionStream.php(96): Evenement\EventEmitter->emit()
|
||||
#26 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/ClientRequestStream.php(228): React\Http\Io\CloseProtectionStream->handleData()
|
||||
#27 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Http\Io\ClientRequestSt[2026-02-14T17:29:24.613787+00:00] DiscordPHP.DEBUG: REQ POST interactions/1472283593102327903/aW50ZXJhY3Rpb246MTQ3MjI4MzU5MzEwMjMyNzkwMzpiMHZYVExrTTl5eHE2TjRKZ2d6d1lETUc4a2xoc1dHaVM2OWlwMk9veUtESHIyR2VFaUxEOUdCNG16aEpsWll2eU9zNVdoR3F2dkxVWExrek42SkxCN3duR3FCYVp6WGhNb05oUUtSMGhRMDZhYjhPWjVjMzdPc3RLeTVKanpieg/callback successful
|
||||
[2026-02-14T17:29:24.613909+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:29:25.124345+00:00] DiscordPHP.DEBUG: sending heartbeat {"seq":11}
|
||||
[2026-02-14T17:29:25.362816+00:00] DiscordPHP.DEBUG: received heartbeat ack {"response_time":238.2359504699707}
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
[2026-02-14T17:29:43.873337+00:00] DiscordPHP.DEBUG: resetting payload count {"count":4}
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
[2026-02-14T17:30:06.375851+00:00] DiscordPHP.DEBUG: sending heartbeat {"seq":11}
|
||||
[2026-02-14T17:30:06.613000+00:00] DiscordPHP.DEBUG: received heartbeat ack {"response_time":236.83714866638184}
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
[2026-02-14T17:30:13.652063+00:00] DiscordPHP.DEBUG: BUCKET postinteractions/:interaction_id/:interaction_token/callback queued REQ POST interactions/1472283808261869610/aW50ZXJhY3Rpb246MTQ3MjI4MzgwODI2MTg2OTYxMDpYRTVaZ3pyQ0IxMzBDcFVMUzI0dWNvZ0hXYXh6T0JJR0J3akVHWmxUTDhRTGprZWhndTBTS1Y2eFJLM2RWWnR0SURyUUZwTGppOXZUb0pZdW03bzBkSlZ5dkphNlE1aFBOUEpDaEVUT3N4QjZPU2hZOVBTa2NmUVE1aUhxVDR6Mg/callback
|
||||
[2026-02-14T17:30:13.652248+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
[2026-02-14T17:30:15.613329+00:00] DiscordPHP.WARNING: REQ POST interactions/1472283808261869610/aW50ZXJhY3Rpb246MTQ3MjI4MzgwODI2MTg2OTYxMDpYRTVaZ3pyQ0IxMzBDcFVMUzI0dWNvZ0hXYXh6T0JJR0J3akVHWmxUTDhRTGprZWhndTBTS1Y2eFJLM2RWWnR0SURyUUZwTGppOXZUb0pZdW03bzBkSlZ5dkphNlE1aFBOUEpDaEVUT3N4QjZPU2hZOVBTa2NmUVE1aUhxVDR6Mg/callback failed: Discord\Http\Exceptions\BadRequestException: Bad Request - {
|
||||
"message": "Interaction has already been acknowledged.",
|
||||
"code": 40060
|
||||
} in /home/ubuntu/executor/workspace/vendor/discord-php/http/src/Discord/HttpTrait.php:451
|
||||
Stack trace:
|
||||
#0 /home/ubuntu/executor/workspace/vendor/discord-php/http/src/Discord/HttpTrait.php(287): Discord\Http\Http->handleError()
|
||||
#1Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
[2026-02-14T17:30:24.868270+00:00] DiscordPHP.DEBUG: BUCKET postinteractions/:interaction_id/:interaction_token/callback queued REQ POST interactions/1472283854537625784/aW50ZXJhY3Rpb246MTQ3MjI4Mzg1NDUzNzYyNTc4NDpWUldIRzVyVldKTnh2bGpTZE9DZm4xOG5kb3RLZDJkTWFTMU54ZktsUlpOaUpKYkVHT1ZQVDh0NEZRdkF5VEZxSThMVVk3V2hOQWZvS0szZnRNNEc0YktCck9VaXBBMWJWOGc4QURzTVhsdXRQY3ZUWFFCRUxXYXlBQ0Q0UzJLMQ/callback
|
||||
[2026-02-14T17:30:24.868386+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:30:26.365024+00:00] DiscordPHP.WARNING: REQ POST interactions/1472283854537625784/aW50ZXJhY3Rpb246MTQ3MjI4Mzg1NDUzNzYyNTc4NDpWUldIRzVyVldKTnh2bGpTZE9DZm4xOG5kb3RLZDJkTWFTMU54ZktsUlpOaUpKYkVHT1ZQVDh0NEZRdkF5VEZxSThMVVk3V2hOQWZvS0szZnRNNEc0YktCck9VaXBBMWJWOGc4QURzTVhsdXRQY3ZUWFFCRUxXYXlBQ0Q0UzJLMQ/callback failed: Discord\Http\Exceptions\BadRequestException: Bad Request - {
|
||||
"message": "Interaction has already been acknowledged.",
|
||||
"code": 40060
|
||||
} in /home/ubuntu/executor/workspace/vendor/discord-php/http/src/Discord/HttpTrait.php:451
|
||||
Stack trace:
|
||||
#0 /home/ubuntu/executor/workspace/vendor/discord-php/http/src/Discord/HttpTrait.php(287): Discord\Http\Http->handleError()
|
||||
#1 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): Discord\Http\Http->Discord\Http\{closure}()
|
||||
#2 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(174): React\Promise\Internal\FulfilledPromise->then()
|
||||
#3 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#4 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#5 /home/ubuntu/executor/workspace/vendor/react/promise/src/Deferred.php(45): React\Promise\Promise::React\Promise\{closure}()
|
||||
#6 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/Transaction.php(90): React\Promise\Deferred->resolve()
|
||||
#7 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): React\Http\Io\Transaction->React\Http\Io\{closure}()
|
||||
#8 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(174): React\Promise\Internal\FulfilledPromise->then()
|
||||
#9 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#10 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#11 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): React\Promise\Promise::React\Promise\{closure}()
|
||||
#12 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(181): React\Promise\Internal\FulfilledPromise->then()
|
||||
#13 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#14 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#15 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): React\Promise\Promise::React\Promise\{closure}()
|
||||
#16 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(178): React\Promise\Internal\FulfilledPromise->then()
|
||||
#17 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#18 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#19 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/Transaction.php(193): React\Promise\Promise::React\Promise\{closure}()
|
||||
#20 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Http\Io\Transaction->React\Http\Io\{closure}()
|
||||
#21 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/ReadableBodyStream.php(50): Evenement\EventEmitter->emit()
|
||||
#22 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/ReadableBodyStream.php(151): React\Http\Io\ReadableBodyStream->close()
|
||||
#23 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/ReadableBodyStream.php(33): React\Http\Io\ReadableBodyStream->handleEnd()
|
||||
#24 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Http\Io\ReadableBodyStream->React\Http\Io\{closure}()
|
||||
#25 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/CloseProtectionStream.php(96): Evenement\EventEmitter->emit()
|
||||
#26 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/ClientRequestStream.php(228): React\Http\Io\CloseProtectionStream->handleData()
|
||||
#27 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Http\Io\ClientRequestStream->handleData()
|
||||
#28 /home/ubuntu/executor/workspace/vendor/react/stream/src/Util.php(71): Evenement\EventEmitter->emit()
|
||||
#29 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Stream\Util::React\Stream\{closure}()
|
||||
#30 /home/ubuntu/executor/workspace/vendor/react/stream/src/DuplexResourceStream.php(209): Evenement\EventEmitter->emit()
|
||||
#31 /home/ubuntu/executor/workspace/vendor/react/event-loop/src/StreamSelectLoop.php(246): React\Stream\DuplexResourceStream->handleData()
|
||||
#32 /home/ubuntu/executor/workspace/vendor/react/event-loop/src/StreamSelectLoop.php(213): React\EventLoop\StreamSelectLoop->waitForStreamActivity()
|
||||
#33 /home/ubuntu/executor/workspace/vendor/team-reflex/discord-php/src/Discord/Discord.php(1822): React\EventLoop\StreamSelectLoop->run()
|
||||
#34 /home/ubuntu/executor/workspace/bot.php(230): Discord\Discord->run()
|
||||
#35 {main}
|
||||
[2026-02-14T17:30:26.365139+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
Unhandled promise rejection with Discord\Http\Exceptions\BadRequestException: Bad Request - {
|
||||
"message": "Interaction has already been acknowledged.",
|
||||
"code": 40060
|
||||
} in /home/ubuntu/executor/workspace/vendor/discord-php/http/src/Discord/HttpTrait.php:451
|
||||
Stack trace:
|
||||
#0 /home/ubuntu/executor/workspace/vendor/discord-php/http/src/Discord/HttpTrait.php(287): Discord\Http\Http->handleError()
|
||||
#1 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): Discord\Http\Http->Discord\Http\{closure}()
|
||||
#2 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(174): React\Promise\Internal\FulfilledPromise->then()
|
||||
#3 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#4 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#5 /home/ubuntu/executor/workspace/vendor/react/promise/src/Deferred.php(45): React\Promise\Promise::React\Promise\{closure}()
|
||||
#6 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/Transaction.php(90): React\Promise\Deferred->resolve()
|
||||
#7 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): React\Http\Io\Transaction->React\Http\Io\{closure}()
|
||||
#8 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(174): React\Promise\Internal\FulfilledPromise->then()
|
||||
#9 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#10 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#11 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): React\Promise\Promise::React\Promise\{closure}()
|
||||
#12 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(181): React\Promise\Internal\FulfilledPromise->then()
|
||||
#13 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#14 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#15 /home/ubuntu/executor/workspace/vendor/react/promise/src/Internal/FulfilledPromise.php(47): React\Promise\Promise::React\Promise\{closure}()
|
||||
#16 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(178): React\Promise\Internal\FulfilledPromise->then()
|
||||
#17 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(222): React\Promise\Promise::React\Promise\{closure}()
|
||||
#18 /home/ubuntu/executor/workspace/vendor/react/promise/src/Promise.php(287): React\Promise\Promise->settle()
|
||||
#19 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/Transaction.php(193): React\Promise\Promise::React\Promise\{closure}()
|
||||
#20 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Http\Io\Transaction->React\Http\Io\{closure}()
|
||||
#21 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/ReadableBodyStream.php(50): Evenement\EventEmitter->emit()
|
||||
#22 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/ReadableBodyStream.php(151): React\Http\Io\ReadableBodyStream->close()
|
||||
#23 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/ReadableBodyStream.php(33): React\Http\Io\ReadableBodyStream->handleEnd()
|
||||
#24 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Http\Io\ReadableBodyStream->React\Http\Io\{closure}()
|
||||
#25 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/CloseProtectionStream.php(96): Evenement\EventEmitter->emit()
|
||||
#26 /home/ubuntu/executor/workspace/vendor/react/http/src/Io/ClientRequestStream.php(228): React\Http\Io\CloseProtectionStream->handleData()
|
||||
#27 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Http\Io\ClientRequestStream->handleData()
|
||||
#28 /home/ubuntu/executor/workspace/vendor/react/stream/src/Util.php(71): Evenement\EventEmitter->emit()
|
||||
#29 /home/ubuntu/executor/workspace/vendor/evenement/evenement/src/EventEmitterTrait.php(143): React\Stream\Util::React\Stream\{closure}()
|
||||
#30 /home/ubuntu/executor/workspace/vendor/react/stream/src/DuplexResourceStream.php(209): Evenement\EventEmitter->emit()
|
||||
#31 /home/ubuntu/executor/workspace/vendor/react/event-loop/src/StreamSelectLoop.php(246): React\Stream\DuplexResourceStream->handleData()
|
||||
#32 /home/ubuntu/executor/workspace/vendor/react/event-loop/src/StreamSelectLoop.php(213): React\EventLoop\StreamSelectLoop->waitForStreamActivity()
|
||||
#33 /home/ubuntu/executor/workspace/vendor/team-reflCould not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
[2026-02-14T17:30:43.875969+00:00] DiscordPHP.DEBUG: resetting payload count {"count":1}
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
[2026-02-14T17:30:47.626211+00:00] DiscordPHP.DEBUG: sending heartbeat {"seq":15}
|
||||
[2026-02-14T17:30:47.864167+00:00] DiscordPHP.DEBUG: received heartbeat ack {"response_time":237.73479461669922}
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
:31:11.632401+00:00] DiscordPHP.DEBUG: sending heartbeat {"seq":16}
|
||||
[2026-02-14T17:31:11.867756+00:00] DiscordPHP.DEBUG: received heartbeat ack {"response_time":235.0931167602539}
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
278
bot.php
Normal file
278
bot.php
Normal file
@ -0,0 +1,278 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
use Discord\Discord;
|
||||
use Discord\Voice\VoiceClient;
|
||||
use Discord\Parts\Channel\Channel;
|
||||
use Discord\Parts\User\Member;
|
||||
use Discord\WebSockets\Event;
|
||||
use Discord\WebSockets\Intents;
|
||||
use Discord\Builders\CommandBuilder;
|
||||
use Discord\Builders\MessageBuilder;
|
||||
use Discord\Parts\Interactions\Interaction;
|
||||
use Discord\Parts\Interactions\Command\Option;
|
||||
|
||||
$db = db();
|
||||
$settings = $db->query("SELECT setting_key, setting_value FROM bot_settings")->fetchAll(PDO::FETCH_KEY_PAIR);
|
||||
|
||||
$token = $settings['bot_token'] ?? '';
|
||||
$vcId = $settings['voice_channel_id'] ?? '1457687430189682781';
|
||||
$sahurTime = $settings['sahur_time'] ?? '03:00';
|
||||
$sahurSource = $settings['sahur_source'] ?? 'sahur.mp3';
|
||||
|
||||
if (empty($token)) {
|
||||
echo "Error: Bot token is missing in settings.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$discord = new Discord([
|
||||
'token' => $token,
|
||||
'intents' => Intents::getDefaultIntents() | Intents::GUILD_VOICE_STATES | Intents::MESSAGE_CONTENT,
|
||||
]);
|
||||
|
||||
$voiceClient = null;
|
||||
$queue = [];
|
||||
$currentTrack = null;
|
||||
|
||||
function logToDb($message, $level = 'info') {
|
||||
$db = db();
|
||||
$stmt = $db->prepare("INSERT INTO bot_logs (message, log_level) VALUES (?, ?)");
|
||||
$stmt->execute([$message, $level]);
|
||||
}
|
||||
|
||||
$discord->on('ready', function (Discord $discord) use (&$voiceClient, $vcId, $sahurTime) {
|
||||
echo "Bot is ready!", PHP_EOL;
|
||||
logToDb("Bot is online and ready.");
|
||||
|
||||
// Update status in DB
|
||||
$db = db();
|
||||
$db->prepare("UPDATE bot_settings SET setting_value = 'online' WHERE setting_key = 'bot_status'")->execute();
|
||||
|
||||
// Auto-join VC
|
||||
joinVoiceChannel($discord, $vcId);
|
||||
|
||||
// Schedule Sahur
|
||||
$discord->getLoop()->addPeriodicTimer(60, function () use ($discord, $sahurTime, $vcId) {
|
||||
$now = date('H:i');
|
||||
if ($now === $sahurTime) {
|
||||
echo "Sahur time! Playing audio...\n";
|
||||
logToDb("Sahur time triggered. Playing audio.");
|
||||
playSahur($discord, $vcId);
|
||||
}
|
||||
});
|
||||
|
||||
// Register Slash Commands
|
||||
registerCommands($discord);
|
||||
});
|
||||
|
||||
function joinVoiceChannel(Discord $discord, $channelId, $interaction = null) {
|
||||
global $voiceClient;
|
||||
|
||||
if ($voiceClient) {
|
||||
$msg = "I am already in a voice channel: " . ($voiceClient->getChannel()->name ?? 'Unknown');
|
||||
if ($interaction) {
|
||||
$interaction->respondWithMessage(MessageBuilder::new()->setContent($msg));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$channel = $discord->getChannel($channelId);
|
||||
|
||||
// If channel not in cache, try to find it in guilds
|
||||
if (!$channel) {
|
||||
foreach ($discord->guilds as $guild) {
|
||||
$channel = $guild->channels->get('id', $channelId);
|
||||
if ($channel) break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($channel instanceof Channel && $channel->type === Channel::TYPE_VOICE) {
|
||||
if ($interaction) {
|
||||
$interaction->respondWithMessage(MessageBuilder::new()->setContent("Connecting to voice channel: " . $channel->name . "..."));
|
||||
}
|
||||
|
||||
$discord->joinVoiceChannel($channel)->then(function (VoiceClient $vc) use (&$voiceClient, $discord, $channelId, $channel, $interaction) {
|
||||
$voiceClient = $vc;
|
||||
echo "Joined voice channel: " . $channel->name, PHP_EOL;
|
||||
logToDb("Joined voice channel: " . $channel->name);
|
||||
|
||||
if ($interaction) {
|
||||
$interaction->updateOriginalResponse(MessageBuilder::new()->setContent("Successfully joined voice channel: " . $channel->name));
|
||||
}
|
||||
|
||||
$vc->on('error', function ($e) use ($discord, $channelId) {
|
||||
echo "Voice Error: " . $e->getMessage(), PHP_EOL;
|
||||
logToDb("Voice error: " . $e->getMessage(), 'error');
|
||||
});
|
||||
}, function ($e) use ($interaction) {
|
||||
$errorMsg = "Could not join voice channel: " . $e->getMessage();
|
||||
echo $errorMsg, PHP_EOL;
|
||||
logToDb($errorMsg, 'error');
|
||||
if ($interaction) {
|
||||
try {
|
||||
$interaction->updateOriginalResponse(MessageBuilder::new()->setContent($errorMsg));
|
||||
} catch (\Exception $ex) {
|
||||
// Fallback if update fails
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$errorMsg = "Voice channel not found or invalid ID: " . $channelId;
|
||||
if ($interaction) {
|
||||
$interaction->respondWithMessage(MessageBuilder::new()->setContent($errorMsg));
|
||||
}
|
||||
echo $errorMsg, PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
function playSahur(Discord $discord, $vcId) {
|
||||
global $voiceClient, $sahurSource;
|
||||
if (!$voiceClient) {
|
||||
joinVoiceChannel($discord, $vcId);
|
||||
// Wait a bit for connection
|
||||
$discord->getLoop()->addTimer(3, function() use ($discord, $vcId) {
|
||||
playSahur($discord, $vcId);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (filter_var($sahurSource, FILTER_VALIDATE_URL)) {
|
||||
echo "Playing sahur from URL: $sahurSource\n";
|
||||
$cmd = "yt-dlp -g -f bestaudio \"$sahurSource\"";
|
||||
exec($cmd, $output, $resultCode);
|
||||
if ($resultCode === 0 && !empty($output[0])) {
|
||||
$voiceClient->playRawStream($output[0]);
|
||||
}
|
||||
} else {
|
||||
if (!file_exists($sahurSource)) {
|
||||
$error = "Error: Audio file '$sahurSource' not found. Please upload it to the bot's root directory.";
|
||||
echo $error, PHP_EOL;
|
||||
logToDb($error, 'error');
|
||||
return;
|
||||
}
|
||||
echo "Playing sahur from file: $sahurSource\n";
|
||||
$voiceClient->playFile($sahurSource)->done(function() {
|
||||
echo "Finished playing sahur audio.\n";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerCommands(Discord $discord) {
|
||||
$commands = [
|
||||
CommandBuilder::new()
|
||||
->setName('join')
|
||||
->setDescription('Join the default voice channel'),
|
||||
CommandBuilder::new()
|
||||
->setName('out')
|
||||
->setDescription('Leave the voice channel'),
|
||||
CommandBuilder::new()
|
||||
->setName('status')
|
||||
->setDescription('Check bot status'),
|
||||
CommandBuilder::new()
|
||||
->setName('testsahur')
|
||||
->setDescription('Test play sahur audio now'),
|
||||
CommandBuilder::new()
|
||||
->setName('play')
|
||||
->setDescription('Play music from URL')
|
||||
->addOption((new Option($discord))
|
||||
->setName('url')
|
||||
->setDescription('The URL of the song (YouTube, SoundCloud, etc.)')
|
||||
->setType(Option::STRING)
|
||||
->setRequired(true)),
|
||||
CommandBuilder::new()
|
||||
->setName('skip')
|
||||
->setDescription('Skip current song'),
|
||||
CommandBuilder::new()
|
||||
->setName('stop')
|
||||
->setDescription('Stop music and clear queue'),
|
||||
];
|
||||
|
||||
foreach ($commands as $command) {
|
||||
$discord->application->commands->save(
|
||||
$discord->application->commands->create($command->toArray())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$discord->on(Event::INTERACTION_CREATE, function (Interaction $interaction, Discord $discord) use (&$voiceClient, $vcId) {
|
||||
$command = $interaction->data->name;
|
||||
|
||||
switch ($command) {
|
||||
case 'join':
|
||||
$userChannel = $interaction->member->getVoiceChannel();
|
||||
if ($userChannel) {
|
||||
joinVoiceChannel($discord, $userChannel->id, $interaction);
|
||||
} else {
|
||||
joinVoiceChannel($discord, $vcId, $interaction);
|
||||
}
|
||||
break;
|
||||
case 'out':
|
||||
if ($voiceClient) {
|
||||
$channelName = $voiceClient->getChannel()->name ?? 'channel';
|
||||
$voiceClient->close();
|
||||
$voiceClient = null;
|
||||
$interaction->respondWithMessage(MessageBuilder::new()->setContent("Left voice channel: $channelName."));
|
||||
} else {
|
||||
$interaction->respondWithMessage(MessageBuilder::new()->setContent("I'm not in a voice channel."));
|
||||
}
|
||||
break;
|
||||
case 'status':
|
||||
$vcStatus = $voiceClient ? "Connected to: " . ($voiceClient->getChannel()->name ?? 'Unknown') : "Not connected to any voice channel.";
|
||||
$status = "Bot is online.\n$vcStatus\nTarget default VC: $vcId";
|
||||
$interaction->respondWithMessage(MessageBuilder::new()->setContent($status));
|
||||
break;
|
||||
case 'testsahur':
|
||||
playSahur($discord, $vcId);
|
||||
$interaction->respondWithMessage(MessageBuilder::new()->setContent("Testing sahur audio..."));
|
||||
break;
|
||||
case 'play':
|
||||
$url = $interaction->data->options['url']->value;
|
||||
handlePlay($interaction, $discord, $url);
|
||||
break;
|
||||
case 'skip':
|
||||
if ($voiceClient) {
|
||||
$voiceClient->stop();
|
||||
$interaction->respondWithMessage(MessageBuilder::new()->setContent("Skipped."));
|
||||
}
|
||||
break;
|
||||
case 'stop':
|
||||
global $queue;
|
||||
$queue = [];
|
||||
if ($voiceClient) {
|
||||
$voiceClient->stop();
|
||||
}
|
||||
$interaction->respondWithMessage(MessageBuilder::new()->setContent("Stopped and cleared queue."));
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
function handlePlay(Interaction $interaction, Discord $discord, string $url) {
|
||||
global $voiceClient, $queue, $vcId;
|
||||
|
||||
if (!$voiceClient) {
|
||||
$interaction->respondWithMessage(MessageBuilder::new()->setContent("I need to be in a voice channel first. use `/join`"));
|
||||
return;
|
||||
}
|
||||
|
||||
$interaction->acknowledge();
|
||||
|
||||
// Simple play logic using yt-dlp
|
||||
$cmd = "yt-dlp -g -f bestaudio \"$url\"";
|
||||
exec($cmd, $output, $resultCode);
|
||||
|
||||
if ($resultCode === 0 && !empty($output[0])) {
|
||||
$audioUrl = $output[0];
|
||||
$voiceClient->playRawStream($audioUrl)->done(function() use ($interaction) {
|
||||
// Logic for next in queue could go here
|
||||
});
|
||||
$interaction->updateOriginalResponse(MessageBuilder::new()->setContent("Now playing: $url"));
|
||||
} else {
|
||||
$interaction->updateOriginalResponse(MessageBuilder::new()->setContent("Error: Could not fetch audio for that URL."));
|
||||
}
|
||||
}
|
||||
|
||||
$discord->run();
|
||||
123
bot_output.log
Normal file
123
bot_output.log
Normal file
@ -0,0 +1,123 @@
|
||||
[2026-02-14T17:34:29.469151+00:00] DiscordPHP.DEBUG: Initializing DiscordPHP v10.46.0 (DiscordPHP-Http: v10.8.0 & Gateway: v10) on PHP 8.2.29
|
||||
[2026-02-14T17:34:29.968583+00:00] DiscordPHP.DEBUG: BUCKET getapplications/@me queued REQ GET applications/@me
|
||||
[2026-02-14T17:34:29.968742+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:34:29.990065+00:00] DiscordPHP.DEBUG: BUCKET getgateway/bot queued REQ GET gateway/bot
|
||||
[2026-02-14T17:34:29.990183+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":1,"empty":true}
|
||||
[2026-02-14T17:34:32.473338+00:00] DiscordPHP.DEBUG: REQ GET gateway/bot successful
|
||||
[2026-02-14T17:34:32.473473+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":1,"empty":true}
|
||||
[2026-02-14T17:34:32.475855+00:00] DiscordPHP.INFO: gateway retrieved and set {"gateway":"wss://gateway.discord.gg/?v=10&encoding=json&compress=zlib-stream","session":{"total":1000,"remaining":997,"reset_after":86034402,"max_concurrency":1}}
|
||||
[2026-02-14T17:34:32.475972+00:00] DiscordPHP.DEBUG: session data received {"session":{"total":1000,"remaining":997,"reset_after":86034402,"max_concurrency":1}}
|
||||
[2026-02-14T17:34:32.476017+00:00] DiscordPHP.INFO: starting connection to websocket {"gateway":"wss://gateway.discord.gg/?v=10&encoding=json&compress=zlib-stream"}
|
||||
[2026-02-14T17:34:32.742743+00:00] DiscordPHP.DEBUG: REQ GET applications/@me successful
|
||||
[2026-02-14T17:34:32.742880+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:34:33.237129+00:00] DiscordPHP.INFO: websocket connection has been created
|
||||
[2026-02-14T17:34:33.468708+00:00] DiscordPHP.INFO: received hello
|
||||
[2026-02-14T17:34:33.468844+00:00] DiscordPHP.DEBUG: sending heartbeat {"seq":null}
|
||||
[2026-02-14T17:34:33.469003+00:00] DiscordPHP.INFO: heartbeat timer initialized {"interval":41250.0}
|
||||
[2026-02-14T17:34:33.469063+00:00] DiscordPHP.INFO: identifying {"payload":{"op":2,"d":{"token":"*****","properties":{"os":"Linux","browser":"DiscordBot (https://github.com/discord-php/DiscordPHP-HTTP, v10.8.0)","device":"DiscordBot (https://github.com/discord-php/DiscordPHP-HTTP, v10.8.0)","referrer":"https://github.com/discord-php/DiscordPHP","referring_domain":"https://github.com/discord-php/DiscordPHP"},"compress":true,"intents":53608189}}}
|
||||
[2026-02-14T17:34:33.714362+00:00] DiscordPHP.DEBUG: received heartbeat ack {"response_time":245.3451156616211}
|
||||
[2026-02-14T17:34:33.714619+00:00] DiscordPHP.DEBUG: ready packet received
|
||||
[2026-02-14T17:34:33.714682+00:00] DiscordPHP.DEBUG: resume_gateway_url received {"url":"wss://gateway-us-east1-d.discord.gg"}
|
||||
[2026-02-14T17:34:33.714724+00:00] DiscordPHP.DEBUG: discord trace received {"trace":["[\"gateway-prd-arm-us-east1-d-w99h\",{\"micros\":136287,\"calls\":[\"id_created\",{\"micros\":756,\"calls\":[]},\"session_lookup_time\",{\"micros\":406,\"calls\":[]},\"session_lookup_finished\",{\"micros\":8,\"calls\":[]},\"discord-sessions-prd-2-153\",{\"micros\":134815,\"calls\":[\"start_session\",{\"micros\":120390,\"calls\":[\"discord-api-rpc-66c79f4bd4-wn8x4\",{\"micros\":42287,\"calls\":[\"get_user\",{\"micros\":7101},\"get_guilds\",{\"micros\":12443},\"send_scheduled_deletion_message\",{\"micros\":15},\"guild_join_requests\",{\"micros\":1445},\"authorized_ip_coro\",{\"micros\":8},\"pending_payments\",{\"micros\":1460},\"apex_experiments\",{\"micros\":69210},\"sessions_experiments\",{\"micros\":6},\"user_activities\",{\"micros\":3},\"played_application_ids\",{\"micros\":3},\"linked_users\",{\"micros\":2},\"ad_personalization_toggles_disabled\",{\"micros\":3},\"regional_feature_config\",{\"micros\":2}]}]},\"starting_guild_connect\",{\"micros\":32,\"calls\":[]},\"presence_started\",{\"micros\":307,\"calls\":[]},\"guilds_started\",{\"micros\":70,\"calls\":[]},\"lobbies_started\",{\"micros\":1,\"calls\":[]},\"guilds_connect\",{\"micros\":1,\"calls\":[]},\"presence_connect\",{\"micros\":13941,\"calls\":[]},\"connect_finished\",{\"micros\":13957,\"calls\":[]},\"build_ready\",{\"micros\":14,\"calls\":[]},\"clean_ready\",{\"micros\":0,\"calls\":[]},\"optimize_ready\",{\"micros\":1,\"calls\":[]},\"split_ready\",{\"micros\":42,\"calls\":[]}]}]}]"]}
|
||||
[2026-02-14T17:34:33.722205+00:00] DiscordPHP.DEBUG: client created and session id stored {"session_id":"482756b966dea44fe65ffe101b18b3f6","user":{"id":"1471909193886859294","username":"AsepSahur","discriminator":"6954","global_name":null,"avatar":"https://cdn.discordapp.com/avatars/1471909193886859294/8a88b0710fa41f7eef469c3dedc30e27.webp?size=1024","bot":true,"system":null,"mfa_enabled":false,"banner":null,"accent_color":null,"locale":null,"verified":true,"email":null,"flags":0,"premium_type":null,"public_flags":null,"avatar_decoration_data":null,"collectibles":null,"primary_guild":null}}
|
||||
[2026-02-14T17:34:33.728414+00:00] DiscordPHP.INFO: stored guilds {"count":0,"unavailable":1}
|
||||
[2026-02-14T17:34:34.222915+00:00] DiscordPHP.DEBUG: guild available {"guild":"1428530728706117632","unavailable":1}
|
||||
[2026-02-14T17:34:34.226546+00:00] DiscordPHP.INFO: all guilds are now available {"count":1}
|
||||
[2026-02-14T17:34:34.226691+00:00] DiscordPHP.INFO: loadAllMembers option is disabled, not setting chunking up
|
||||
[2026-02-14T17:34:34.228423+00:00] DiscordPHP.INFO: voice class initialized
|
||||
[2026-02-14T17:34:34.228507+00:00] DiscordPHP.INFO: client is ready
|
||||
[2026-02-14T17:34:34.228549+00:00] DiscordPHP.INFO: The 'ready' event is deprecated and will be removed in a future version of DiscordPHP. Please use 'init' instead.
|
||||
Bot is ready!
|
||||
[2026-02-14T17:34:34.483250+00:00] DiscordPHP.DEBUG: BUCKET postapplications/:application_id/commands queued REQ POST applications/1471909193886859294/commands
|
||||
[2026-02-14T17:34:34.483360+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:34:34.485450+00:00] DiscordPHP.DEBUG: BUCKET postapplications/:application_id/commands queued REQ POST applications/1471909193886859294/commands
|
||||
[2026-02-14T17:34:34.486602+00:00] DiscordPHP.DEBUG: BUCKET postapplications/:application_id/commands queued REQ POST applications/1471909193886859294/commands
|
||||
[2026-02-14T17:34:34.487055+00:00] DiscordPHP.DEBUG: BUCKET postapplications/:application_id/commands queued REQ POST applications/1471909193886859294/commands
|
||||
[2026-02-14T17:34:34.487593+00:00] DiscordPHP.DEBUG: BUCKET postapplications/:application_id/commands queued REQ POST applications/1471909193886859294/commands
|
||||
[2026-02-14T17:34:34.487713+00:00] DiscordPHP.DEBUG: BUCKET postapplications/:application_id/commands queued REQ POST applications/1471909193886859294/commands
|
||||
[2026-02-14T17:34:34.488476+00:00] DiscordPHP.DEBUG: BUCKET postapplications/:application_id/commands queued REQ POST applications/1471909193886859294/commands
|
||||
[2026-02-14T17:34:34.714902+00:00] DiscordPHP.INFO: received session id for voice session {"guild":"1428530728706117632","session_id":"482756b966dea44fe65ffe101b18b3f6"}
|
||||
[2026-02-14T17:34:34.717132+00:00] DiscordPHP.INFO: received token and endpoint for voice session {"guild":"1428530728706117632","token":"*****","endpoint":"c-fra20-5f509e9b.discord.media:2083"}
|
||||
[2026-02-14T17:34:34.733765+00:00] DiscordPHP.DEBUG: Creating new voice websocket {"endpoint":"c-fra20-5f509e9b.discord.media:2083"}
|
||||
[2026-02-14T17:34:38.231445+00:00] DiscordPHP.DEBUG: REQ POST applications/1471909193886859294/commands successful
|
||||
[2026-02-14T17:34:38.235940+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:34:38.238993+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:34:38.463643+00:00] DiscordPHP.DEBUG: REQ POST applications/1471909193886859294/commands successful
|
||||
[2026-02-14T17:34:38.463750+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:34:38.463838+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:34:38.715045+00:00] DiscordPHP.DEBUG: REQ POST applications/1471909193886859294/commands successful
|
||||
[2026-02-14T17:34:38.715179+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:34:38.715294+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:34:38.963521+00:00] DiscordPHP.DEBUG: REQ POST applications/1471909193886859294/commands successful
|
||||
[2026-02-14T17:34:38.963648+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:34:38.963740+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:34:39.215534+00:00] DiscordPHP.DEBUG: REQ POST applications/1471909193886859294/commands successful
|
||||
[2026-02-14T17:34:39.215661+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:34:39.215738+00:00] DiscordPHP.INFO: BUCKET postapplications/:application_id/commands expecting rate limit, timer interval 18007 ms
|
||||
[2026-02-14T17:34:39.216420+00:00] DiscordPHP.DEBUG: connected to voice websocket
|
||||
[2026-02-14T17:34:39.218555+00:00] DiscordPHP.DEBUG: sending identify {"packet":{"op":0,"d":{"server_id":"1428530728706117632","user_id":"1471909193886859294","token":"*****","max_dave_protocol_version":0,"session_id":"482756b966dea44fe65ffe101b18b3f6"}}}
|
||||
[2026-02-14T17:34:39.219880+00:00] DiscordPHP.DEBUG: sending heartbeat
|
||||
[2026-02-14T17:34:39.347543+00:00] DiscordPHP.DEBUG: received voice ready packet {"data":{"streams":[{"type":"video","ssrc":1935,"rtx_ssrc":1936,"rid":"","quality":0,"active":false}],"ssrc":1934,"port":19314,"modes":["aead_aes256_gcm_rtpsize","aead_xchacha20_poly1305_rtpsize"],"ip":"104.29.147.190","experiments":["fixed_keyframe_interval"]}}
|
||||
[2026-02-14T17:34:39.464642+00:00] DiscordPHP.ERROR: error while connecting to udp {"e":"syntax error, unexpected identifier \"SILENCE_FRAME\", expecting \"=\""}
|
||||
[2026-02-14T17:34:39.464778+00:00] DiscordPHP.ERROR: error initializing voice manager {"e":"syntax error, unexpected identifier \"SILENCE_FRAME\", expecting \"=\""}
|
||||
Could not join voice channel: syntax error, unexpected identifier "SILENCE_FRAME", expecting "="
|
||||
[2026-02-14T17:34:39.466740+00:00] DiscordPHP.ERROR: error initializing voice client {"e":"syntax error, unexpected identifier \"SILENCE_FRAME\", expecting \"=\""}
|
||||
[2026-02-14T17:34:39.466928+00:00] DiscordPHP.DEBUG: received client connect packet {"data":{"Discord\\WebSockets\\Payload":{"op":11,"d":{"user_ids":["235088799074484224","830530156048285716","906246223504240641","923944350612848700","1414108278354608278"]}}}}
|
||||
[2026-02-14T17:34:39.469249+00:00] DiscordPHP.DEBUG: received speaking packet {"data":{"user_id":"235088799074484224","ssrc":228,"speaking":1}}
|
||||
[2026-02-14T17:34:39.469398+00:00] DiscordPHP.DEBUG: received speaking packet {"data":{"user_id":"830530156048285716","ssrc":243,"speaking":1}}
|
||||
[2026-02-14T17:34:39.470418+00:00] DiscordPHP.DEBUG: received flags packet {"data":{"attributes":{"user_id":"235088799074484224","flags":null},"created":true,"class":"Discord\\Voice\\Flags"}}
|
||||
[2026-02-14T17:34:39.470613+00:00] DiscordPHP.DEBUG: received flags packet {"data":{"attributes":{"user_id":"830530156048285716","flags":null},"created":true,"class":"Discord\\Voice\\Flags"}}
|
||||
[2026-02-14T17:34:39.470724+00:00] DiscordPHP.DEBUG: received flags packet {"data":{"attributes":{"user_id":"906246223504240641","flags":null},"created":true,"class":"Discord\\Voice\\Flags"}}
|
||||
[2026-02-14T17:34:39.470787+00:00] DiscordPHP.DEBUG: received flags packet {"data":{"attributes":{"user_id":"923944350612848700","flags":2},"created":true,"class":"Discord\\Voice\\Flags"}}
|
||||
[2026-02-14T17:34:39.470833+00:00] DiscordPHP.DEBUG: received flags packet {"data":{"attributes":{"user_id":"1414108278354608278","flags":2},"created":true,"class":"Discord\\Voice\\Flags"}}
|
||||
[2026-02-14T17:34:39.471638+00:00] DiscordPHP.DEBUG: received platform packet {"data":{"attributes":{"user_id":"235088799074484224","platform":null},"created":true,"class":"Discord\\Voice\\Platform"}}
|
||||
[2026-02-14T17:34:39.471761+00:00] DiscordPHP.DEBUG: received platform packet {"data":{"attributes":{"user_id":"830530156048285716","platform":null},"created":true,"class":"Discord\\Voice\\Platform"}}
|
||||
[2026-02-14T17:34:39.471828+00:00] DiscordPHP.DEBUG: received platform packet {"data":{"attributes":{"user_id":"906246223504240641","platform":null},"created":true,"class":"Discord\\Voice\\Platform"}}
|
||||
[2026-02-14T17:34:39.471874+00:00] DiscordPHP.DEBUG: received platform packet {"data":{"attributes":{"user_id":"923944350612848700","platform":1},"created":true,"class":"Discord\\Voice\\Platform"}}
|
||||
[2026-02-14T17:34:39.471994+00:00] DiscordPHP.DEBUG: received platform packet {"data":{"attributes":{"user_id":"1414108278354608278","platform":1},"created":true,"class":"Discord\\Voice\\Platform"}}
|
||||
[2026-02-14T17:34:39.472040+00:00] DiscordPHP.DEBUG: received heartbeat ack {"response_time":472.03588485717773}
|
||||
[2026-02-14T17:34:39.521773+00:00] DiscordPHP.INFO: received session id for voice session {"guild":"1428530728706117632","session_id":"482756b966dea44fe65ffe101b18b3f6"}
|
||||
[2026-02-14T17:34:39.714513+00:00] DiscordPHP.WARNING: voice websocket closed {"op":4014,"reason":"Disconnected."}
|
||||
[2026-02-14T17:34:39.714739+00:00] DiscordPHP.WARNING: received critical opcode - not reconnecting {"op":4014,"reason":"Disconnected."}
|
||||
[2026-02-14T17:34:52.979398+00:00] DiscordPHP.DEBUG: sending heartbeat
|
||||
[2026-02-14T17:34:57.225711+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:34:57.516904+00:00] DiscordPHP.DEBUG: REQ POST applications/1471909193886859294/commands successful
|
||||
[2026-02-14T17:34:57.517062+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:34:57.517159+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:34:57.614652+00:00] DiscordPHP.DEBUG: REQ POST applications/1471909193886859294/commands successful
|
||||
[2026-02-14T17:34:57.614778+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:34:59.731602+00:00] DiscordPHP.DEBUG: BUCKET postinteractions/:interaction_id/:interaction_token/callback queued REQ POST interactions/1472285008109764903/aW50ZXJhY3Rpb246MTQ3MjI4NTAwODEwOTc2NDkwMzp5ek81V2d5T1dmNzhDbjVoTVNmU1pmd0NLdnE0QjZ2ZDdCU2hIc21pUU9jc1hVcFFXT3I0YzVzRjlwdFBKTldIdjNrYmptYUNvMlNkNmRsYXN0WEVYRWNNOTZ6NVZMcmJmS2ZnVnc0SFdWa3JISVBDcTJSN21JYW5uQ2gwclBocA/callback
|
||||
[2026-02-14T17:34:59.731754+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":0,"empty":true}
|
||||
Could not join voice channel: You cannot join more than one voice channel per guild/server.
|
||||
[2026-02-14T17:34:59.735903+00:00] DiscordPHP.DEBUG: BUCKET patchwebhooks/:application_id/:interaction_token/messages/@original queued REQ PATCH webhooks/1471909193886859294/aW50ZXJhY3Rpb246MTQ3MjI4NTAwODEwOTc2NDkwMzp5ek81V2d5T1dmNzhDbjVoTVNmU1pmd0NLdnE0QjZ2ZDdCU2hIc21pUU9jc1hVcFFXT3I0YzVzRjlwdFBKTldIdjNrYmptYUNvMlNkNmRsYXN0WEVYRWNNOTZ6NVZMcmJmS2ZnVnc0SFdWa3JISVBDcTJSN21JYW5uQ2gwclBocA/messages/@original
|
||||
[2026-02-14T17:34:59.735998+00:00] DiscordPHP.DEBUG: http not checking interaction queue {"waiting":1,"empty":true}
|
||||
[2026-02-14T17:35:01.231064+00:00] DiscordPHP.DEBUG: REQ POST interactions/1472285008109764903/aW50ZXJhY3Rpb246MTQ3MjI4NTAwODEwOTc2NDkwMzp5ek81V2d5T1dmNzhDbjVoTVNmU1pmd0NLdnE0QjZ2ZDdCU2hIc21pUU9jc1hVcFFXT3I0YzVzRjlwdFBKTldIdjNrYmptYUNvMlNkNmRsYXN0WEVYRWNNOTZ6NVZMcmJmS2ZnVnc0SFdWa3JISVBDcTJSN21JYW5uQ2gwclBocA/callback successful
|
||||
[2026-02-14T17:35:01.231183+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":1,"empty":true}
|
||||
[2026-02-14T17:35:01.467159+00:00] DiscordPHP.DEBUG: REQ PATCH webhooks/1471909193886859294/aW50ZXJhY3Rpb246MTQ3MjI4NTAwODEwOTc2NDkwMzp5ek81V2d5T1dmNzhDbjVoTVNmU1pmd0NLdnE0QjZ2ZDdCU2hIc21pUU9jc1hVcFFXT3I0YzVzRjlwdFBKTldIdjNrYmptYUNvMlNkNmRsYXN0WEVYRWNNOTZ6NVZMcmJmS2ZnVnc0SFdWa3JISVBDcTJSN21JYW5uQ2gwclBocA/messages/@original successful
|
||||
[2026-02-14T17:35:01.467302+00:00] DiscordPHP.DEBUG: http not checking queue {"waiting":0,"empty":true}
|
||||
[2026-02-14T17:35:06.730579+00:00] DiscordPHP.DEBUG: sending heartbeat
|
||||
[2026-02-14T17:35:14.726139+00:00] DiscordPHP.DEBUG: sending heartbeat {"seq":8}
|
||||
[2026-02-14T17:35:14.773734+00:00] DiscordPHP.DEBUG: received heartbeat ack {"response_time":47.34015464782715}
|
||||
[2026-02-14T17:35:20.486026+00:00] DiscordPHP.DEBUG: sending heartbeat
|
||||
[2026-02-14T17:35:33.249495+00:00] DiscordPHP.DEBUG: resetting payload count {"count":5}
|
||||
[2026-02-14T17:35:34.236641+00:00] DiscordPHP.DEBUG: sending heartbeat
|
||||
[2026-02-14T17:35:47.988865+00:00] DiscordPHP.DEBUG: sending heartbeat
|
||||
[2026-02-14T17:35:55.978998+00:00] DiscordPHP.DEBUG: sending heartbeat {"seq":8}
|
||||
[2026-02-14T17:35:56.020302+00:00] DiscordPHP.DEBUG: received heartbeat ack {"response_time":41.02015495300293}
|
||||
[2026-02-14T17:36:01.739956+00:00] DiscordPHP.DEBUG: sending heartbeat
|
||||
[2026-02-14T17:36:15.492364+00:00] DiscordPHP.DEBUG: sending heartbeat
|
||||
[2026-02-14T17:36:29.249215+00:00] DiscordPHP.DEBUG: sending heartbeat
|
||||
[2026-02-14T17:36:33.250107+00:00] DiscordPHP.DEBUG: resetting payload count {"count":1}
|
||||
[2026-02-14T17:36:37.230128+00:00] DiscordPHP.DEBUG: sending heartbeat {"seq":11}
|
||||
[2026-02-14T17:36:37.286064+00:00] DiscordPHP.DEBUG: received heartbeat ack {"response_time":55.63092231750488}
|
||||
[2026-02-14T17:36:43.000574+00:00] DiscordPHP.DEBUG: sending heartbeat
|
||||
[2026-02-14T17:36:56.764775+00:00] DiscordPHP.DEBUG: sending heartbeat
|
||||
[2026-02-14T17:37:10.525293+00:00] DiscordPHP.DEBUG: sending heartbeat
|
||||
[2026-02-14T17:37:18.485572+00:00] DiscordPHP.DEBUG: sending heartbeat {"seq":11}
|
||||
[2026-02-14T17:37:18.525100+00:00] DiscordPHP.DEBUG: received heartbeat ack {"response_time":39.26587104797363}
|
||||
[2026-02-14T17:37:24.280203+00:00] DiscordPHP.DEBUG: sending heartbeat
|
||||
[2026-02-14T17:37:33.259032+00:00] DiscordPHP.DEBUG: resetting payload count {"count":2}
|
||||
[2026-02-14T17:37:38.031284+00:00] DiscordPHP.DEBUG: sending heartbeat
|
||||
[2026-02-14T17:37:51.789903+00:00] DiscordPHP.DEBUG: sending heartbeat
|
||||
[2026-02-14T17:37:59.736211+00:00] DiscordPHP.DEBUG: sending heartbeat {"seq":11}
|
||||
[2026-02-14T17:37:59.776711+00:00] DiscordPHP.DEBUG: received heartbeat ack {"response_time":40.16280174255371}
|
||||
5
composer.json
Normal file
5
composer.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"require": {
|
||||
"team-reflex/discord-php": "^10.45"
|
||||
}
|
||||
}
|
||||
2719
composer.lock
generated
Normal file
2719
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
279
index.php
279
index.php
@ -1,150 +1,149 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
@ini_set('display_errors', '1');
|
||||
@error_reporting(E_ALL);
|
||||
@date_default_timezone_set('UTC');
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
$phpVersion = PHP_VERSION;
|
||||
$now = date('Y-m-d H:i:s');
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>New Style</title>
|
||||
<?php
|
||||
// Read project preview data from environment
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
||||
$db = db();
|
||||
$settings = $db->query("SELECT setting_key, setting_value FROM bot_settings")->fetchAll(PDO::FETCH_KEY_PAIR);
|
||||
$logs = $db->query("SELECT * FROM bot_logs ORDER BY created_at DESC LIMIT 10")->fetchAll();
|
||||
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Discord Sahur Bot Management Dashboard';
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
?>
|
||||
<?php if ($projectDescription): ?>
|
||||
<!-- Meta description -->
|
||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
||||
<!-- Open Graph meta tags -->
|
||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<!-- Twitter meta tags -->
|
||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<?php endif; ?>
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<!-- Open Graph image -->
|
||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<!-- Twitter image -->
|
||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<?php endif; ?>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg-color-start: #6a11cb;
|
||||
--bg-color-end: #2575fc;
|
||||
--text-color: #ffffff;
|
||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
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>');
|
||||
animation: bg-pan 20s linear infinite;
|
||||
z-index: -1;
|
||||
}
|
||||
@keyframes bg-pan {
|
||||
0% { background-position: 0% 0%; }
|
||||
100% { background-position: 100% 100%; }
|
||||
}
|
||||
main {
|
||||
padding: 2rem;
|
||||
}
|
||||
.card {
|
||||
background: var(--card-bg-color);
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 16px;
|
||||
padding: 2rem;
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.loader {
|
||||
margin: 1.25rem auto 1.25rem;
|
||||
width: 48px;
|
||||
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>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Sahur Bot Dashboard</title>
|
||||
<?php if ($projectDescription): ?>
|
||||
<meta name="description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<?php endif; ?>
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<?php endif; ?>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
|
||||
<link href="assets/css/custom.css?v=<?= time() ?>" rel="stylesheet">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="card">
|
||||
<h1>Analyzing your requirements and generating your website…</h1>
|
||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||
<span class="sr-only">Loading…</span>
|
||||
</div>
|
||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-4">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="#"><i class="bi bi-robot"></i> Sahur Bot</a>
|
||||
<div class="ms-auto">
|
||||
<span class="badge <?= ($settings['bot_status'] ?? 'offline') === 'online' ? 'bg-success' : 'bg-danger' ?>">
|
||||
<i class="bi bi-circle-fill me-1"></i> <?= strtoupper($settings['bot_status'] ?? 'offline') ?>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||
</footer>
|
||||
</nav>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<!-- Configuration Section -->
|
||||
<div class="col-lg-8">
|
||||
<div class="card mb-4 shadow-sm">
|
||||
<div class="card-header bg-white">
|
||||
<h5 class="mb-0 font-weight-bold">Bot Configuration</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="configForm" action="save_config.php" method="POST">
|
||||
<div class="mb-3">
|
||||
<label for="bot_token" class="form-label">Discord Bot Token</label>
|
||||
<input type="password" class="form-control" id="bot_token" name="bot_token" value="<?= htmlspecialchars($settings['bot_token'] ?? '') ?>" placeholder="Enter your bot token">
|
||||
<div class="form-text text-muted">Never share your token with anyone.</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="voice_channel_id" class="form-label">Voice Channel ID</label>
|
||||
<input type="text" class="form-control" id="voice_channel_id" name="voice_channel_id" value="<?= htmlspecialchars($settings['voice_channel_id'] ?? '') ?>" required>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="sahur_time" class="form-label">Sahur Time (HH:MM)</label>
|
||||
<input type="time" class="form-control" id="sahur_time" name="sahur_time" value="<?= htmlspecialchars($settings['sahur_time'] ?? '03:00') ?>" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="sahur_source" class="form-label">Sahur Audio Source (URL or File Path)</label>
|
||||
<input type="text" class="form-control" id="sahur_source" name="sahur_source" value="<?= htmlspecialchars($settings['sahur_source'] ?? 'sahur.mp3') ?>" required>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<button type="submit" class="btn btn-primary">Save Configuration</button>
|
||||
<button type="button" id="btnTestSahur" class="btn btn-outline-secondary">Test Sahur Now</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bot Status & Actions -->
|
||||
<div class="card mb-4 shadow-sm">
|
||||
<div class="card-header bg-white">
|
||||
<h5 class="mb-0">Bot Management</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Manage the Discord bot service status.</p>
|
||||
<div class="d-grid gap-2 d-md-block">
|
||||
<button id="btnStartBot" class="btn btn-success me-md-2" type="button"><i class="bi bi-play-fill"></i> Start Bot</button>
|
||||
<button id="btnStopBot" class="btn btn-danger me-md-2" type="button"><i class="bi bi-stop-fill"></i> Stop Bot</button>
|
||||
<button id="btnRestartBot" class="btn btn-warning" type="button"><i class="bi bi-arrow-clockwise"></i> Restart</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Logs Section -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-white d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Activity Logs</h5>
|
||||
<button class="btn btn-sm btn-link p-0" id="refreshLogs"><i class="bi bi-arrow-repeat"></i></button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="list-group list-group-flush" id="logList">
|
||||
<?php if (empty($logs)): ?>
|
||||
<div class="p-3 text-center text-muted">No activity logs found.</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($logs as $log): ?>
|
||||
<div class="list-group-item">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<small class="text-muted"><?= $log['created_at'] ?></small>
|
||||
<span class="badge bg-<?= $log['log_level'] === 'error' ? 'danger' : ($log['log_level'] === 'warning' ? 'warning' : 'info') ?>"><?= $log['log_level'] ?></span>
|
||||
</div>
|
||||
<p class="mb-1 small"><?= htmlspecialchars($log['message']) ?></p>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="mt-5 py-4 bg-light border-top">
|
||||
<div class="container text-center text-muted">
|
||||
<small>© <?= date('Y') ?> Sahur Discord Bot Dashboard. Built with LAMP.</small>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<div class="position-fixed bottom-0 end-0 p-3" style="z-index: 11">
|
||||
<div id="liveToast" class="toast hide" role="alert" aria-live="assertive" aria-atomic="true">
|
||||
<div class="toast-header">
|
||||
<i class="bi bi-info-circle me-2"></i>
|
||||
<strong class="me-auto">Notification</strong>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="toast-body" id="toastMessage"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="assets/js/main.js?v=<?= time() ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
73
manage_bot.php
Normal file
73
manage_bot.php
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$action = $_GET['action'] ?? '';
|
||||
$pidFile = __DIR__ . '/bot.pid';
|
||||
$botScript = __DIR__ . '/bot.php';
|
||||
|
||||
$response = ['success' => false];
|
||||
|
||||
function isBotRunning($pidFile) {
|
||||
if (!file_exists($pidFile)) return false;
|
||||
$pid = (int)file_get_contents($pidFile);
|
||||
return posix_getpgid($pid) !== false;
|
||||
}
|
||||
|
||||
if ($action === 'start') {
|
||||
if (isBotRunning($pidFile)) {
|
||||
$response['error'] = 'Bot is already running.';
|
||||
} else {
|
||||
$logFile = __DIR__ . '/bot_output.log';
|
||||
$cmd = "nohup php $botScript > $logFile 2>&1 & echo $!";
|
||||
$pid = trim(shell_exec($cmd));
|
||||
if ($pid) {
|
||||
file_put_contents($pidFile, $pid);
|
||||
$response['success'] = true;
|
||||
// Log to DB
|
||||
$db = db();
|
||||
$db->prepare("INSERT INTO bot_logs (message, log_level) VALUES ('Bot started manually', 'info')")->execute();
|
||||
} else {
|
||||
$response['error'] = 'Failed to start bot.';
|
||||
}
|
||||
}
|
||||
} elseif ($action === 'stop') {
|
||||
if (file_exists($pidFile)) {
|
||||
$pid = (int)file_get_contents($pidFile);
|
||||
exec("kill $pid");
|
||||
unlink($pidFile);
|
||||
$response['success'] = true;
|
||||
// Update status in DB
|
||||
$db = db();
|
||||
$db->prepare("UPDATE bot_settings SET setting_value = 'offline' WHERE setting_key = 'bot_status'")->execute();
|
||||
$db->prepare("INSERT INTO bot_logs (message, log_level) VALUES ('Bot stopped manually', 'info')")->execute();
|
||||
} else {
|
||||
$response['error'] = 'Bot is not running.';
|
||||
}
|
||||
} elseif ($action === 'restart') {
|
||||
// Stop
|
||||
if (file_exists($pidFile)) {
|
||||
$pid = (int)file_get_contents($pidFile);
|
||||
exec("kill $pid");
|
||||
unlink($pidFile);
|
||||
}
|
||||
// Start
|
||||
$logFile = __DIR__ . '/bot_output.log';
|
||||
$cmd = "nohup php $botScript > $logFile 2>&1 & echo $!";
|
||||
$pid = trim(shell_exec($cmd));
|
||||
if ($pid) {
|
||||
file_put_contents($pidFile, $pid);
|
||||
$response['success'] = true;
|
||||
$db = db();
|
||||
$db->prepare("UPDATE bot_settings SET setting_value = 'online' WHERE setting_key = 'bot_status'")->execute();
|
||||
$db->prepare("INSERT INTO bot_logs (message, log_level) VALUES ('Bot restarted manually', 'info')")->execute();
|
||||
} else {
|
||||
$response['error'] = 'Failed to restart bot.';
|
||||
}
|
||||
} else {
|
||||
$response['error'] = 'Invalid action.';
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
31
save_config.php
Normal file
31
save_config.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['success' => false, 'error' => 'Invalid request method']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$token = $_POST['bot_token'] ?? '';
|
||||
$vc_id = $_POST['voice_channel_id'] ?? '';
|
||||
$time = $_POST['sahur_time'] ?? '';
|
||||
$source = $_POST['sahur_source'] ?? '';
|
||||
|
||||
try {
|
||||
$db = db();
|
||||
$stmt = $db->prepare("UPDATE bot_settings SET setting_value = ? WHERE setting_key = ?");
|
||||
|
||||
$stmt->execute([$token, 'bot_token']);
|
||||
$stmt->execute([$vc_id, 'voice_channel_id']);
|
||||
$stmt->execute([$time, 'sahur_time']);
|
||||
$stmt->execute([$source, 'sahur_source']);
|
||||
|
||||
// Log the change
|
||||
$logStmt = $db->prepare("INSERT INTO bot_logs (log_level, message) VALUES ('info', 'Settings updated by administrator')");
|
||||
$logStmt->execute();
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
18
test_sahur.php
Normal file
18
test_sahur.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
try {
|
||||
$db = db();
|
||||
$vc_id = $db->query("SELECT setting_value FROM bot_settings WHERE setting_key = 'voice_channel_id'")->fetchColumn();
|
||||
$source = $db->query("SELECT setting_value FROM bot_settings WHERE setting_key = 'sahur_source'")->fetchColumn();
|
||||
|
||||
$message = "Test Sahur triggered. Joining VC ID: $vc_id and playing from source: $source";
|
||||
|
||||
$stmt = $db->prepare("INSERT INTO bot_logs (log_level, message) VALUES ('info', ?)");
|
||||
$stmt->execute([$message]);
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
25
vendor/autoload.php
vendored
Normal file
25
vendor/autoload.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
if (PHP_VERSION_ID < 50600) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, $err);
|
||||
} elseif (!headers_sent()) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
$err,
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit3f15e11d83f664c614d99889af2eb047::getLoader();
|
||||
120
vendor/bin/carbon
vendored
Executable file
120
vendor/bin/carbon
vendored
Executable file
@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Proxy PHP file generated by Composer
|
||||
*
|
||||
* This file includes the referenced bin path (../nesbot/carbon/bin/carbon)
|
||||
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||
*
|
||||
* @generated
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||
|
||||
if (PHP_VERSION_ID < 80000) {
|
||||
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BinProxyWrapper
|
||||
{
|
||||
private $handle;
|
||||
private $position;
|
||||
private $realpath;
|
||||
|
||||
public function stream_open($path, $mode, $options, &$opened_path)
|
||||
{
|
||||
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||
$opened_path = substr($path, 17);
|
||||
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||
$opened_path = $this->realpath;
|
||||
$this->handle = fopen($this->realpath, $mode);
|
||||
$this->position = 0;
|
||||
|
||||
return (bool) $this->handle;
|
||||
}
|
||||
|
||||
public function stream_read($count)
|
||||
{
|
||||
$data = fread($this->handle, $count);
|
||||
|
||||
if ($this->position === 0) {
|
||||
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||
}
|
||||
|
||||
$this->position += strlen($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function stream_cast($castAs)
|
||||
{
|
||||
return $this->handle;
|
||||
}
|
||||
|
||||
public function stream_close()
|
||||
{
|
||||
fclose($this->handle);
|
||||
}
|
||||
|
||||
public function stream_lock($operation)
|
||||
{
|
||||
return $operation ? flock($this->handle, $operation) : true;
|
||||
}
|
||||
|
||||
public function stream_seek($offset, $whence)
|
||||
{
|
||||
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||
$this->position = ftell($this->handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function stream_tell()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function stream_eof()
|
||||
{
|
||||
return feof($this->handle);
|
||||
}
|
||||
|
||||
public function stream_stat()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function stream_set_option($option, $arg1, $arg2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function url_stat($path, $flags)
|
||||
{
|
||||
$path = substr($path, 17);
|
||||
if (file_exists($path)) {
|
||||
return stat($path);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||
) {
|
||||
include("phpvfscomposer://" . __DIR__ . '/..'.'/nesbot/carbon/bin/carbon');
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
include __DIR__ . '/..'.'/nesbot/carbon/bin/carbon';
|
||||
21
vendor/carbonphp/carbon-doctrine-types/LICENSE
vendored
Normal file
21
vendor/carbonphp/carbon-doctrine-types/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Carbon
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
14
vendor/carbonphp/carbon-doctrine-types/README.md
vendored
Normal file
14
vendor/carbonphp/carbon-doctrine-types/README.md
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
# carbonphp/carbon-doctrine-types
|
||||
|
||||
Types to use Carbon in Doctrine
|
||||
|
||||
## Documentation
|
||||
|
||||
[Check how to use in the official Carbon documentation](https://carbon.nesbot.com/symfony/)
|
||||
|
||||
This package is an externalization of [src/Carbon/Doctrine](https://github.com/briannesbitt/Carbon/tree/2.71.0/src/Carbon/Doctrine)
|
||||
from `nestbot/carbon` package.
|
||||
|
||||
Externalization allows to better deal with different versions of dbal. With
|
||||
version 4.0 of dbal, it no longer sustainable to be compatible with all version
|
||||
using a single code.
|
||||
36
vendor/carbonphp/carbon-doctrine-types/composer.json
vendored
Normal file
36
vendor/carbonphp/carbon-doctrine-types/composer.json
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "carbonphp/carbon-doctrine-types",
|
||||
"description": "Types to use Carbon in Doctrine",
|
||||
"type": "library",
|
||||
"keywords": [
|
||||
"date",
|
||||
"time",
|
||||
"DateTime",
|
||||
"Carbon",
|
||||
"Doctrine"
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/dbal": "^4.0.0",
|
||||
"nesbot/carbon": "^2.71.0 || ^3.0.0",
|
||||
"phpunit/phpunit": "^10.3"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/dbal": "<4.0.0 || >=5.0.0"
|
||||
},
|
||||
"license": "MIT",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Carbon\\Doctrine\\": "src/Carbon/Doctrine/"
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "KyleKatarn",
|
||||
"email": "kylekatarnls@gmail.com"
|
||||
}
|
||||
],
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
16
vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonDoctrineType.php
vendored
Normal file
16
vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonDoctrineType.php
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Carbon\Doctrine;
|
||||
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
|
||||
interface CarbonDoctrineType
|
||||
{
|
||||
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform);
|
||||
|
||||
public function convertToPHPValue(mixed $value, AbstractPlatform $platform);
|
||||
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform);
|
||||
}
|
||||
9
vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonImmutableType.php
vendored
Normal file
9
vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonImmutableType.php
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Carbon\Doctrine;
|
||||
|
||||
class CarbonImmutableType extends DateTimeImmutableType implements CarbonDoctrineType
|
||||
{
|
||||
}
|
||||
9
vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonType.php
vendored
Normal file
9
vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonType.php
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Carbon\Doctrine;
|
||||
|
||||
class CarbonType extends DateTimeType implements CarbonDoctrineType
|
||||
{
|
||||
}
|
||||
131
vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonTypeConverter.php
vendored
Normal file
131
vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/CarbonTypeConverter.php
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Carbon\Doctrine;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Carbon\CarbonInterface;
|
||||
use DateTimeInterface;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Platforms\DB2Platform;
|
||||
use Doctrine\DBAL\Platforms\OraclePlatform;
|
||||
use Doctrine\DBAL\Platforms\SQLitePlatform;
|
||||
use Doctrine\DBAL\Platforms\SQLServerPlatform;
|
||||
use Doctrine\DBAL\Types\Exception\InvalidType;
|
||||
use Doctrine\DBAL\Types\Exception\ValueNotConvertible;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* @template T of CarbonInterface
|
||||
*/
|
||||
trait CarbonTypeConverter
|
||||
{
|
||||
/**
|
||||
* This property differentiates types installed by carbonphp/carbon-doctrine-types
|
||||
* from the ones embedded previously in nesbot/carbon source directly.
|
||||
*
|
||||
* @readonly
|
||||
*/
|
||||
public bool $external = true;
|
||||
|
||||
/**
|
||||
* @return class-string<T>
|
||||
*/
|
||||
protected function getCarbonClassName(): string
|
||||
{
|
||||
return Carbon::class;
|
||||
}
|
||||
|
||||
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
|
||||
{
|
||||
$precision = min(
|
||||
$fieldDeclaration['precision'] ?? DateTimeDefaultPrecision::get(),
|
||||
$this->getMaximumPrecision($platform),
|
||||
);
|
||||
|
||||
$type = parent::getSQLDeclaration($fieldDeclaration, $platform);
|
||||
|
||||
if (!$precision) {
|
||||
return $type;
|
||||
}
|
||||
|
||||
if (str_contains($type, '(')) {
|
||||
return preg_replace('/\(\d+\)/', "($precision)", $type);
|
||||
}
|
||||
|
||||
[$before, $after] = explode(' ', "$type ");
|
||||
|
||||
return trim("$before($precision) $after");
|
||||
}
|
||||
|
||||
/**
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
|
||||
{
|
||||
if ($value === null) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ($value instanceof DateTimeInterface) {
|
||||
return $value->format('Y-m-d H:i:s.u');
|
||||
}
|
||||
|
||||
throw InvalidType::new(
|
||||
$value,
|
||||
static::class,
|
||||
['null', 'DateTime', 'Carbon']
|
||||
);
|
||||
}
|
||||
|
||||
private function doConvertToPHPValue(mixed $value)
|
||||
{
|
||||
$class = $this->getCarbonClassName();
|
||||
|
||||
if ($value === null || is_a($value, $class)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ($value instanceof DateTimeInterface) {
|
||||
return $class::instance($value);
|
||||
}
|
||||
|
||||
$date = null;
|
||||
$error = null;
|
||||
|
||||
try {
|
||||
$date = $class::parse($value);
|
||||
} catch (Exception $exception) {
|
||||
$error = $exception;
|
||||
}
|
||||
|
||||
if (!$date) {
|
||||
throw ValueNotConvertible::new(
|
||||
$value,
|
||||
static::class,
|
||||
'Y-m-d H:i:s.u or any format supported by '.$class.'::parse()',
|
||||
$error
|
||||
);
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
private function getMaximumPrecision(AbstractPlatform $platform): int
|
||||
{
|
||||
if ($platform instanceof DB2Platform) {
|
||||
return 12;
|
||||
}
|
||||
|
||||
if ($platform instanceof OraclePlatform) {
|
||||
return 9;
|
||||
}
|
||||
|
||||
if ($platform instanceof SQLServerPlatform || $platform instanceof SQLitePlatform) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
30
vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeDefaultPrecision.php
vendored
Normal file
30
vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeDefaultPrecision.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Carbon\Doctrine;
|
||||
|
||||
class DateTimeDefaultPrecision
|
||||
{
|
||||
private static $precision = 6;
|
||||
|
||||
/**
|
||||
* Change the default Doctrine datetime and datetime_immutable precision.
|
||||
*
|
||||
* @param int $precision
|
||||
*/
|
||||
public static function set(int $precision): void
|
||||
{
|
||||
self::$precision = $precision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default Doctrine datetime and datetime_immutable precision.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function get(): int
|
||||
{
|
||||
return self::$precision;
|
||||
}
|
||||
}
|
||||
32
vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeImmutableType.php
vendored
Normal file
32
vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeImmutableType.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Carbon\Doctrine;
|
||||
|
||||
use Carbon\CarbonImmutable;
|
||||
use DateTimeImmutable;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Types\VarDateTimeImmutableType;
|
||||
|
||||
class DateTimeImmutableType extends VarDateTimeImmutableType implements CarbonDoctrineType
|
||||
{
|
||||
/** @use CarbonTypeConverter<CarbonImmutable> */
|
||||
use CarbonTypeConverter;
|
||||
|
||||
/**
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?CarbonImmutable
|
||||
{
|
||||
return $this->doConvertToPHPValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<CarbonImmutable>
|
||||
*/
|
||||
protected function getCarbonClassName(): string
|
||||
{
|
||||
return CarbonImmutable::class;
|
||||
}
|
||||
}
|
||||
24
vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeType.php
vendored
Normal file
24
vendor/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine/DateTimeType.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Carbon\Doctrine;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use DateTime;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Types\VarDateTimeType;
|
||||
|
||||
class DateTimeType extends VarDateTimeType implements CarbonDoctrineType
|
||||
{
|
||||
/** @use CarbonTypeConverter<Carbon> */
|
||||
use CarbonTypeConverter;
|
||||
|
||||
/**
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?Carbon
|
||||
{
|
||||
return $this->doConvertToPHPValue($value);
|
||||
}
|
||||
}
|
||||
585
vendor/composer/ClassLoader.php
vendored
Normal file
585
vendor/composer/ClassLoader.php
vendored
Normal file
@ -0,0 +1,585 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see https://www.php-fig.org/psr/psr-0/
|
||||
* @see https://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
/** @var \Closure(string):void */
|
||||
private static $includeFile;
|
||||
|
||||
/** @var ?string */
|
||||
private $vendorDir;
|
||||
|
||||
// PSR-4
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array<string, int>>
|
||||
*/
|
||||
private $prefixLengthsPsr4 = array();
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array<int, string>>
|
||||
*/
|
||||
private $prefixDirsPsr4 = array();
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, string>
|
||||
*/
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array<string, string[]>>
|
||||
*/
|
||||
private $prefixesPsr0 = array();
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, string>
|
||||
*/
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
/** @var bool */
|
||||
private $useIncludePath = false;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
* @psalm-var array<string, string>
|
||||
*/
|
||||
private $classMap = array();
|
||||
|
||||
/** @var bool */
|
||||
private $classMapAuthoritative = false;
|
||||
|
||||
/**
|
||||
* @var bool[]
|
||||
* @psalm-var array<string, bool>
|
||||
*/
|
||||
private $missingClasses = array();
|
||||
|
||||
/** @var ?string */
|
||||
private $apcuPrefix;
|
||||
|
||||
/**
|
||||
* @var self[]
|
||||
*/
|
||||
private static $registeredLoaders = array();
|
||||
|
||||
/**
|
||||
* @param ?string $vendorDir
|
||||
*/
|
||||
public function __construct($vendorDir = null)
|
||||
{
|
||||
$this->vendorDir = $vendorDir;
|
||||
self::initializeIncludeClosure();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return array<string, array<int, string>>
|
||||
*/
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return array<string, string>
|
||||
*/
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return array<string, string>
|
||||
*/
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[] Array of classname => path
|
||||
* @psalm-return array<string, string>
|
||||
*/
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $classMap Class to filename map
|
||||
* @psalm-param array<string, string> $classMap
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param string[]|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param string[]|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param string[]|string $paths The PSR-0 base directories
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param string[]|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
|
||||
if (null === $this->vendorDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($prepend) {
|
||||
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
|
||||
} else {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
self::$registeredLoaders[$this->vendorDir] = $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
|
||||
if (null !== $this->vendorDir) {
|
||||
unset(self::$registeredLoaders[$this->vendorDir]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return true|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
$includeFile = self::$includeFile;
|
||||
$includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently registered loaders indexed by their corresponding vendor directories.
|
||||
*
|
||||
* @return self[]
|
||||
*/
|
||||
public static function getRegisteredLoaders()
|
||||
{
|
||||
return self::$registeredLoaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $class
|
||||
* @param string $ext
|
||||
* @return string|false
|
||||
*/
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private static function initializeIncludeClosure()
|
||||
{
|
||||
if (self::$includeFile !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*
|
||||
* @param string $file
|
||||
* @return void
|
||||
*/
|
||||
self::$includeFile = \Closure::bind(static function($file) {
|
||||
include $file;
|
||||
}, null, null);
|
||||
}
|
||||
}
|
||||
359
vendor/composer/InstalledVersions.php
vendored
Normal file
359
vendor/composer/InstalledVersions.php
vendored
Normal file
@ -0,0 +1,359 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
use Composer\Autoload\ClassLoader;
|
||||
use Composer\Semver\VersionParser;
|
||||
|
||||
/**
|
||||
* This class is copied in every Composer installed project and available to all
|
||||
*
|
||||
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
|
||||
*
|
||||
* To require its presence, you can require `composer-runtime-api ^2.0`
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class InstalledVersions
|
||||
{
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||
*/
|
||||
private static $installed;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
private static $canGetVendors;
|
||||
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static $installedByVendor = array();
|
||||
|
||||
/**
|
||||
* Returns a list of all package names which are present, either by being installed, replaced or provided
|
||||
*
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackages()
|
||||
{
|
||||
$packages = array();
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
$packages[] = array_keys($installed['versions']);
|
||||
}
|
||||
|
||||
if (1 === \count($packages)) {
|
||||
return $packages[0];
|
||||
}
|
||||
|
||||
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all package names with a specific type e.g. 'library'
|
||||
*
|
||||
* @param string $type
|
||||
* @return string[]
|
||||
* @psalm-return list<string>
|
||||
*/
|
||||
public static function getInstalledPackagesByType($type)
|
||||
{
|
||||
$packagesByType = array();
|
||||
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
foreach ($installed['versions'] as $name => $package) {
|
||||
if (isset($package['type']) && $package['type'] === $type) {
|
||||
$packagesByType[] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $packagesByType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package is installed
|
||||
*
|
||||
* This also returns true if the package name is provided or replaced by another package
|
||||
*
|
||||
* @param string $packageName
|
||||
* @param bool $includeDevRequirements
|
||||
* @return bool
|
||||
*/
|
||||
public static function isInstalled($packageName, $includeDevRequirements = true)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (isset($installed['versions'][$packageName])) {
|
||||
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given package satisfies a version constraint
|
||||
*
|
||||
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
|
||||
*
|
||||
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
|
||||
*
|
||||
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
|
||||
* @param string $packageName
|
||||
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
|
||||
* @return bool
|
||||
*/
|
||||
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||
{
|
||||
$constraint = $parser->parseConstraints((string) $constraint);
|
||||
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||
|
||||
return $provided->matches($constraint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a version constraint representing all the range(s) which are installed for a given package
|
||||
*
|
||||
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
|
||||
* whether a given version of a package is installed, and not just whether it exists
|
||||
*
|
||||
* @param string $packageName
|
||||
* @return string Version constraint usable with composer/semver
|
||||
*/
|
||||
public static function getVersionRanges($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$ranges = array();
|
||||
if (isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
|
||||
}
|
||||
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
|
||||
}
|
||||
if (array_key_exists('provided', $installed['versions'][$packageName])) {
|
||||
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
|
||||
}
|
||||
|
||||
return implode(' || ', $ranges);
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
|
||||
*/
|
||||
public static function getPrettyVersion($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['pretty_version'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
|
||||
*/
|
||||
public static function getReference($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($installed['versions'][$packageName]['reference'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $installed['versions'][$packageName]['reference'];
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $packageName
|
||||
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
|
||||
*/
|
||||
public static function getInstallPath($packageName)
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (!isset($installed['versions'][$packageName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
|
||||
}
|
||||
|
||||
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
|
||||
*/
|
||||
public static function getRootPackage()
|
||||
{
|
||||
$installed = self::getInstalled();
|
||||
|
||||
return $installed[0]['root'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw installed.php data for custom implementations
|
||||
*
|
||||
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
|
||||
* @return array[]
|
||||
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
|
||||
*/
|
||||
public static function getRawData()
|
||||
{
|
||||
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
self::$installed = include __DIR__ . '/installed.php';
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
|
||||
return self::$installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw data of all installed.php which are currently loaded for custom implementations
|
||||
*
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
public static function getAllRawData()
|
||||
{
|
||||
return self::getInstalled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets you reload the static array from another file
|
||||
*
|
||||
* This is only useful for complex integrations in which a project needs to use
|
||||
* this class but then also needs to execute another project's autoloader in process,
|
||||
* and wants to ensure both projects have access to their version of installed.php.
|
||||
*
|
||||
* A typical case would be PHPUnit, where it would need to make sure it reads all
|
||||
* the data it needs from this class, then call reload() with
|
||||
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
|
||||
* the project in which it runs can then also use this class safely, without
|
||||
* interference between PHPUnit's dependencies and the project's dependencies.
|
||||
*
|
||||
* @param array[] $data A vendor/composer/installed.php data set
|
||||
* @return void
|
||||
*
|
||||
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
|
||||
*/
|
||||
public static function reload($data)
|
||||
{
|
||||
self::$installed = $data;
|
||||
self::$installedByVendor = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
*/
|
||||
private static function getInstalled()
|
||||
{
|
||||
if (null === self::$canGetVendors) {
|
||||
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
|
||||
}
|
||||
|
||||
$installed = array();
|
||||
|
||||
if (self::$canGetVendors) {
|
||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir];
|
||||
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = require $vendorDir.'/composer/installed.php';
|
||||
$installed[] = self::$installedByVendor[$vendorDir] = $required;
|
||||
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
||||
self::$installed = $installed[count($installed) - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null === self::$installed) {
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = require __DIR__ . '/installed.php';
|
||||
self::$installed = $required;
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
|
||||
if (self::$installed !== array()) {
|
||||
$installed[] = self::$installed;
|
||||
}
|
||||
|
||||
return $installed;
|
||||
}
|
||||
}
|
||||
19
vendor/composer/LICENSE
vendored
Normal file
19
vendor/composer/LICENSE
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
26
vendor/composer/autoload_classmap.php
vendored
Normal file
26
vendor/composer/autoload_classmap.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
||||
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
|
||||
'DateError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateError.php',
|
||||
'DateException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateException.php',
|
||||
'DateInvalidOperationException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateInvalidOperationException.php',
|
||||
'DateInvalidTimeZoneException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateInvalidTimeZoneException.php',
|
||||
'DateMalformedIntervalStringException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateMalformedIntervalStringException.php',
|
||||
'DateMalformedPeriodStringException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateMalformedPeriodStringException.php',
|
||||
'DateMalformedStringException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateMalformedStringException.php',
|
||||
'DateObjectError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateObjectError.php',
|
||||
'DateRangeError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateRangeError.php',
|
||||
'Override' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/Override.php',
|
||||
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
|
||||
'SQLite3Exception' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/SQLite3Exception.php',
|
||||
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
||||
'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
|
||||
'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
|
||||
);
|
||||
20
vendor/composer/autoload_files.php
vendored
Normal file
20
vendor/composer/autoload_files.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'ad155f8f1cf0d418fe49e248db8c661b' => $vendorDir . '/react/promise/src/functions_include.php',
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'662a729f963d39afe703c9d9b7ab4a8c' => $vendorDir . '/symfony/polyfill-php83/bootstrap.php',
|
||||
'2203a247e6fda86070a5e4e07aed533a' => $vendorDir . '/symfony/clock/Resources/now.php',
|
||||
'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
|
||||
'3be16222a6efa6dd226a219eaaff823b' => $vendorDir . '/ratchet/pawl/src/functions_include.php',
|
||||
'c4e03ecd470d2a87804979c0a8152284' => $vendorDir . '/react/async/src/functions_include.php',
|
||||
'864b292aadc96fda0e2642b894a38d16' => $vendorDir . '/team-reflex/discord-php/src/Discord/functions.php',
|
||||
);
|
||||
10
vendor/composer/autoload_namespaces.php
vendored
Normal file
10
vendor/composer/autoload_namespaces.php
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'TrafficCophp' => array($vendorDir . '/trafficcophp/bytebuffer/src'),
|
||||
);
|
||||
41
vendor/composer/autoload_psr4.php
vendored
Normal file
41
vendor/composer/autoload_psr4.php
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(__DIR__);
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Tests\\Discord\\Http\\' => array($vendorDir . '/discord-php/http/tests/Discord'),
|
||||
'Symfony\\Polyfill\\Php83\\' => array($vendorDir . '/symfony/polyfill-php83'),
|
||||
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
|
||||
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
|
||||
'Symfony\\Contracts\\Translation\\' => array($vendorDir . '/symfony/translation-contracts'),
|
||||
'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
|
||||
'Symfony\\Component\\OptionsResolver\\' => array($vendorDir . '/symfony/options-resolver'),
|
||||
'Symfony\\Component\\Clock\\' => array($vendorDir . '/symfony/clock'),
|
||||
'React\\Stream\\' => array($vendorDir . '/react/stream/src'),
|
||||
'React\\Socket\\' => array($vendorDir . '/react/socket/src'),
|
||||
'React\\Promise\\' => array($vendorDir . '/react/promise/src'),
|
||||
'React\\Http\\' => array($vendorDir . '/react/http/src'),
|
||||
'React\\EventLoop\\' => array($vendorDir . '/react/event-loop/src'),
|
||||
'React\\Dns\\' => array($vendorDir . '/react/dns/src'),
|
||||
'React\\Datagram\\' => array($vendorDir . '/react/datagram/src'),
|
||||
'React\\ChildProcess\\' => array($vendorDir . '/react/child-process/src'),
|
||||
'React\\Cache\\' => array($vendorDir . '/react/cache/src'),
|
||||
'React\\Async\\' => array($vendorDir . '/react/async/src'),
|
||||
'Ratchet\\RFC6455\\' => array($vendorDir . '/ratchet/rfc6455/src'),
|
||||
'Ratchet\\Client\\' => array($vendorDir . '/ratchet/pawl/src'),
|
||||
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
|
||||
'Psr\\Log\\' => array($vendorDir . '/psr/log/src'),
|
||||
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src', $vendorDir . '/psr/http-factory/src'),
|
||||
'Psr\\Clock\\' => array($vendorDir . '/psr/clock/src'),
|
||||
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
|
||||
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
|
||||
'Fig\\Http\\Message\\' => array($vendorDir . '/fig/http-message-util/src'),
|
||||
'Evenement\\' => array($vendorDir . '/evenement/evenement/src'),
|
||||
'Discord\\Http\\' => array($vendorDir . '/discord-php/http/src/Discord'),
|
||||
'Discord\\' => array($vendorDir . '/discord/interactions/discord', $vendorDir . '/discord-php-helpers/voice/src/Discord', $vendorDir . '/discord-php-helpers/collection/src/Discord', $vendorDir . '/team-reflex/discord-php/src/Discord'),
|
||||
'Carbon\\Doctrine\\' => array($vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine'),
|
||||
'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'),
|
||||
);
|
||||
50
vendor/composer/autoload_real.php
vendored
Normal file
50
vendor/composer/autoload_real.php
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit3f15e11d83f664c614d99889af2eb047
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Composer\Autoload\ClassLoader
|
||||
*/
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
require __DIR__ . '/platform_check.php';
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit3f15e11d83f664c614d99889af2eb047', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit3f15e11d83f664c614d99889af2eb047', 'loadClassLoader'));
|
||||
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit3f15e11d83f664c614d99889af2eb047::getInitializer($loader));
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
$filesToLoad = \Composer\Autoload\ComposerStaticInit3f15e11d83f664c614d99889af2eb047::$files;
|
||||
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
|
||||
require $file;
|
||||
}
|
||||
}, null, null);
|
||||
foreach ($filesToLoad as $fileIdentifier => $file) {
|
||||
$requireFile($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
263
vendor/composer/autoload_static.php
vendored
Normal file
263
vendor/composer/autoload_static.php
vendored
Normal file
@ -0,0 +1,263 @@
|
||||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInit3f15e11d83f664c614d99889af2eb047
|
||||
{
|
||||
public static $files = array (
|
||||
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'662a729f963d39afe703c9d9b7ab4a8c' => __DIR__ . '/..' . '/symfony/polyfill-php83/bootstrap.php',
|
||||
'2203a247e6fda86070a5e4e07aed533a' => __DIR__ . '/..' . '/symfony/clock/Resources/now.php',
|
||||
'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php',
|
||||
'3be16222a6efa6dd226a219eaaff823b' => __DIR__ . '/..' . '/ratchet/pawl/src/functions_include.php',
|
||||
'c4e03ecd470d2a87804979c0a8152284' => __DIR__ . '/..' . '/react/async/src/functions_include.php',
|
||||
'864b292aadc96fda0e2642b894a38d16' => __DIR__ . '/..' . '/team-reflex/discord-php/src/Discord/functions.php',
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'T' =>
|
||||
array (
|
||||
'Tests\\Discord\\Http\\' => 19,
|
||||
),
|
||||
'S' =>
|
||||
array (
|
||||
'Symfony\\Polyfill\\Php83\\' => 23,
|
||||
'Symfony\\Polyfill\\Php80\\' => 23,
|
||||
'Symfony\\Polyfill\\Mbstring\\' => 26,
|
||||
'Symfony\\Contracts\\Translation\\' => 30,
|
||||
'Symfony\\Component\\Translation\\' => 30,
|
||||
'Symfony\\Component\\OptionsResolver\\' => 34,
|
||||
'Symfony\\Component\\Clock\\' => 24,
|
||||
),
|
||||
'R' =>
|
||||
array (
|
||||
'React\\Stream\\' => 13,
|
||||
'React\\Socket\\' => 13,
|
||||
'React\\Promise\\' => 14,
|
||||
'React\\Http\\' => 11,
|
||||
'React\\EventLoop\\' => 16,
|
||||
'React\\Dns\\' => 10,
|
||||
'React\\Datagram\\' => 15,
|
||||
'React\\ChildProcess\\' => 19,
|
||||
'React\\Cache\\' => 12,
|
||||
'React\\Async\\' => 12,
|
||||
'Ratchet\\RFC6455\\' => 16,
|
||||
'Ratchet\\Client\\' => 15,
|
||||
),
|
||||
'P' =>
|
||||
array (
|
||||
'Psr\\SimpleCache\\' => 16,
|
||||
'Psr\\Log\\' => 8,
|
||||
'Psr\\Http\\Message\\' => 17,
|
||||
'Psr\\Clock\\' => 10,
|
||||
),
|
||||
'M' =>
|
||||
array (
|
||||
'Monolog\\' => 8,
|
||||
),
|
||||
'G' =>
|
||||
array (
|
||||
'GuzzleHttp\\Psr7\\' => 16,
|
||||
),
|
||||
'F' =>
|
||||
array (
|
||||
'Fig\\Http\\Message\\' => 17,
|
||||
),
|
||||
'E' =>
|
||||
array (
|
||||
'Evenement\\' => 10,
|
||||
),
|
||||
'D' =>
|
||||
array (
|
||||
'Discord\\Http\\' => 13,
|
||||
'Discord\\' => 8,
|
||||
),
|
||||
'C' =>
|
||||
array (
|
||||
'Carbon\\Doctrine\\' => 16,
|
||||
'Carbon\\' => 7,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'Tests\\Discord\\Http\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/discord-php/http/tests/Discord',
|
||||
),
|
||||
'Symfony\\Polyfill\\Php83\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php83',
|
||||
),
|
||||
'Symfony\\Polyfill\\Php80\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
|
||||
),
|
||||
'Symfony\\Polyfill\\Mbstring\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
|
||||
),
|
||||
'Symfony\\Contracts\\Translation\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/translation-contracts',
|
||||
),
|
||||
'Symfony\\Component\\Translation\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/translation',
|
||||
),
|
||||
'Symfony\\Component\\OptionsResolver\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/options-resolver',
|
||||
),
|
||||
'Symfony\\Component\\Clock\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/clock',
|
||||
),
|
||||
'React\\Stream\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/stream/src',
|
||||
),
|
||||
'React\\Socket\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/socket/src',
|
||||
),
|
||||
'React\\Promise\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/promise/src',
|
||||
),
|
||||
'React\\Http\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/http/src',
|
||||
),
|
||||
'React\\EventLoop\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/event-loop/src',
|
||||
),
|
||||
'React\\Dns\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/dns/src',
|
||||
),
|
||||
'React\\Datagram\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/datagram/src',
|
||||
),
|
||||
'React\\ChildProcess\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/child-process/src',
|
||||
),
|
||||
'React\\Cache\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/cache/src',
|
||||
),
|
||||
'React\\Async\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/react/async/src',
|
||||
),
|
||||
'Ratchet\\RFC6455\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/ratchet/rfc6455/src',
|
||||
),
|
||||
'Ratchet\\Client\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/ratchet/pawl/src',
|
||||
),
|
||||
'Psr\\SimpleCache\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/simple-cache/src',
|
||||
),
|
||||
'Psr\\Log\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/log/src',
|
||||
),
|
||||
'Psr\\Http\\Message\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/http-message/src',
|
||||
1 => __DIR__ . '/..' . '/psr/http-factory/src',
|
||||
),
|
||||
'Psr\\Clock\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/psr/clock/src',
|
||||
),
|
||||
'Monolog\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/monolog/monolog/src/Monolog',
|
||||
),
|
||||
'GuzzleHttp\\Psr7\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',
|
||||
),
|
||||
'Fig\\Http\\Message\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/fig/http-message-util/src',
|
||||
),
|
||||
'Evenement\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/evenement/evenement/src',
|
||||
),
|
||||
'Discord\\Http\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/discord-php/http/src/Discord',
|
||||
),
|
||||
'Discord\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/discord/interactions/discord',
|
||||
1 => __DIR__ . '/..' . '/discord-php-helpers/voice/src/Discord',
|
||||
2 => __DIR__ . '/..' . '/discord-php-helpers/collection/src/Discord',
|
||||
3 => __DIR__ . '/..' . '/team-reflex/discord-php/src/Discord',
|
||||
),
|
||||
'Carbon\\Doctrine\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine',
|
||||
),
|
||||
'Carbon\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon',
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixesPsr0 = array (
|
||||
'T' =>
|
||||
array (
|
||||
'TrafficCophp' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/trafficcophp/bytebuffer/src',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
|
||||
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
|
||||
'DateError' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateError.php',
|
||||
'DateException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateException.php',
|
||||
'DateInvalidOperationException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateInvalidOperationException.php',
|
||||
'DateInvalidTimeZoneException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateInvalidTimeZoneException.php',
|
||||
'DateMalformedIntervalStringException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateMalformedIntervalStringException.php',
|
||||
'DateMalformedPeriodStringException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateMalformedPeriodStringException.php',
|
||||
'DateMalformedStringException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateMalformedStringException.php',
|
||||
'DateObjectError' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateObjectError.php',
|
||||
'DateRangeError' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateRangeError.php',
|
||||
'Override' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/Override.php',
|
||||
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
|
||||
'SQLite3Exception' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/SQLite3Exception.php',
|
||||
'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
|
||||
'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
|
||||
'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInit3f15e11d83f664c614d99889af2eb047::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInit3f15e11d83f664c614d99889af2eb047::$prefixDirsPsr4;
|
||||
$loader->prefixesPsr0 = ComposerStaticInit3f15e11d83f664c614d99889af2eb047::$prefixesPsr0;
|
||||
$loader->classMap = ComposerStaticInit3f15e11d83f664c614d99889af2eb047::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
||||
2820
vendor/composer/installed.json
vendored
Normal file
2820
vendor/composer/installed.json
vendored
Normal file
File diff suppressed because it is too large
Load Diff
395
vendor/composer/installed.php
vendored
Normal file
395
vendor/composer/installed.php
vendored
Normal file
@ -0,0 +1,395 @@
|
||||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => '__root__',
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => '6d183d7a92b5a77ec97b393003963fca81a2f802',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev' => true,
|
||||
),
|
||||
'versions' => array(
|
||||
'__root__' => array(
|
||||
'pretty_version' => 'dev-master',
|
||||
'version' => 'dev-master',
|
||||
'reference' => '6d183d7a92b5a77ec97b393003963fca81a2f802',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'carbonphp/carbon-doctrine-types' => array(
|
||||
'pretty_version' => '3.2.0',
|
||||
'version' => '3.2.0.0',
|
||||
'reference' => '18ba5ddfec8976260ead6e866180bd5d2f71aa1d',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../carbonphp/carbon-doctrine-types',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'discord-php-helpers/collection' => array(
|
||||
'pretty_version' => 'v8.0.0',
|
||||
'version' => '8.0.0.0',
|
||||
'reference' => '27eae375a21d5086a665b803881cee09863b2269',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../discord-php-helpers/collection',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'discord-php-helpers/voice' => array(
|
||||
'pretty_version' => 'v8.0.5',
|
||||
'version' => '8.0.5.0',
|
||||
'reference' => '9c6ce3547f1463a2726d6726fd971ea26f99a1dc',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../discord-php-helpers/voice',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'discord-php/http' => array(
|
||||
'pretty_version' => 'v10.9.0',
|
||||
'version' => '10.9.0.0',
|
||||
'reference' => 'f2d281a435ec4f42b084dd1b843cfac91ac2fbbd',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../discord-php/http',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'discord/interactions' => array(
|
||||
'pretty_version' => '2.2.0',
|
||||
'version' => '2.2.0.0',
|
||||
'reference' => 'a6fc0c877b75cf5ff5811f2ea69c5cc4ad6ac457',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../discord/interactions',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'evenement/evenement' => array(
|
||||
'pretty_version' => 'v3.0.2',
|
||||
'version' => '3.0.2.0',
|
||||
'reference' => '0a16b0d71ab13284339abb99d9d2bd813640efbc',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../evenement/evenement',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'fig/http-message-util' => array(
|
||||
'pretty_version' => '1.1.5',
|
||||
'version' => '1.1.5.0',
|
||||
'reference' => '9d94dc0154230ac39e5bf89398b324a86f63f765',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../fig/http-message-util',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'guzzlehttp/psr7' => array(
|
||||
'pretty_version' => '2.8.0',
|
||||
'version' => '2.8.0.0',
|
||||
'reference' => '21dc724a0583619cd1652f673303492272778051',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../guzzlehttp/psr7',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'monolog/monolog' => array(
|
||||
'pretty_version' => '3.10.0',
|
||||
'version' => '3.10.0.0',
|
||||
'reference' => 'b321dd6749f0bf7189444158a3ce785cc16d69b0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../monolog/monolog',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'nesbot/carbon' => array(
|
||||
'pretty_version' => '3.11.1',
|
||||
'version' => '3.11.1.0',
|
||||
'reference' => 'f438fcc98f92babee98381d399c65336f3a3827f',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../nesbot/carbon',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/clock' => array(
|
||||
'pretty_version' => '1.0.0',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => 'e41a24703d4560fd0acb709162f73b8adfc3aa0d',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/clock',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/clock-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '1.0',
|
||||
),
|
||||
),
|
||||
'psr/http-factory' => array(
|
||||
'pretty_version' => '1.1.0',
|
||||
'version' => '1.1.0.0',
|
||||
'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/http-factory',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/http-factory-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '1.0',
|
||||
),
|
||||
),
|
||||
'psr/http-message' => array(
|
||||
'pretty_version' => '1.1',
|
||||
'version' => '1.1.0.0',
|
||||
'reference' => 'cb6ce4845ce34a8ad9e68117c10ee90a29919eba',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/http-message',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/http-message-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '1.0',
|
||||
),
|
||||
),
|
||||
'psr/log' => array(
|
||||
'pretty_version' => '3.0.2',
|
||||
'version' => '3.0.2.0',
|
||||
'reference' => 'f16e1d5863e37f8d8c2a01719f5b34baa2b714d3',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/log',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'psr/log-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '3.0.0',
|
||||
),
|
||||
),
|
||||
'psr/simple-cache' => array(
|
||||
'pretty_version' => '3.0.0',
|
||||
'version' => '3.0.0.0',
|
||||
'reference' => '764e0b3939f5ca87cb904f570ef9be2d78a07865',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../psr/simple-cache',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'ralouphie/getallheaders' => array(
|
||||
'pretty_version' => '3.0.3',
|
||||
'version' => '3.0.3.0',
|
||||
'reference' => '120b605dfeb996808c31b6477290a714d356e822',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../ralouphie/getallheaders',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'ratchet/pawl' => array(
|
||||
'pretty_version' => 'v0.4.3',
|
||||
'version' => '0.4.3.0',
|
||||
'reference' => '2c582373c78271de32cb04c755c4c0db7e09c9c0',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../ratchet/pawl',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'ratchet/rfc6455' => array(
|
||||
'pretty_version' => 'v0.4.0',
|
||||
'version' => '0.4.0.0',
|
||||
'reference' => '859d95f85dda0912c6d5b936d036d044e3af47ef',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../ratchet/rfc6455',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/async' => array(
|
||||
'pretty_version' => 'v4.3.0',
|
||||
'version' => '4.3.0.0',
|
||||
'reference' => '635d50e30844a484495713e8cb8d9e079c0008a5',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/async',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/cache' => array(
|
||||
'pretty_version' => 'v1.2.0',
|
||||
'version' => '1.2.0.0',
|
||||
'reference' => 'd47c472b64aa5608225f47965a484b75c7817d5b',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/cache',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/child-process' => array(
|
||||
'pretty_version' => 'v0.6.7',
|
||||
'version' => '0.6.7.0',
|
||||
'reference' => '970f0e71945556422ee4570ccbabaedc3cf04ad3',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/child-process',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/datagram' => array(
|
||||
'pretty_version' => 'v1.10.0',
|
||||
'version' => '1.10.0.0',
|
||||
'reference' => '9236e1f5a67a6029be17d551e9858c487836c301',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/datagram',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/dns' => array(
|
||||
'pretty_version' => 'v1.14.0',
|
||||
'version' => '1.14.0.0',
|
||||
'reference' => '7562c05391f42701c1fccf189c8225fece1cd7c3',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/dns',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/event-loop' => array(
|
||||
'pretty_version' => 'v1.6.0',
|
||||
'version' => '1.6.0.0',
|
||||
'reference' => 'ba276bda6083df7e0050fd9b33f66ad7a4ac747a',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/event-loop',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/http' => array(
|
||||
'pretty_version' => 'v1.11.0',
|
||||
'version' => '1.11.0.0',
|
||||
'reference' => '8db02de41dcca82037367f67a2d4be365b1c4db9',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/http',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/promise' => array(
|
||||
'pretty_version' => 'v3.3.0',
|
||||
'version' => '3.3.0.0',
|
||||
'reference' => '23444f53a813a3296c1368bb104793ce8d88f04a',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/promise',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/socket' => array(
|
||||
'pretty_version' => 'v1.17.0',
|
||||
'version' => '1.17.0.0',
|
||||
'reference' => 'ef5b17b81f6f60504c539313f94f2d826c5faa08',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/socket',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'react/stream' => array(
|
||||
'pretty_version' => 'v1.4.0',
|
||||
'version' => '1.4.0.0',
|
||||
'reference' => '1e5b0acb8fe55143b5b426817155190eb6f5b18d',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../react/stream',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/clock' => array(
|
||||
'pretty_version' => 'v7.4.0',
|
||||
'version' => '7.4.0.0',
|
||||
'reference' => '9169f24776edde469914c1e7a1442a50f7a4e110',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/clock',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/deprecation-contracts' => array(
|
||||
'pretty_version' => 'v3.6.0',
|
||||
'version' => '3.6.0.0',
|
||||
'reference' => '63afe740e99a13ba87ec199bb07bbdee937a5b62',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/options-resolver' => array(
|
||||
'pretty_version' => 'v7.4.0',
|
||||
'version' => '7.4.0.0',
|
||||
'reference' => 'b38026df55197f9e39a44f3215788edf83187b80',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/options-resolver',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-mbstring' => array(
|
||||
'pretty_version' => 'v1.33.0',
|
||||
'version' => '1.33.0.0',
|
||||
'reference' => '6d857f4d76bd4b343eac26d6b539585d2bc56493',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-php80' => array(
|
||||
'pretty_version' => 'v1.33.0',
|
||||
'version' => '1.33.0.0',
|
||||
'reference' => '0cc9dd0f17f61d8131e7df6b84bd344899fe2608',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/polyfill-php83' => array(
|
||||
'pretty_version' => 'v1.33.0',
|
||||
'version' => '1.33.0.0',
|
||||
'reference' => '17f6f9a6b1735c0f163024d959f700cfbc5155e5',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/polyfill-php83',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/translation' => array(
|
||||
'pretty_version' => 'v7.4.4',
|
||||
'version' => '7.4.4.0',
|
||||
'reference' => 'bfde13711f53f549e73b06d27b35a55207528877',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/translation',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/translation-contracts' => array(
|
||||
'pretty_version' => 'v3.6.1',
|
||||
'version' => '3.6.1.0',
|
||||
'reference' => '65a8bc82080447fae78373aa10f8d13b38338977',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/translation-contracts',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/translation-implementation' => array(
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '2.3|3.0',
|
||||
),
|
||||
),
|
||||
'team-reflex/discord-php' => array(
|
||||
'pretty_version' => 'v10.45.18',
|
||||
'version' => '10.45.18.0',
|
||||
'reference' => '05a2b31b1301827e26a478c9e970da103a20bd67',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../team-reflex/discord-php',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'trafficcophp/bytebuffer' => array(
|
||||
'pretty_version' => 'v0.3',
|
||||
'version' => '0.3.0.0',
|
||||
'reference' => 'e94e5c87c41bc79c0f738b0fa89bad11d27ae0b4',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../trafficcophp/bytebuffer',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
),
|
||||
);
|
||||
26
vendor/composer/platform_check.php
vendored
Normal file
26
vendor/composer/platform_check.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
// platform_check.php @generated by Composer
|
||||
|
||||
$issues = array();
|
||||
|
||||
if (!(PHP_VERSION_ID >= 80200)) {
|
||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.2.0". You are running ' . PHP_VERSION . '.';
|
||||
}
|
||||
|
||||
if ($issues) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
|
||||
} elseif (!headers_sent()) {
|
||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
31
vendor/discord-php-helpers/collection/.gitignore
vendored
Normal file
31
vendor/discord-php-helpers/collection/.gitignore
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
# general
|
||||
.env
|
||||
.DS_Store
|
||||
test*.php
|
||||
.idea
|
||||
.phplint-cache
|
||||
dump.html
|
||||
.vscode
|
||||
|
||||
# phive
|
||||
/tools
|
||||
.phive
|
||||
|
||||
# composer
|
||||
/vendor
|
||||
composer.lock
|
||||
|
||||
# docs
|
||||
/build
|
||||
.phpdoc
|
||||
|
||||
# php-cs-fixer
|
||||
.php_cs.cache
|
||||
.php_cs
|
||||
.php-cs-fixer.php
|
||||
.php-cs-fixer.cache
|
||||
|
||||
# phpunit
|
||||
phpunit.log
|
||||
/.phpunit*
|
||||
/coverage
|
||||
21
vendor/discord-php-helpers/collection/LICENSE
vendored
Normal file
21
vendor/discord-php-helpers/collection/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 DiscordPHP
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
15
vendor/discord-php-helpers/collection/README.md
vendored
Normal file
15
vendor/discord-php-helpers/collection/README.md
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# DiscordPHP Collection
|
||||
|
||||
A collection library for DiscordPHP. Inspired by Laravel Collections.
|
||||
|
||||
## Installation
|
||||
|
||||
You can install the library via Composer:
|
||||
|
||||
```bash
|
||||
composer require discord-php-helpers/collection
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
||||
27
vendor/discord-php-helpers/collection/composer.json
vendored
Normal file
27
vendor/discord-php-helpers/collection/composer.json
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "discord-php-helpers/collection",
|
||||
"description": "A collection library for DiscordPHP. Inspired by Laravel Collections.",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "David Cole",
|
||||
"email": "david.cole1340@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Valithor Obsidion",
|
||||
"email": "valzargaming@gmail.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"team-reflex/discord-php": "dev-master"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Discord\\": "src/Discord/"
|
||||
}
|
||||
}
|
||||
}
|
||||
90
vendor/discord-php-helpers/collection/src/Discord/Helpers/Collection.php
vendored
Normal file
90
vendor/discord-php-helpers/collection/src/Discord/Helpers/Collection.php
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Helpers;
|
||||
|
||||
use JsonSerializable;
|
||||
|
||||
/**
|
||||
* Collection of items. Inspired by Laravel Collections.
|
||||
*
|
||||
* @since 5.0.0 No longer extends Laravel's BaseCollection
|
||||
* @since 4.0.0
|
||||
*/
|
||||
class Collection implements ExCollectionInterface, JsonSerializable
|
||||
{
|
||||
use CollectionTrait;
|
||||
/**
|
||||
* The collection discriminator.
|
||||
*
|
||||
* @var ?string
|
||||
*/
|
||||
protected $discrim;
|
||||
|
||||
/**
|
||||
* The items contained in the collection.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $items;
|
||||
|
||||
/**
|
||||
* Class type allowed into the collection.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $class;
|
||||
|
||||
/**
|
||||
* Create a new Collection.
|
||||
*
|
||||
* @param array $items
|
||||
* @param ?string $discrim
|
||||
* @param ?string $class
|
||||
*/
|
||||
public function __construct(array $items = [], ?string $discrim = 'id', ?string $class = null)
|
||||
{
|
||||
$this->items = $items;
|
||||
$this->discrim = $discrim;
|
||||
$this->class = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a collection from an array.
|
||||
*
|
||||
* @param array $items
|
||||
* @param ?string $discrim
|
||||
* @param ?string $class
|
||||
*
|
||||
* @return ExCollectionInterface
|
||||
*/
|
||||
public static function from(array $items = [], ?string $discrim = 'id', ?string $class = null)
|
||||
{
|
||||
return new Collection($items, $discrim, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a collection for a class.
|
||||
*
|
||||
* @param string $class
|
||||
* @param ?string $discrim
|
||||
*
|
||||
* @return ExCollectionInterface
|
||||
*/
|
||||
public static function for(string $class, ?string $discrim = 'id')
|
||||
{
|
||||
$items = [];
|
||||
|
||||
return new Collection($items, $discrim, $class);
|
||||
}
|
||||
}
|
||||
54
vendor/discord-php-helpers/collection/src/Discord/Helpers/CollectionInterface.php
vendored
Normal file
54
vendor/discord-php-helpers/collection/src/Discord/Helpers/CollectionInterface.php
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Helpers;
|
||||
|
||||
use ArrayAccess;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use JsonSerializable;
|
||||
use Traversable;
|
||||
|
||||
interface CollectionInterface extends ArrayAccess, JsonSerializable, IteratorAggregate, Countable
|
||||
{
|
||||
public function get(string $discrim, $key);
|
||||
public function set($offset, $value);
|
||||
public function pull($key, $default = null);
|
||||
public function fill($items): self;
|
||||
public function push(...$items): self;
|
||||
public function pushItem($item): self;
|
||||
public function count(): int;
|
||||
public function first();
|
||||
public function last();
|
||||
public function isset($offset): bool;
|
||||
public function has(...$keys): bool;
|
||||
public function filter(callable $callback);
|
||||
public function find(callable $callback);
|
||||
public function clear(): void;
|
||||
public function map(callable $callback);
|
||||
public function merge($collection): self;
|
||||
/** @deprecated 10.42.0 Use `jsonSerialize` */
|
||||
public function toArray(bool $assoc = true): array;
|
||||
public function offsetExists($offset): bool;
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset);
|
||||
public function offsetSet($offset, $value): void;
|
||||
public function offsetUnset($offset): void;
|
||||
public function serialize(int $flags = 0, ?int $depth = 512): string;
|
||||
public function __serialize(): array;
|
||||
public function unserialize(string $serialized): void;
|
||||
public function __unserialize($data): void;
|
||||
public function jsonSerialize(bool $assoc = true): array;
|
||||
public function getIterator(): Traversable;
|
||||
public function __debugInfo(): array;
|
||||
}
|
||||
721
vendor/discord-php-helpers/collection/src/Discord/Helpers/CollectionTrait.php
vendored
Normal file
721
vendor/discord-php-helpers/collection/src/Discord/Helpers/CollectionTrait.php
vendored
Normal file
@ -0,0 +1,721 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Helpers;
|
||||
|
||||
/**
|
||||
* Provides common functionality for collections.
|
||||
*
|
||||
* @property string|null $discrim The collection discriminator.
|
||||
* @property array $items The items contained in the collection.
|
||||
* @property string $class Class type allowed into the collection.
|
||||
*/
|
||||
trait CollectionTrait
|
||||
{
|
||||
|
||||
/**
|
||||
* Gets an item from the collection.
|
||||
*
|
||||
* @param string $discrim
|
||||
* @param mixed $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $discrim, $key)
|
||||
{
|
||||
if ($discrim === $this->discrim && isset($this->items[$key])) {
|
||||
return $this->items[$key];
|
||||
}
|
||||
|
||||
foreach ($this->items as $item) {
|
||||
if (is_array($item) && isset($item[$discrim]) && $item[$discrim] === $key) {
|
||||
return $item;
|
||||
} elseif (is_object($item) && $item->{$discrim} === $key) {
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value in the collection.
|
||||
*
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function set($offset, $value)
|
||||
{
|
||||
// Don't insert elements that are not of type class.
|
||||
if (null !== $this->class && ! ($value instanceof $this->class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->offsetSet($offset, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls an item from the collection.
|
||||
*
|
||||
* @param mixed $key
|
||||
* @param mixed $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function pull($key, $default = null)
|
||||
{
|
||||
if (isset($this->items[$key])) {
|
||||
$default = $this->items[$key];
|
||||
unset($this->items[$key]);
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts an item from the collection.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function shift()
|
||||
{
|
||||
if (empty($this->items)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
reset($this->items);
|
||||
$key = key($this->items);
|
||||
$value = array_shift($this->items);
|
||||
|
||||
return [$key => $value];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills an array of items into the collection.
|
||||
*
|
||||
* @param ExCollectionInterface|array $items
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function fill($items): self
|
||||
{
|
||||
$items = $items instanceof CollectionInterface
|
||||
? $items->jsonSerialize()
|
||||
: $items;
|
||||
if (! is_array($items)) {
|
||||
throw new \InvalidArgumentException('The fill method only accepts arrays or CollectionInterface instances.');
|
||||
}
|
||||
|
||||
foreach ($items as $item) {
|
||||
$this->pushItem($item);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes items to the collection.
|
||||
*
|
||||
* @param mixed ...$items
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function push(...$items): self
|
||||
{
|
||||
foreach ($items as $item) {
|
||||
$this->pushItem($item);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes a single item to the collection.
|
||||
*
|
||||
* @param mixed $item
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function pushItem($item): self
|
||||
{
|
||||
if (null === $this->discrim) {
|
||||
$this->items[] = $item;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (null !== $this->class && ! ($item instanceof $this->class)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (is_array($item)) {
|
||||
$this->items[$item[$this->discrim]] = $item;
|
||||
} elseif (is_object($item)) {
|
||||
$this->items[$item->{$this->discrim}] = $item;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the amount of objects in the collection.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first element of the collection.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function first()
|
||||
{
|
||||
foreach ($this->items as $item) {
|
||||
return $item;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last element of the collection.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function last()
|
||||
{
|
||||
$last = end($this->items);
|
||||
|
||||
if ($last !== false) {
|
||||
reset($this->items);
|
||||
|
||||
return $last;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the collection has an offset.
|
||||
*
|
||||
* @param mixed $offset
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isset($offset): bool
|
||||
{
|
||||
return $this->offsetExists($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the array has multiple offsets.
|
||||
*
|
||||
* @param string|int ...$keys
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has(...$keys): bool
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
if (! isset($this->items[$key])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for a given value within the collection and returns the corresponding key if successful.
|
||||
*
|
||||
* @param mixed $needle
|
||||
* @param bool $strict [optional]
|
||||
*
|
||||
* @return string|int|false
|
||||
*/
|
||||
public function search(mixed $needle, bool $strict = false): string|int|false
|
||||
{
|
||||
return array_search($needle, $this->items, $strict);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a filter callback over the collection and returns a new Collection
|
||||
* based on the response of the callback.
|
||||
*
|
||||
* @param callable $callback
|
||||
*
|
||||
* @return ExCollectionInterface
|
||||
*
|
||||
* @todo This method will be typed to return a CollectionInterface in v11
|
||||
*/
|
||||
public function filter(callable $callback)
|
||||
{
|
||||
$collection = new Collection([], $this->discrim, $this->class);
|
||||
|
||||
foreach ($this->items as $item) {
|
||||
if ($callback($item)) {
|
||||
$collection->push($item);
|
||||
}
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a filter callback over the collection and returns the first item
|
||||
* where the callback returns `true` when given the item.
|
||||
*
|
||||
* @param callable $callback
|
||||
*
|
||||
* @return mixed `null` if no items returns `true` when called in the `$callback`.
|
||||
*/
|
||||
public function find(callable $callback)
|
||||
{
|
||||
foreach ($this->items as $item) {
|
||||
if ($callback($item)) {
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the key of the first item that matches the callback.
|
||||
*
|
||||
* @param callable $callback
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function find_key(callable $callback)
|
||||
{
|
||||
foreach ($this->items as $key => $item) {
|
||||
if ($callback($item)) {
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any item matches the callback.
|
||||
*
|
||||
* @param callable $callback
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function any(callable $callback): bool
|
||||
{
|
||||
foreach ($this->items as $item) {
|
||||
if ($callback($item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if all items match the callback.
|
||||
*
|
||||
* @param callable $callback
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function all(callable $callback): bool
|
||||
{
|
||||
foreach ($this->items as $item) {
|
||||
if (! $callback($item)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splices the collection, removing a portion of the items and replacing them with the given replacement.
|
||||
*
|
||||
* @param int $offset
|
||||
* @param ?int $length
|
||||
* @param mixed $replacement
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function splice(int $offset, ?int $length, mixed $replacement = []): self
|
||||
{
|
||||
array_splice($this->items, $offset, $length, $replacement);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the collection.
|
||||
*/
|
||||
public function clear(): void
|
||||
{
|
||||
$this->items = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Slices the collection.
|
||||
*
|
||||
* @param int $offset
|
||||
* @param ?int $length
|
||||
* @param bool $preserve_keys
|
||||
*
|
||||
* @return ExCollectionInterface
|
||||
*/
|
||||
public function slice(int $offset, ?int $length = null, bool $preserve_keys = false)
|
||||
{
|
||||
$items = $this->items;
|
||||
|
||||
$items = array_slice($items, $offset, $length, $preserve_keys);
|
||||
|
||||
return new Collection($items, $this->discrim, $this->class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort through each item with a callback.
|
||||
*
|
||||
* @param callable|int|null $callback
|
||||
*
|
||||
* @return ExCollectionInterface
|
||||
*/
|
||||
public function sort(callable|int|null $callback)
|
||||
{
|
||||
$items = $this->items;
|
||||
|
||||
$callback && is_callable($callback)
|
||||
? uasort($items, $callback)
|
||||
: asort($items, $callback ?? SORT_REGULAR);
|
||||
|
||||
return new Collection($items, $this->discrim, $this->class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the difference between the items.
|
||||
*
|
||||
* If a callback is provided and is callable, it uses `array_udiff_assoc` to compute the difference.
|
||||
* Otherwise, it uses `array_diff`.
|
||||
*
|
||||
* @param CollectionInterface|array $array
|
||||
* @param ?callable $callback
|
||||
*
|
||||
* @return ExCollectionInterface
|
||||
*/
|
||||
public function diff($items, ?callable $callback = null)
|
||||
{
|
||||
$items = $items instanceof CollectionInterface
|
||||
? $items->jsonSerialize()
|
||||
: $items;
|
||||
|
||||
$diff = $callback && is_callable($callback)
|
||||
? array_udiff_assoc($this->items, $items, $callback)
|
||||
: array_diff($this->items, $items);
|
||||
|
||||
return new Collection($diff, $this->discrim, $this->class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the intersection of the items.
|
||||
*
|
||||
* If a callback is provided and is callable, it uses `array_uintersect_assoc` to compute the intersection.
|
||||
* Otherwise, it uses `array_intersect`.
|
||||
*
|
||||
* @param CollectionInterface|array $array
|
||||
* @param ?callable $callback
|
||||
*
|
||||
* @return ExCollectionInterface
|
||||
*/
|
||||
public function intersect($items, ?callable $callback = null)
|
||||
{
|
||||
$items = $items instanceof CollectionInterface
|
||||
? $items->jsonSerialize()
|
||||
: $items;
|
||||
|
||||
$diff = $callback && is_callable($callback)
|
||||
? array_uintersect_assoc($this->items, $items, $callback)
|
||||
: array_intersect($this->items, $items);
|
||||
|
||||
return new Collection($diff, $this->discrim, $this->class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the given callback function to each item in the collection.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param mixed $arg
|
||||
*
|
||||
* @return ExCollectionInterface
|
||||
*/
|
||||
public function walk(callable $callback, mixed $arg = null)
|
||||
{
|
||||
$items = $this->items;
|
||||
|
||||
array_walk($items, $callback, $arg);
|
||||
|
||||
return new Collection($items, $this->discrim, $this->class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduces the collection to a single value using a callback function.
|
||||
*
|
||||
* @param callable $callback
|
||||
* @param ?mixed $initial
|
||||
*
|
||||
* @return ExCollectionInterface
|
||||
*/
|
||||
public function reduce(callable $callback, $initial = null)
|
||||
{
|
||||
$items = $this->items;
|
||||
|
||||
$items = array_reduce($items, $callback, $initial);
|
||||
|
||||
return new Collection($items, $this->discrim, $this->class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a callback over the collection and creates a new Collection.
|
||||
*
|
||||
* @param callable $callback
|
||||
*
|
||||
* @return ExCollectionInterface
|
||||
*/
|
||||
public function map(callable $callback)
|
||||
{
|
||||
$keys = array_keys($this->items);
|
||||
$values = array_map($callback, array_values($this->items));
|
||||
|
||||
return new Collection(array_combine($keys, $values), $this->discrim, $this->class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unique items.
|
||||
*
|
||||
* @param int $flags
|
||||
*
|
||||
* @return ExCollectionInterface
|
||||
*/
|
||||
public function unique(int $flags = SORT_STRING)
|
||||
{
|
||||
return new Collection(array_unique($this->items, $flags), $this->discrim, $this->class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges another collection into this collection.
|
||||
*
|
||||
* @param $collection
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function merge($collection): self
|
||||
{
|
||||
$items = $collection instanceof CollectionInterface
|
||||
? $collection->jsonSerialize()
|
||||
: $collection;
|
||||
|
||||
$this->items = array_merge($this->items, $items);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the collection to an array.
|
||||
*
|
||||
* @param bool $assoc Whether to map keys to values.
|
||||
*
|
||||
* @deprecated 10.42.0 Use `jsonSerialize`
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(bool $assoc = true): array
|
||||
{
|
||||
return $this->jsonSerialize($assoc);
|
||||
}
|
||||
/**
|
||||
* Converts the items into a new collection.
|
||||
*
|
||||
* @return ExCollectionInterface
|
||||
*/
|
||||
public function collect()
|
||||
{
|
||||
return new Collection($this->items, $this->discrim, $this->class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the keys of the items.
|
||||
*
|
||||
* @since 10.2.0
|
||||
*
|
||||
* @return int[]|string[]
|
||||
*/
|
||||
public function keys(): array
|
||||
{
|
||||
return array_keys($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the values of the items.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function values(): array
|
||||
{
|
||||
return array_values($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the collection has an offset.
|
||||
*
|
||||
* @param mixed $offset
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset): bool
|
||||
{
|
||||
return isset($this->items[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an item from the collection.
|
||||
*
|
||||
* @param mixed $offset
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->items[$offset] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an item into the collection.
|
||||
*
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function offsetSet($offset, $value): void
|
||||
{
|
||||
// Attempt to use the value's discrim property as the key if offset is null
|
||||
if (empty($offset)) {
|
||||
if (is_array($value) && isset($value[$this->discrim])) {
|
||||
$offset = $value[$this->discrim];
|
||||
} elseif (is_object($value) && property_exists($value, $this->discrim)) {
|
||||
$offset = $value->{$this->discrim};
|
||||
}
|
||||
}
|
||||
|
||||
$this->items[$offset] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets an index from the collection.
|
||||
*
|
||||
* @param mixed $offset
|
||||
*/
|
||||
public function offsetUnset($offset): void
|
||||
{
|
||||
unset($this->items[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of the collection.
|
||||
*
|
||||
* @param int $flags
|
||||
* @param ?int $depth
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function serialize(int $flags = 0, ?int $depth = 512): string
|
||||
{
|
||||
return json_encode($this->items, $flags, $depth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of the collection.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __serialize(): array
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unserializes the collection.
|
||||
*
|
||||
* @param string $serialized
|
||||
*/
|
||||
public function unserialize(string $serialized): void
|
||||
{
|
||||
$this->items = json_decode($serialized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unserializes the collection.
|
||||
*
|
||||
* @param ExCollectionInterface|array $data
|
||||
*/
|
||||
public function __unserialize($data): void
|
||||
{
|
||||
if ($data instanceof CollectionInterface) {
|
||||
$data = $data->jsonSerialize();
|
||||
}
|
||||
if (! is_array($data)) {
|
||||
throw new \InvalidArgumentException('The __unserialize method only accepts arrays or CollectionInterface instances.');
|
||||
}
|
||||
|
||||
$this->items = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the object to a value that can be serialized natively by json_encode().
|
||||
*
|
||||
* @param bool $assoc Whether to map keys to values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function jsonSerialize(bool $assoc = true): array
|
||||
{
|
||||
return $assoc
|
||||
? $this->items
|
||||
: array_values($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator for the collection.
|
||||
*
|
||||
* @return \Traversable
|
||||
*/
|
||||
public function getIterator(): \Traversable
|
||||
{
|
||||
return new \ArrayIterator($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an item that will be displayed for debugging.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
}
|
||||
42
vendor/discord-php-helpers/collection/src/Discord/Helpers/ExCollectionInterface.php
vendored
Normal file
42
vendor/discord-php-helpers/collection/src/Discord/Helpers/ExCollectionInterface.php
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Helpers;
|
||||
|
||||
interface ExCollectionInterface extends CollectionInterface
|
||||
{
|
||||
public function shift();
|
||||
public function search(mixed $needle, bool $strict = false): string|int|false;
|
||||
public function find_key(callable $callback);
|
||||
public function any(callable $callback): bool;
|
||||
public function all(callable $callback): bool;
|
||||
/** @return ExCollectionInterface */
|
||||
public function splice(int $offset, ?int $length, mixed $replacement = []): self;
|
||||
public function clear(): void;
|
||||
/** @return ExCollectionInterface */
|
||||
public function slice(int $offset, ?int $length = null, bool $preserve_keys = false);
|
||||
/** @return ExCollectionInterface */
|
||||
public function sort(callable|int|null $callback);
|
||||
/** @return ExCollectionInterface */
|
||||
public function diff($items, ?callable $callback = null);
|
||||
/** @return ExCollectionInterface */
|
||||
public function intersect($items, ?callable $callback = null);
|
||||
/** @return ExCollectionInterface */
|
||||
public function walk(callable $callback, mixed $arg = null);
|
||||
/** @return ExCollectionInterface */
|
||||
public function reduce(callable $callback, $initial = null);
|
||||
/** @return ExCollectionInterface */
|
||||
public function unique(int $flags = SORT_STRING);
|
||||
public function keys(): array;
|
||||
public function values(): array;
|
||||
}
|
||||
15
vendor/discord-php-helpers/voice/.editorconfig
vendored
Normal file
15
vendor/discord-php-helpers/voice/.editorconfig
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
|
||||
[*.js]
|
||||
indent_size = 2
|
||||
2
vendor/discord-php-helpers/voice/.github/FUNDING.yml
vendored
Normal file
2
vendor/discord-php-helpers/voice/.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
github: [valzargaming, Log1x]
|
||||
patreon: DiscordPHP
|
||||
39
vendor/discord-php-helpers/voice/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
39
vendor/discord-php-helpers/voice/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
|
||||
---
|
||||
|
||||
<!-- Please keep issues strictly about bugs and feature requests. -->
|
||||
<!-- For any other questions, either join our discord at https://discord.gg/dphp or click on "Discussions" above. -->
|
||||
|
||||
<!-- Please provide your PHP and DiscordPHP version. This will help us debug. -->
|
||||
<!-- PHP version can be retrieved by running `php -v` -->
|
||||
<!-- DiscordPHP version can be retrieved by running `composer show team-reflex/discord-php` -->
|
||||
**Environment**
|
||||
- PHP Version:
|
||||
- x.x.x
|
||||
- DiscordPHP Version:
|
||||
- x.x.x
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
```php
|
||||
$discord->on(..., function () {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
4
vendor/discord-php-helpers/voice/.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
4
vendor/discord-php-helpers/voice/.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
contact_links:
|
||||
- name: Question and Answers
|
||||
url: https://github.com/discord-php/DiscordPHP/discussions/categories/q-a
|
||||
about: If you have question about how to use this Library and have not find any solution in documentation or wiki, please do not make issues, instead go find or create one in Discussions section.
|
||||
22
vendor/discord-php-helpers/voice/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
22
vendor/discord-php-helpers/voice/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: features
|
||||
|
||||
---
|
||||
|
||||
<!-- Please keep issues strictly about bugs and feature requests. -->
|
||||
<!-- For any other questions, either join our discord at https://discord.gg/dphp or click on "Discussions" above. -->
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
11
vendor/discord-php-helpers/voice/.github/dependabot.yml
vendored
Normal file
11
vendor/discord-php-helpers/voice/.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "composer" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "daily"
|
||||
36
vendor/discord-php-helpers/voice/.github/workflows/docs.yml
vendored
Normal file
36
vendor/discord-php-helpers/voice/.github/workflows/docs.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
name: Build Docs
|
||||
on:
|
||||
push:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
docs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'release' || contains(github.event.head_commit.message, 'build docs')
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.1'
|
||||
extensions: iconv
|
||||
tools: phive
|
||||
- name: Install phpDocumentor
|
||||
run: phive install phpDocumentor --trust-gpg-keys 67F861C3D889C656,6DA3ACC4991FFAE5
|
||||
- name: Build class reference
|
||||
run: ./tools/phpDocumentor
|
||||
- name: Build documentation
|
||||
run: |
|
||||
cd ./docs
|
||||
yarn install
|
||||
yarn build
|
||||
sudo mv public/* ../build
|
||||
- name: Publish docs
|
||||
uses: JamesIves/github-pages-deploy-action@3.7.1
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
BRANCH: gh-pages
|
||||
FOLDER: build
|
||||
CLEAN: true
|
||||
42
vendor/discord-php-helpers/voice/.github/workflows/unit.yml
vendored
Normal file
42
vendor/discord-php-helpers/voice/.github/workflows/unit.yml
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
name: Unit Tests
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
||||
TEST_CHANNEL: ${{ secrets.TEST_CHANNEL }}
|
||||
TEST_CHANNEL_NAME: ${{ secrets.TEST_CHANNEL_NAME }}
|
||||
|
||||
jobs:
|
||||
unit-lint:
|
||||
name: PHPUnit and Lint
|
||||
runs-on: ubuntu-latest
|
||||
if: "!contains(github.event.head_commit.message, 'no test')"
|
||||
concurrency: phpunit
|
||||
strategy:
|
||||
matrix:
|
||||
php:
|
||||
- '8.4'
|
||||
- '8.3'
|
||||
- '8.2'
|
||||
- '8.1'
|
||||
- '8.0'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: uv, zlib, mbstring
|
||||
tools: phpunit, phplint
|
||||
- name: Install dependencies
|
||||
run: composer install
|
||||
- name: Redis Server in GitHub Actions
|
||||
uses: supercharge/redis-github-action@1.4.0
|
||||
# - name: Memcached Service
|
||||
# uses: niden/actions-memcached@v7
|
||||
- name: Run PHPUnit
|
||||
run: phpunit
|
||||
- name: Run PHPLint
|
||||
run: phplint
|
||||
32
vendor/discord-php-helpers/voice/.gitignore
vendored
Normal file
32
vendor/discord-php-helpers/voice/.gitignore
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
# general
|
||||
.env
|
||||
.DS_Store
|
||||
test*.php
|
||||
.idea
|
||||
.phplint-cache
|
||||
dump.html
|
||||
.vscode
|
||||
|
||||
# phive
|
||||
/tools
|
||||
.phive
|
||||
|
||||
# composer
|
||||
/vendor
|
||||
composer.lock
|
||||
|
||||
# docs
|
||||
/build
|
||||
.phpdoc
|
||||
|
||||
# php-cs-fixer
|
||||
.php_cs.cache
|
||||
.php_cs
|
||||
.php-cs-fixer.php
|
||||
.php-cs-fixer.cache
|
||||
|
||||
# phpunit
|
||||
phpunit.log
|
||||
/.phpunit*
|
||||
/coverage
|
||||
/.vs
|
||||
102
vendor/discord-php-helpers/voice/.php-cs-fixer.dist.php
vendored
Normal file
102
vendor/discord-php-helpers/voice/.php-cs-fixer.dist.php
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
$header = <<<'EOF'
|
||||
This file is a part of the DiscordPHP project.
|
||||
|
||||
Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
|
||||
This file is subject to the MIT license that is bundled
|
||||
with this source code in the LICENSE.md file.
|
||||
EOF;
|
||||
|
||||
$fixers = [
|
||||
'blank_line_after_namespace',
|
||||
'braces',
|
||||
'class_definition',
|
||||
'elseif',
|
||||
'encoding',
|
||||
'full_opening_tag',
|
||||
'function_declaration',
|
||||
'lowercase_keywords',
|
||||
'method_argument_space',
|
||||
'no_closing_tag',
|
||||
'no_spaces_after_function_name',
|
||||
'no_spaces_inside_parenthesis',
|
||||
'no_trailing_whitespace',
|
||||
'no_trailing_whitespace_in_comment',
|
||||
'single_blank_line_at_eof',
|
||||
'single_class_element_per_statement',
|
||||
'single_import_per_statement',
|
||||
'single_line_after_imports',
|
||||
'switch_case_semicolon_to_colon',
|
||||
'switch_case_space',
|
||||
'visibility_required',
|
||||
'blank_line_after_opening_tag',
|
||||
'no_multiline_whitespace_around_double_arrow',
|
||||
'no_empty_statement',
|
||||
'include',
|
||||
'no_trailing_comma_in_list_call',
|
||||
'not_operator_with_successor_space',
|
||||
'no_leading_namespace_whitespace',
|
||||
'no_blank_lines_after_class_opening',
|
||||
'no_blank_lines_after_phpdoc',
|
||||
'object_operator_without_whitespace',
|
||||
'binary_operator_spaces',
|
||||
'phpdoc_indent',
|
||||
'general_phpdoc_tag_rename',
|
||||
'phpdoc_inline_tag_normalizer',
|
||||
'phpdoc_tag_type',
|
||||
'phpdoc_no_access',
|
||||
'phpdoc_no_package',
|
||||
'phpdoc_scalar',
|
||||
'phpdoc_summary',
|
||||
'phpdoc_trim',
|
||||
'phpdoc_var_without_name',
|
||||
'no_leading_import_slash',
|
||||
'no_trailing_comma_in_singleline_array',
|
||||
'single_blank_line_before_namespace',
|
||||
'single_quote',
|
||||
'no_singleline_whitespace_before_semicolons',
|
||||
'cast_spaces',
|
||||
'standardize_not_equals',
|
||||
'ternary_operator_spaces',
|
||||
'trim_array_spaces',
|
||||
'unary_operator_spaces',
|
||||
'no_unused_imports',
|
||||
'no_useless_else',
|
||||
'no_useless_return',
|
||||
'phpdoc_no_empty_return',
|
||||
'no_extra_blank_lines',
|
||||
'multiline_whitespace_before_semicolons',
|
||||
];
|
||||
|
||||
$rules = [
|
||||
'concat_space' => ['spacing' => 'none'],
|
||||
'phpdoc_no_alias_tag' => ['replacements' => ['type' => 'var']],
|
||||
'array_syntax' => ['syntax' => 'short'],
|
||||
'binary_operator_spaces' => ['align_double_arrow' => true, 'align_equals' => true],
|
||||
'header_comment' => ['header' => $header],
|
||||
'indentation_type' => true,
|
||||
'phpdoc_align' => [
|
||||
'align' => 'vertical',
|
||||
'tags' => ['param', 'property', 'property-read', 'property-write', 'return', 'throws', 'type', 'var', 'method'],
|
||||
],
|
||||
'blank_line_before_statement' => ['statements' => ['return']],
|
||||
'constant_case' => ['case' => 'lower'],
|
||||
'echo_tag_syntax' => ['format' => 'long'],
|
||||
'trailing_comma_in_multiline' => ['elements' => ['arrays']],
|
||||
];
|
||||
|
||||
foreach ($fixers as $fix) {
|
||||
$rules[$fix] = true;
|
||||
}
|
||||
|
||||
$config = new PhpCsFixer\Config();
|
||||
|
||||
return $config
|
||||
->setRules($rules)
|
||||
->setFinder(
|
||||
PhpCsFixer\Finder::create()
|
||||
->exclude('examples')
|
||||
->in(__DIR__)
|
||||
);
|
||||
7
vendor/discord-php-helpers/voice/.phplint.yml
vendored
Normal file
7
vendor/discord-php-helpers/voice/.phplint.yml
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
path: ./
|
||||
jobs: 10
|
||||
extensions:
|
||||
- php
|
||||
exclude:
|
||||
- vendor
|
||||
warning: false
|
||||
3
vendor/discord-php-helpers/voice/CONTRIBUTING.md
vendored
Normal file
3
vendor/discord-php-helpers/voice/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
We are open to contributions. However, please make sure you follow our coding standards (PSR-4 autoloading and custom styling). Please run php-cs-fixer before opening a pull request by running ``composer run-script cs.``
|
||||
|
||||
Please only use the issue tracker for submitting issues with the code. If you have questions about how to use DiscordPHP, hop over to our discord at [](https://discord.gg/dphp)
|
||||
21
vendor/discord-php-helpers/voice/LICENSE
vendored
Normal file
21
vendor/discord-php-helpers/voice/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 DiscordPHP
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
21
vendor/discord-php-helpers/voice/LICENSE.md
vendored
Normal file
21
vendor/discord-php-helpers/voice/LICENSE.md
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2022 David Cole <david.cole1340@gmail.com> and all contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
106
vendor/discord-php-helpers/voice/README.md
vendored
Normal file
106
vendor/discord-php-helpers/voice/README.md
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
DiscordPHP Voice
|
||||
====
|
||||
[](https://packagist.org/packages/team-reflex/discord-php) [](https://packagist.org/packages/team-reflex/discord-php) [](https://packagist.org/packages/team-reflex/discord-php) [](https://packagist.org/packages/team-reflex/discord-php)
|
||||
|
||||
[](https://discord.gg/dphp)
|
||||
|
||||
A wrapper for the official [Discord](https://discordapp.com) REST, gateway and voice APIs. Documentation is [available here](http://discord-php.github.io/DiscordPHP), albeit limited at the moment, as well as a class reference. Feel free to ask questions in the Discord server above.
|
||||
|
||||
For testing and stability it would be greatly appreciated if you were able to add our test bot to your server. We don't store any data - the bot simply idles and does not interact with anyone and is used to test stability with large numbers of guilds. You can invite the bot [here.](https://discord.com/oauth2/authorize?client_id=157746770539970560&scope=bot)
|
||||
|
||||
## Cache Interface (experimental)
|
||||
> **Warning**
|
||||
> This branch contains an experimental feature, do not use it in production! See [the wiki page for more information](https://github.com/discord-php/DiscordPHP/wiki/Cache-Interface) on how to set it up.
|
||||
|
||||
## FAQ
|
||||
|
||||
1. Can I run DiscordPHP on a webserver (e.g. Apache, nginx)?
|
||||
- No, DiscordPHP will only run in CLI. If you want to have an interface for your bot you can integrate [react/http](https://github.com/ReactPHP/http) with your bot and run it through CLI.
|
||||
2. PHP is running out of memory?
|
||||
- Try unlimit your PHP memory using `ini_set('memory_limit', '-1');`.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Before you start using this Library, you **need** to know how PHP works, you need to know how Event Loops and Promises work. This is a fundamental requirement before you start. Without this knowledge, you will only suffer.
|
||||
|
||||
### Requirements
|
||||
|
||||
- [PHP 8.0](https://php.net) or higher (latest version recommended)
|
||||
- x86 (32-bit) PHP requires [`ext-gmp`](https://www.php.net/manual/en/book.gmp.php) enabled.
|
||||
- [`ext-json`](https://www.php.net/manual/en/book.json.php)
|
||||
- [`ext-zlib`](https://www.php.net/manual/en/book.zlib.php)
|
||||
|
||||
#### Recommended Extensions
|
||||
|
||||
- One of [`ext-uv`](https://github.com/amphp/ext-uv) (recommended), `ext-ev` or `ext-event` for a faster, and more performant event loop.
|
||||
- [`ext-mbstring`](https://www.php.net/manual/en/book.mbstring.php) if handling non-latin characters.
|
||||
|
||||
#### Voice Requirements
|
||||
|
||||
- 64-bit PHP
|
||||
- [`ext-sodium`](https://www.php.net/manual/en/book.sodium.php)
|
||||
- [FFmpeg](https://ffmpeg.org/)
|
||||
|
||||
### Windows and SSL
|
||||
|
||||
Unfortunately PHP on Windows does not have access to the Windows Certificate Store. This is an issue because TLS gets used and as such certificate verification gets applied (turning this off is **not** an option).
|
||||
|
||||
You will notice this issue by your script exiting immediately after one loop turn without any errors.
|
||||
|
||||
As such users of this library need to download a [Certificate Authority extract](https://curl.haxx.se/docs/caextract.html) from the cURL website.<br>
|
||||
The path to the caextract must be set in the [`php.ini`](https://secure.php.net/manual/en/openssl.configuration.php) for `openssl.cafile`.
|
||||
|
||||
### Installing DiscordPHP
|
||||
|
||||
DiscordPHP is installed using [Composer](https://getcomposer.org).
|
||||
|
||||
1. Run `composer require team-reflex/discord-php`. This will install the latest stable release.
|
||||
- If you would like, you can also install the development branch by running `composer require team-reflex/discord-php dev-master`.
|
||||
2. Include the Composer autoload file at the top of your main file:
|
||||
- `include __DIR__.'/vendor/autoload.php';`
|
||||
3. Make a bot!
|
||||
|
||||
### Basic Example
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
include __DIR__.'/vendor/autoload.php';
|
||||
|
||||
use Discord\Discord;
|
||||
use Discord\Parts\Channel\Message;
|
||||
use Discord\WebSockets\Intents;
|
||||
use Discord\WebSockets\Event;
|
||||
|
||||
$discord = new Discord([
|
||||
'token' => 'bot-token',
|
||||
'intents' => Intents::getDefaultIntents()
|
||||
// | Intents::MESSAGE_CONTENT, // Note: MESSAGE_CONTENT is privileged, see https://dis.gd/mcfaq
|
||||
]);
|
||||
|
||||
$discord->on('ready', function (Discord $discord) {
|
||||
echo "Bot is ready!", PHP_EOL;
|
||||
|
||||
// Listen for messages.
|
||||
$discord->on(Event::MESSAGE_CREATE, function (Message $message, Discord $discord) {
|
||||
echo "{$message->author->username}: {$message->content}", PHP_EOL;
|
||||
// Note: MESSAGE_CONTENT intent must be enabled to get the content if the bot is not mentioned/DMed.
|
||||
});
|
||||
});
|
||||
|
||||
$discord->run();
|
||||
```
|
||||
|
||||
See [examples folder](examples) for more.
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation for the latest version can be found [here](//discord-php.github.io/DiscordPHP/guide). Community contributed tutorials can be found on the [wiki](//github.com/discord-php/DiscordPHP/wiki).
|
||||
|
||||
## Contributing
|
||||
|
||||
We are open to contributions. However, please make sure you follow our coding standards (PSR-4 autoloading and custom styling). Please run php-cs-fixer before opening a pull request by running `composer run-script cs`.
|
||||
|
||||
## License
|
||||
|
||||
MIT License, © David Cole and other contributers 2016-present.
|
||||
43
vendor/discord-php-helpers/voice/composer.json
vendored
Normal file
43
vendor/discord-php-helpers/voice/composer.json
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "discord-php-helpers/voice",
|
||||
"description": "A voice library for DiscordPHP",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "David Cole",
|
||||
"email": "david.cole1340@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Alexandre Candeias",
|
||||
"email": "alexandreluisbarreto@gmail.com"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/discord-php/DiscordPHP-Voice/issues",
|
||||
"wiki": "https://github.com/discord-php/DiscordPHP/wiki",
|
||||
"docs": "https://discord-php.github.io/DiscordPHP/",
|
||||
"chat": "https://discord.gg/dphp"
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.1.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"team-reflex/discord-php": "dev-master",
|
||||
"ext-ffi": "*",
|
||||
"friendsofphp/php-cs-fixer": "^3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Discord\\": "src/Discord"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"pint": ["./vendor/bin/pint --config ./pint.json"],
|
||||
"cs": ["./vendor/bin/php-cs-fixer fix"],
|
||||
"cs-unsupported": ["./vendor/bin/php-cs-fixer fix --allow-unsupported-php-version yes"],
|
||||
"unit": ["./vendor/bin/phpunit --testdox"],
|
||||
"coverage": ["XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-html coverage --testdox"]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
||||
27
vendor/discord-php-helpers/voice/phpdoc.dist.xml
vendored
Normal file
27
vendor/discord-php-helpers/voice/phpdoc.dist.xml
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<phpdocumentor
|
||||
configVersion="3"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://www.phpdoc.org"
|
||||
xsi:noNamespaceSchemaLocation="https://docs.phpdoc.org/latest/phpdoc.xsd"
|
||||
>
|
||||
<title>DiscordPHP Documentation</title>
|
||||
<paths>
|
||||
<output>build</output>
|
||||
</paths>
|
||||
<version number="latest">
|
||||
<api>
|
||||
<source dsn=".">
|
||||
<path>src</path>
|
||||
</source>
|
||||
<output>reference</output>
|
||||
</api>
|
||||
<guide>
|
||||
<source dsn=".">
|
||||
<path>guide</path>
|
||||
</source>
|
||||
<output>guide</output>
|
||||
</guide>
|
||||
</version>
|
||||
<setting name="guides.enabled" value="true"/>
|
||||
</phpdocumentor>
|
||||
27
vendor/discord-php-helpers/voice/phpunit.xml
vendored
Normal file
27
vendor/discord-php-helpers/voice/phpunit.xml
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
|
||||
bootstrap="tests/bootstrap.php"
|
||||
cacheResultFile=".phpunit.cache/test-results"
|
||||
executionOrder="depends,defects"
|
||||
forceCoversAnnotation="false"
|
||||
beStrictAboutCoversAnnotation="false"
|
||||
beStrictAboutOutputDuringTests="true"
|
||||
beStrictAboutTodoAnnotatedTests="true"
|
||||
failOnRisky="true"
|
||||
failOnWarning="true"
|
||||
verbose="true">
|
||||
<testsuites>
|
||||
<testsuite name="default">
|
||||
<directory suffix="Test.php">tests</directory>
|
||||
<!--<file>tests/DiscordTest.php</file>-->
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<coverage cacheDirectory=".phpunit.cache/code-coverage"
|
||||
processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">src</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
</phpunit>
|
||||
6
vendor/discord-php-helpers/voice/pint.json
vendored
Normal file
6
vendor/discord-php-helpers/voice/pint.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"preset": "psr12",
|
||||
"rules": {
|
||||
"declare_strict_types": true
|
||||
}
|
||||
}
|
||||
51
vendor/discord-php-helpers/voice/src/Discord/Factory/SocketFactory.php
vendored
Normal file
51
vendor/discord-php-helpers/voice/src/Discord/Factory/SocketFactory.php
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Factory;
|
||||
|
||||
use Discord\Voice\Client\UDP;
|
||||
use Discord\Voice\Client\WS;
|
||||
use React\Datagram\Factory;
|
||||
use React\Dns\Resolver\Factory as DnsFactory;
|
||||
|
||||
final class SocketFactory extends Factory
|
||||
{
|
||||
protected ?WS $ws;
|
||||
|
||||
public function __construct($loop = null, $resolver = null, ?WS $ws = null)
|
||||
{
|
||||
if (null === $resolver) {
|
||||
$resolver = (new DnsFactory())->createCached($ws->data['dnsConfig'], $loop);
|
||||
}
|
||||
|
||||
parent::__construct($loop, $resolver);
|
||||
|
||||
if ($ws !== null) {
|
||||
$this->ws = $ws;
|
||||
}
|
||||
}
|
||||
|
||||
public function createClient($address)
|
||||
{
|
||||
$loop = $this->loop;
|
||||
|
||||
return $this->resolveAddress($address)->then(function ($address) use ($loop) {
|
||||
$socket = @\stream_socket_client($address, $errno, $errstr);
|
||||
if (! $socket) {
|
||||
throw new \Exception('Unable to create client socket: '.$errstr, $errno);
|
||||
}
|
||||
|
||||
return new UDP($loop, $socket, ws: $this?->ws);
|
||||
});
|
||||
}
|
||||
}
|
||||
26
vendor/discord-php-helpers/voice/src/Discord/Parts/Voice/UserConnected.php
vendored
Normal file
26
vendor/discord-php-helpers/voice/src/Discord/Parts/Voice/UserConnected.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Parts\Voice;
|
||||
|
||||
use Discord\Parts\Part;
|
||||
|
||||
class UserConnected extends Part
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
];
|
||||
}
|
||||
28
vendor/discord-php-helpers/voice/src/Discord/Voice/ByteBuffer/AbstractBuffer.php
vendored
Normal file
28
vendor/discord-php-helpers/voice/src/Discord/Voice/ByteBuffer/AbstractBuffer.php
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author alexandre433
|
||||
*/
|
||||
abstract class AbstractBuffer implements ReadableBuffer, WriteableBuffer
|
||||
{
|
||||
abstract public function __construct($argument);
|
||||
|
||||
abstract public function __toString(): string;
|
||||
|
||||
abstract public function length(): int;
|
||||
|
||||
abstract public function getLastEmptyPosition(): int;
|
||||
}
|
||||
307
vendor/discord-php-helpers/voice/src/Discord/Voice/ByteBuffer/Buffer.php
vendored
Normal file
307
vendor/discord-php-helpers/voice/src/Discord/Voice/ByteBuffer/Buffer.php
vendored
Normal file
@ -0,0 +1,307 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\ByteBuffer;
|
||||
|
||||
/**
|
||||
* Helper class for handling binary data.
|
||||
*
|
||||
* @author alexandre433
|
||||
*
|
||||
* @throws \InvalidArgumentException If invalid arguments are provided or buffer overflows.
|
||||
*/
|
||||
class Buffer extends AbstractBuffer implements \ArrayAccess
|
||||
{
|
||||
use BufferArrayAccessTrait;
|
||||
|
||||
protected \SplFixedArray $buffer;
|
||||
|
||||
public function __construct($argument)
|
||||
{
|
||||
is_string($argument)
|
||||
? $this->initializeStructs(strlen($argument), $argument)
|
||||
: (is_int($argument)
|
||||
? $this->initializeStructs($argument, pack(FormatPackEnum::x->value."$argument"))
|
||||
: throw new \InvalidArgumentException('Constructor argument must be an binary string or integer'));
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return implode('', iterator_to_array($this->buffer, false));
|
||||
}
|
||||
|
||||
public static function make($argument): static
|
||||
{
|
||||
return new static($argument);
|
||||
}
|
||||
|
||||
protected function initializeStructs($length, string $content): void
|
||||
{
|
||||
$this->buffer = new \SplFixedArray($length);
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$this->buffer[$i] = $content[$i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a value into the buffer at the specified offset.
|
||||
*
|
||||
* @param FormatPackEnum|string $format
|
||||
* @param mixed $value
|
||||
* @param int $offset
|
||||
* @param ?int $length
|
||||
* @return Buffer
|
||||
*/
|
||||
protected function insert($format, $value, int $offset, ?int $length = null): self
|
||||
{
|
||||
$bytes = pack($format?->value ?? $format, $value);
|
||||
|
||||
if (null === $length) {
|
||||
$length = strlen($bytes);
|
||||
}
|
||||
|
||||
for ($i = 0; $i < strlen($bytes); $i++) {
|
||||
$this->buffer[$offset++] = $bytes[$i];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a value from the buffer at the specified offset.
|
||||
*
|
||||
* @param FormatPackEnum|string $format
|
||||
* @param int $offset
|
||||
* @param int $length
|
||||
* @return mixed
|
||||
*/
|
||||
protected function extract(FormatPackEnum|string $format, int $offset, int $length)
|
||||
{
|
||||
$encoded = '';
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$encoded .= $this->buffer->offsetGet($offset + $i);
|
||||
}
|
||||
|
||||
if ($format == FormatPackEnum::N && PHP_INT_SIZE <= 4) {
|
||||
[, $h, $l] = unpack('n*', $encoded);
|
||||
$result = $l + $h * 0x010000;
|
||||
} elseif ($format == FormatPackEnum::V && PHP_INT_SIZE <= 4) {
|
||||
[, $h, $l] = unpack('v*', $encoded);
|
||||
$result = $h + $l * 0x010000;
|
||||
} else {
|
||||
[, $result] = unpack($format?->value ?? $format, $encoded);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the actual value exceeds the expected maximum size.
|
||||
*
|
||||
* @param mixed $excpectedMax
|
||||
* @param mixed $actual
|
||||
* @throws \InvalidArgumentException
|
||||
* @return static
|
||||
*/
|
||||
protected function checkForOverSize($expectedMax, string|int $actual): self
|
||||
{
|
||||
if ($actual > $expectedMax) {
|
||||
throw new \InvalidArgumentException('actual exceeded expectedMax limit');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function length(): int
|
||||
{
|
||||
return $this->buffer->getSize();
|
||||
}
|
||||
|
||||
public function getLastEmptyPosition(): int
|
||||
{
|
||||
foreach ($this->buffer as $key => $value) {
|
||||
if (empty(trim($value))) {
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string to the buffer at the specified offset.
|
||||
*
|
||||
* @param string $value The value that will be written.
|
||||
* @param int|null $offset The offset that the value will be written at.
|
||||
* @return static
|
||||
*/
|
||||
public function write($value, ?int $offset = null): self
|
||||
{
|
||||
if (null === $offset) {
|
||||
$offset = $this->getLastEmptyPosition();
|
||||
}
|
||||
|
||||
$length = strlen($value);
|
||||
$this->insert('a'.$length, $value, $offset, $length);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an 8-bit signed integer to the buffer at the specified offset.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int|null $offset The offset that the value will be written at.
|
||||
* @return static
|
||||
*/
|
||||
public function writeInt8($value, ?int $offset = null): self
|
||||
{
|
||||
if (null === $offset) {
|
||||
$offset = $this->getLastEmptyPosition();
|
||||
}
|
||||
|
||||
$format = FormatPackEnum::C;
|
||||
$this->checkForOverSize(0xff, $value);
|
||||
$this->insert($format, $value, $offset, $format->getLength());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a 16-bit signed integer to the buffer at the specified offset.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int|null $offset The offset that the value will be written at.
|
||||
* @return static
|
||||
*/
|
||||
public function writeInt16BE($value, ?int $offset = null): self
|
||||
{
|
||||
if (null === $offset) {
|
||||
$offset = $this->getLastEmptyPosition();
|
||||
}
|
||||
|
||||
$format = FormatPackEnum::n;
|
||||
$this->checkForOverSize(0xffff, $value);
|
||||
$this->insert($format, $value, $offset, $format->getLength());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a 16-bit signed integer to the buffer at the specified offset.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int|null $offset The offset that the value will be written at.
|
||||
* @return static
|
||||
*/
|
||||
public function writeInt16LE($value, ?int $offset = null): self
|
||||
{
|
||||
if (null === $offset) {
|
||||
$offset = $this->getLastEmptyPosition();
|
||||
}
|
||||
|
||||
$format = FormatPackEnum::v;
|
||||
$this->checkForOverSize(0xffff, $value);
|
||||
$this->insert($format, $value, $offset, $format->getLength());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a 32-bit signed integer to the buffer at the specified offset.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int|null $offset The offset that the value will be written at.
|
||||
* @return static
|
||||
*/
|
||||
public function writeInt32BE($value, ?int $offset = null): self
|
||||
{
|
||||
if (null === $offset) {
|
||||
$offset = $this->getLastEmptyPosition();
|
||||
}
|
||||
|
||||
$format = FormatPackEnum::N;
|
||||
$this->checkForOverSize(0xffffffff, $value);
|
||||
$this->insert($format, $value, $offset, $format->getLength());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a 32-bit signed integer to the buffer at the specified offset.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int|null $offset The offset that the value will be written at.
|
||||
* @return static
|
||||
*/
|
||||
#[\Override]
|
||||
public function writeInt32LE($value, ?int $offset = null): self
|
||||
{
|
||||
if (null === $offset) {
|
||||
$offset = $this->getLastEmptyPosition();
|
||||
}
|
||||
|
||||
$format = FormatPackEnum::V;
|
||||
$this->checkForOverSize(0xffffffff, $value);
|
||||
$this->insert($format, $value, $offset, $format->getLength());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a string from the buffer at the specified offset.
|
||||
*
|
||||
* @param int $offset The offset to read from.
|
||||
* @param int $length The length of the string to read.
|
||||
* @return string The data read.
|
||||
*/
|
||||
public function read(int $offset, int $length)
|
||||
{
|
||||
return $this->extract('a'.$length, $offset, $length);
|
||||
}
|
||||
|
||||
public function readInt8(int $offset)
|
||||
{
|
||||
$format = FormatPackEnum::C;
|
||||
|
||||
return $this->extract($format, $offset, $format->getLength());
|
||||
}
|
||||
|
||||
public function readInt16BE(int $offset)
|
||||
{
|
||||
$format = FormatPackEnum::n;
|
||||
|
||||
return $this->extract($format, $offset, $format->getLength());
|
||||
}
|
||||
|
||||
public function readInt16LE(int $offset)
|
||||
{
|
||||
$format = FormatPackEnum::v;
|
||||
|
||||
return $this->extract($format, $offset, $format->getLength());
|
||||
}
|
||||
|
||||
public function readInt32BE(int $offset)
|
||||
{
|
||||
$format = FormatPackEnum::N;
|
||||
|
||||
return $this->extract($format, $offset, $format->getLength());
|
||||
}
|
||||
|
||||
public function readInt32LE(int $offset)
|
||||
{
|
||||
$format = FormatPackEnum::V;
|
||||
|
||||
return $this->extract($format, $offset, $format->getLength());
|
||||
}
|
||||
}
|
||||
216
vendor/discord-php-helpers/voice/src/Discord/Voice/ByteBuffer/BufferArrayAccessTrait.php
vendored
Normal file
216
vendor/discord-php-helpers/voice/src/Discord/Voice/ByteBuffer/BufferArrayAccessTrait.php
vendored
Normal file
@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author Valithor Obsidion <valithor@discordphp.org>
|
||||
*/
|
||||
trait BufferArrayAccessTrait
|
||||
{
|
||||
/**
|
||||
* Writes a 32-bit unsigned integer with big endian.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int $offset The offset that the value will be written.
|
||||
*/
|
||||
public function writeUInt32BE(int $value, int $offset): self
|
||||
{
|
||||
return $this->insert(FormatPackEnum::N, $value, $offset, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a 64-bit unsigned integer with little endian.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int $offset The offset that the value will be written.
|
||||
*/
|
||||
public function writeUInt64LE(int $value, int $offset): self
|
||||
{
|
||||
return $this->insert(FormatPackEnum::P, $value, $offset, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a signed integer.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int $offset The offset that the value will be written.
|
||||
*/
|
||||
public function writeInt(int $value, int $offset): self
|
||||
{
|
||||
return $this->insert(FormatPackEnum::N, $value, $offset, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a unsigned integer.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int $offset The offset that the value will be written.
|
||||
*/
|
||||
public function writeUInt(int $value, int $offset): self
|
||||
{
|
||||
return $this->insert(FormatPackEnum::I, $value, $offset, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a signed integer.
|
||||
*
|
||||
* @param int $offset The offset to read from.
|
||||
*
|
||||
* @return int The data read.
|
||||
*/
|
||||
public function readInt(int $offset): int
|
||||
{
|
||||
return $this->extract(FormatPackEnum::N, $offset, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a signed integer.
|
||||
*
|
||||
* @param int $offset The offset to read from.
|
||||
*
|
||||
* @return int The data read.
|
||||
*/
|
||||
public function readUInt(int $offset): int
|
||||
{
|
||||
return $this->extract(FormatPackEnum::I, $offset, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an unsigned big endian short.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int $offset The offset that the value will be written.
|
||||
*/
|
||||
public function writeShort(int $value, int $offset): self
|
||||
{
|
||||
return $this->insert(FormatPackEnum::n, $value, $offset, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an unsigned big endian short.
|
||||
*
|
||||
* @param int $offset The offset to read from.
|
||||
*
|
||||
* @return int The data read.
|
||||
*/
|
||||
public function readShort(int $offset): int
|
||||
{
|
||||
return $this->extract(FormatPackEnum::n, $offset, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a unsigned integer with little endian.
|
||||
*
|
||||
* @param int $offset The offset that will be read.
|
||||
*
|
||||
* @return int The value that is at the specified offset.
|
||||
*/
|
||||
public function readUIntLE(int $offset): int
|
||||
{
|
||||
return $this->extract(FormatPackEnum::I, $offset, 3);
|
||||
}
|
||||
|
||||
public function readChar(int $offset): string
|
||||
{
|
||||
return $this->extract(FormatPackEnum::c, $offset, 1);
|
||||
}
|
||||
|
||||
public function readUChar(int $offset): string
|
||||
{
|
||||
return $this->extract(FormatPackEnum::C, $offset, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a char.
|
||||
*
|
||||
* @param string $value The value that will be written.
|
||||
* @param int $offset The offset that the value will be written.
|
||||
*/
|
||||
public function writeChar(string $value, int $offset): self
|
||||
{
|
||||
return $this->insert(FormatPackEnum::c, $value, $offset, FormatPackEnum::c->getLength());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes raw binary to the buffer.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int $offset The offset that the value will be written at.
|
||||
*/
|
||||
public function writeRaw(int $value, int $offset): void
|
||||
{
|
||||
$this->buffer[$offset] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a binary string to the buffer.
|
||||
*
|
||||
* @param string $value The value that will be written.
|
||||
* @param int $offset The offset that the value will be written at.
|
||||
*/
|
||||
public function writeRawString(string $value, int $offset): void
|
||||
{
|
||||
for ($i = 0; $i < strlen($value); ++$i) {
|
||||
$this->buffer[$offset++] = $value[$i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an attribute via key. Used for \ArrayAccess.
|
||||
*
|
||||
* @param mixed $key The attribute key.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($key)
|
||||
{
|
||||
return $this->buffer[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an attribute exists via key. Used for \ArrayAccess.
|
||||
*
|
||||
* @param mixed $key The attribute key.
|
||||
*
|
||||
* @return bool Whether the offset exists.
|
||||
*/
|
||||
public function offsetExists($key): bool
|
||||
{
|
||||
return isset($this->buffer[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an attribute via key. Used for \ArrayAccess.
|
||||
*
|
||||
* @param mixed $key The attribute key.
|
||||
* @param mixed $value The attribute value.
|
||||
*/
|
||||
public function offsetSet($key, $value): void
|
||||
{
|
||||
$this->buffer[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets an attribute via key. Used for \ArrayAccess.
|
||||
*
|
||||
* @param string $key The attribute key.
|
||||
*/
|
||||
public function offsetUnset($key): void
|
||||
{
|
||||
if (isset($this->buffer[$key])) {
|
||||
unset($this->buffer[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
178
vendor/discord-php-helpers/voice/src/Discord/Voice/ByteBuffer/FormatPackEnum.php
vendored
Normal file
178
vendor/discord-php-helpers/voice/src/Discord/Voice/ByteBuffer/FormatPackEnum.php
vendored
Normal file
@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\ByteBuffer;
|
||||
|
||||
/**
|
||||
* @link https://www.php.net/manual/en/function.pack.php
|
||||
*/
|
||||
enum FormatPackEnum: string
|
||||
{
|
||||
/**
|
||||
* NUL-padded string.
|
||||
*/
|
||||
case a = 'a';
|
||||
|
||||
/**
|
||||
* SPACE-padded string.
|
||||
*/
|
||||
case A = 'A';
|
||||
|
||||
/**
|
||||
* Hex string, low nibble first.
|
||||
*/
|
||||
case h = 'h';
|
||||
|
||||
/**
|
||||
* Hex string, high nibble first.
|
||||
*/
|
||||
case H = 'H';
|
||||
|
||||
/**
|
||||
* signed char.
|
||||
*/
|
||||
case c = 'c';
|
||||
|
||||
/**
|
||||
* unsigned char.
|
||||
*/
|
||||
case C = 'C';
|
||||
|
||||
/**
|
||||
* signed short (always 16 bit, machine byte order).
|
||||
*/
|
||||
case s = 's';
|
||||
|
||||
/**
|
||||
* unsigned short (always 16 bit, machine byte order).
|
||||
*/
|
||||
case S = 'S';
|
||||
|
||||
/**
|
||||
* unsigned short (always 16 bit, big endian byte order).
|
||||
*/
|
||||
case n = 'n';
|
||||
|
||||
/**
|
||||
* unsigned short (always 16 bit, little endian byte order).
|
||||
*/
|
||||
case v = 'v';
|
||||
|
||||
/**
|
||||
* signed integer (machine dependent size and byte order).
|
||||
*/
|
||||
case i = 'i';
|
||||
|
||||
/**
|
||||
* unsigned integer (machine dependent size and byte order).
|
||||
*/
|
||||
case I = 'I';
|
||||
|
||||
/**
|
||||
* signed long (always 32 bit, machine byte order).
|
||||
*/
|
||||
case l = 'l';
|
||||
|
||||
/**
|
||||
* unsigned long (always 32 bit, machine byte order).
|
||||
*/
|
||||
case L = 'L';
|
||||
|
||||
/**
|
||||
* unsigned long (always 32 bit, big endian byte order).
|
||||
*/
|
||||
case N = 'N';
|
||||
|
||||
/**
|
||||
* unsigned long (always 32 bit, little endian byte order).
|
||||
*/
|
||||
case V = 'V';
|
||||
|
||||
/**
|
||||
* signed long long (always 64 bit, machine byte order).
|
||||
*/
|
||||
case q = 'q';
|
||||
|
||||
/**
|
||||
* unsigned long long (always 64 bit, machine byte order).
|
||||
*/
|
||||
case Q = 'Q';
|
||||
|
||||
/**
|
||||
* unsigned long long (always 64 bit, big endian byte order).
|
||||
*/
|
||||
case J = 'J';
|
||||
|
||||
/**
|
||||
* unsigned long long (always 64 bit, little endian byte order).
|
||||
*/
|
||||
case P = 'P';
|
||||
|
||||
/**
|
||||
* float (machine dependent size and representation).
|
||||
*/
|
||||
case f = 'f';
|
||||
|
||||
/**
|
||||
* float (machine dependent size, little endian byte order).
|
||||
*/
|
||||
case g = 'g';
|
||||
|
||||
/**
|
||||
* float (machine dependent size, big endian byte order).
|
||||
*/
|
||||
case G = 'G';
|
||||
|
||||
/**
|
||||
* double (machine dependent size and representation).
|
||||
*/
|
||||
case d = 'd';
|
||||
|
||||
/**
|
||||
* double (machine dependent size, little endian byte order).
|
||||
*/
|
||||
case e = 'e';
|
||||
|
||||
/**
|
||||
* double (machine dependent size, big endian byte order).
|
||||
*/
|
||||
case E = 'E';
|
||||
|
||||
/**
|
||||
* NUL byte.
|
||||
*/
|
||||
case x = 'x';
|
||||
|
||||
/**
|
||||
* Back up one byte.
|
||||
*/
|
||||
case X = 'X';
|
||||
|
||||
/**
|
||||
* NUL-padded string.
|
||||
*/
|
||||
case Z = 'Z';
|
||||
|
||||
/**
|
||||
* NUL-fill to absolute position.
|
||||
*/
|
||||
case At = '@';
|
||||
|
||||
public function getLength(): int
|
||||
{
|
||||
return match ($this) {
|
||||
self::n, self::v => 2,
|
||||
self::N, self::V => 4,
|
||||
self::c, self::C => 1,
|
||||
default => throw new \InvalidArgumentException('Invalid format pack'),
|
||||
};
|
||||
}
|
||||
}
|
||||
32
vendor/discord-php-helpers/voice/src/Discord/Voice/ByteBuffer/ReadableBuffer.php
vendored
Normal file
32
vendor/discord-php-helpers/voice/src/Discord/Voice/ByteBuffer/ReadableBuffer.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author alexandre433
|
||||
*/
|
||||
interface ReadableBuffer
|
||||
{
|
||||
public function read(int $offset, int $length);
|
||||
|
||||
public function readInt8(int $offset);
|
||||
|
||||
public function readInt16BE(int $offset);
|
||||
|
||||
public function readInt16LE(int $offset);
|
||||
|
||||
public function readInt32BE(int $offset);
|
||||
|
||||
public function readInt32LE(int $offset);
|
||||
}
|
||||
67
vendor/discord-php-helpers/voice/src/Discord/Voice/ByteBuffer/WriteableBuffer.php
vendored
Normal file
67
vendor/discord-php-helpers/voice/src/Discord/Voice/ByteBuffer/WriteableBuffer.php
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author alexandre433
|
||||
*/
|
||||
interface WriteableBuffer
|
||||
{
|
||||
public function write($value, ?int $offset = null): self;
|
||||
|
||||
/**
|
||||
* Write an int8 to the buffer.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param int|null $offset The offset to write the int8, if not provided the length of the buffer will be used
|
||||
* @return self
|
||||
*/
|
||||
public function writeInt8($value, ?int $offset = null): self;
|
||||
|
||||
/**
|
||||
* Write an int16 to the buffer in big-endian format.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param int|null $offset The offset to write the int8, if not provided the length of the buffer will be used
|
||||
* @return self
|
||||
*/
|
||||
public function writeInt16BE($value, ?int $offset = null): self;
|
||||
|
||||
/**
|
||||
* Write an int16 to the buffer in little-endian format.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param int|null $offset The offset to write the int8, if not provided the length of the buffer will be used
|
||||
* @return self
|
||||
*/
|
||||
public function writeInt16LE($value, ?int $offset = null): self;
|
||||
|
||||
/**
|
||||
* Write an int32 to the buffer in big-endian format.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param int|null $offset The offset to write the int8, if not provided the length of the buffer will be used
|
||||
* @return self
|
||||
*/
|
||||
public function writeInt32BE($value, ?int $offset = null): self;
|
||||
|
||||
/**
|
||||
* Write an int32 to the buffer in little-endian format.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param int|null $offset The offset to write the int8, if not provided the length of the buffer will be used
|
||||
* @return self
|
||||
*/
|
||||
public function writeInt32LE($value, ?int $offset = null): self;
|
||||
}
|
||||
23
vendor/discord-php-helpers/voice/src/Discord/Voice/Client.php
vendored
Normal file
23
vendor/discord-php-helpers/voice/src/Discord/Voice/Client.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice;
|
||||
|
||||
/**
|
||||
* The Discord voice client.
|
||||
*
|
||||
* @since 10.19.0
|
||||
*/
|
||||
class Client extends VoiceClient
|
||||
{
|
||||
}
|
||||
40
vendor/discord-php-helpers/voice/src/Discord/Voice/Client/HeaderValuesEnum.php
vendored
Normal file
40
vendor/discord-php-helpers/voice/src/Discord/Voice/Client/HeaderValuesEnum.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Client;
|
||||
|
||||
/**
|
||||
* Enum for header values used in Discord voice client.
|
||||
*
|
||||
* @since 10.19.0
|
||||
*/
|
||||
enum HeaderValuesEnum: int
|
||||
{
|
||||
case RTP_HEADER_OR_NONCE_LENGTH = 12;
|
||||
|
||||
case RTP_VERSION_PAD_EXTEND_INDEX = 0;
|
||||
|
||||
case RTP_VERSION_PAD_EXTEND = 0x80;
|
||||
|
||||
case RTP_PAYLOAD_INDEX = 1;
|
||||
|
||||
case RTP_PAYLOAD_TYPE = 0x78;
|
||||
|
||||
case SEQ_INDEX = 2;
|
||||
|
||||
case TIMESTAMP_OR_NONCE_INDEX = 4;
|
||||
|
||||
case SSRC_INDEX = 8;
|
||||
|
||||
case AUTH_TAG_LENGTH = 16;
|
||||
}
|
||||
404
vendor/discord-php-helpers/voice/src/Discord/Voice/Client/Packet.php
vendored
Normal file
404
vendor/discord-php-helpers/voice/src/Discord/Voice/Client/Packet.php
vendored
Normal file
@ -0,0 +1,404 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Client;
|
||||
|
||||
use Discord\Voice\ByteBuffer\Buffer;
|
||||
use Discord\Voice\ByteBuffer\FormatPackEnum;
|
||||
use Discord\Voice\Exceptions\Libraries\LibSodiumNotFoundException;
|
||||
|
||||
/**
|
||||
* A voice packet received from Discord.
|
||||
*
|
||||
* Huge thanks to Austin and Michael from JDA for the constants and audio
|
||||
* packets. Check out their repo:
|
||||
* https://github.com/DV8FromTheWorld/JDA
|
||||
*
|
||||
* @since 10.19.0
|
||||
*/
|
||||
final class Packet
|
||||
{
|
||||
/**
|
||||
* The audio header, in binary, containing the version, flags, sequence, timestamp, and SSRC.
|
||||
*/
|
||||
protected string $header;
|
||||
|
||||
/**
|
||||
* The buffer containing the voice packet.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
protected Buffer $buffer;
|
||||
|
||||
/**
|
||||
* The version and flags.
|
||||
*/
|
||||
public ?string $versionPlusFlags;
|
||||
|
||||
/**
|
||||
* The payload type.
|
||||
*/
|
||||
public ?string $payloadType;
|
||||
|
||||
/**
|
||||
* The encrypted audio.
|
||||
*/
|
||||
public ?string $encryptedAudio;
|
||||
|
||||
/**
|
||||
* The decrypted audio.
|
||||
*/
|
||||
public null|false|string $decryptedAudio;
|
||||
|
||||
/**
|
||||
* The secret key.
|
||||
*/
|
||||
public ?string $secretKey;
|
||||
|
||||
/**
|
||||
* The raw data.
|
||||
*/
|
||||
protected string $rawData;
|
||||
|
||||
/**
|
||||
* Current packet header size. May differ depending on the RTP header.
|
||||
*/
|
||||
protected int $headerSize;
|
||||
|
||||
/**
|
||||
* Constructs the voice packet.
|
||||
*
|
||||
* @param string $data The Opus data to encode.
|
||||
* @param int $ssrc The client SSRC value.
|
||||
* @param int $seq The packet sequence.
|
||||
* @param int $timestamp The packet timestamp.
|
||||
* @param bool $encryption Whether the packet should be encrypted.
|
||||
* @param string|null $key The encryption key.
|
||||
*/
|
||||
public function __construct(
|
||||
?string $data = null,
|
||||
public ?int $ssrc = null,
|
||||
public ?int $seq = null,
|
||||
public ?int $timestamp = null,
|
||||
bool $decrypt = true,
|
||||
protected ?string $key = null
|
||||
) {
|
||||
if (! function_exists('sodium_crypto_secretbox')) {
|
||||
throw new LibSodiumNotFoundException('libsodium-php could not be found.');
|
||||
}
|
||||
|
||||
if ($decrypt) {
|
||||
$this->unpack($data);
|
||||
$this->decrypt();
|
||||
} else {
|
||||
$this->decryptedAudio = $data;
|
||||
$this->header = $this->buildHeader()->__toString();
|
||||
$this->encrypt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks the voice message into an array.
|
||||
*
|
||||
* C1 (unsigned char) | Version + Flags | 1 bytes | Single byte value of 0x80
|
||||
* C1 (unsigned char) | Payload Type | 1 bytes | Single byte value of 0x78
|
||||
* n (Unsigned short (big endian)) | Sequence | 2 bytes
|
||||
* I (Unsigned integer (big endian)) | Timestamp | 4 bytes
|
||||
* I (Unsigned integer (big endian)) | SSRC | 4 bytes
|
||||
* a* (string) | Encrypted audio | n bytes | Binary data of the encrypted audio.
|
||||
*
|
||||
* @see https://discord.com/developers/docs/topics/voice-connections#transport-encryption-modes-voice-packet-structure
|
||||
* @see https://www.php.net/manual/en/function.unpack.php
|
||||
* @see https://www.php.net/manual/en/function.pack.php For the formats
|
||||
*/
|
||||
public function unpack(string $message): self
|
||||
{
|
||||
$byteHeader = $this->setHeader($message);
|
||||
|
||||
if (! $byteHeader) {
|
||||
//$this->log->warning('Failed to unpack voice packet Header.', ['message' => $message]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
$byteData = substr(
|
||||
$message,
|
||||
HeaderValuesEnum::RTP_HEADER_OR_NONCE_LENGTH->value,
|
||||
strlen($message) - HeaderValuesEnum::AUTH_TAG_LENGTH->value - HeaderValuesEnum::RTP_HEADER_OR_NONCE_LENGTH->value
|
||||
);
|
||||
|
||||
$unpackedMessage = unpack('Cfirst/Csecond/nseq/Ntimestamp/Nssrc', $byteHeader);
|
||||
|
||||
if (! $unpackedMessage) {
|
||||
//$this->log->warning('Failed to unpack voice packet.', ['message' => $message]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->rawData = $message;
|
||||
$this->header = $byteHeader;
|
||||
$this->encryptedAudio = $byteData;
|
||||
|
||||
$this->ssrc = $unpackedMessage['ssrc'];
|
||||
$this->seq = $unpackedMessage['seq'];
|
||||
$this->timestamp = $unpackedMessage['timestamp'];
|
||||
$this->payloadType = $unpackedMessage['payload_type'] ?? null;
|
||||
$this->versionPlusFlags = $unpackedMessage['version_and_flags'] ?? null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the voice message.
|
||||
*/
|
||||
public function decrypt(?string $message = null): string|false|null
|
||||
{
|
||||
if (! $message) {
|
||||
$message = $this->rawData ?? null;
|
||||
}
|
||||
|
||||
if (empty($message)) {
|
||||
// throw error here
|
||||
return null;
|
||||
}
|
||||
|
||||
// total message length
|
||||
$len = strlen($message);
|
||||
|
||||
// 2. Extract the header
|
||||
$header = $this->getHeader();
|
||||
if (! $header) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. Extract the nonce
|
||||
$nonce = substr($message, $len - HeaderValuesEnum::TIMESTAMP_OR_NONCE_INDEX->value, HeaderValuesEnum::TIMESTAMP_OR_NONCE_INDEX->value);
|
||||
// 4. Pad the nonce to 12 bytes
|
||||
$nonceBuffer = str_pad($nonce, SODIUM_CRYPTO_AEAD_AES256GCM_NPUBBYTES, "\0", STR_PAD_RIGHT);
|
||||
|
||||
// 5. Extract the ciphertext and auth tag
|
||||
// The message: [header][ciphertext][auth tag][nonce]
|
||||
// The size of the ciphertext is: total - headerSize - 16 (auth tag) - 4 (nonce)
|
||||
$encryptedLength = $len - $this->headerSize - HeaderValuesEnum::AUTH_TAG_LENGTH->value - HeaderValuesEnum::TIMESTAMP_OR_NONCE_INDEX->value;
|
||||
$cipherText = substr($message, $this->headerSize, $encryptedLength);
|
||||
$authTag = substr($message, $this->headerSize + $encryptedLength, HeaderValuesEnum::AUTH_TAG_LENGTH->value);
|
||||
|
||||
// Concatenate the ciphertext and the auth tag
|
||||
$combined = "$cipherText$authTag";
|
||||
|
||||
$resultMessage = null;
|
||||
|
||||
try {
|
||||
// Decrypt the message
|
||||
$resultMessage = sodium_crypto_aead_aes256gcm_decrypt(
|
||||
$combined,
|
||||
$header,
|
||||
$nonceBuffer,
|
||||
$this->key
|
||||
);
|
||||
|
||||
// If decryption fails, log the error and return
|
||||
// Most of the time, the length is 20 bytes either for a ping, or an empty voice/udp packet
|
||||
if ($resultMessage === false && strlen($cipherText) !== 20) {
|
||||
//$this->log->warning('Failed to decode voice packet.', ['ssrc' => $this->ssrc]);
|
||||
return false;
|
||||
}
|
||||
// Check if the message contains an extension and remove it
|
||||
elseif (substr($message, 12, 2) === "\xBE\xDE") {
|
||||
// Reads the 2 bytes after the extension identifier to get the extension length
|
||||
$extLengthData = substr($message, 14, 2);
|
||||
$headerExtensionLength = unpack('n', $extLengthData)[1];
|
||||
|
||||
// Remove 4 * headerExtensionLength bytes from the beginning of the decrypted result
|
||||
$resultMessage = substr($resultMessage, 4 * $headerExtensionLength);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
//$this->log->error('Exception occurred when decoding voice packet: ' . $e->getMessage());
|
||||
//$this->log->error('Trace: ' . $e->getTraceAsString());
|
||||
return false;
|
||||
} finally {
|
||||
return $this->decryptedAudio = $resultMessage;
|
||||
}
|
||||
}
|
||||
|
||||
public function encrypt()
|
||||
{
|
||||
$header = $this->getHeader();
|
||||
|
||||
// pad nonce to 12 bytes for AES 256 GCM
|
||||
$nonce = pack('V', $this->seq - 1);
|
||||
$paddedNonce = str_pad($nonce, SODIUM_CRYPTO_AEAD_AES256GCM_NPUBBYTES, "\0", STR_PAD_RIGHT);
|
||||
|
||||
// encrypt the audio
|
||||
$this->encryptedAudio = sodium_crypto_aead_aes256gcm_encrypt($this->decryptedAudio, $header, $paddedNonce, $this->key);
|
||||
|
||||
// set the raw encrypted data with header prepended and nonce appended
|
||||
$this->rawData = $header.$this->encryptedAudio.$nonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initilizes the buffer with no encryption.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
protected function initBufferNoEncryption(string $data): void
|
||||
{
|
||||
$data = (string) $data;
|
||||
$header = $this->buildHeader();
|
||||
|
||||
$this->buffer = Buffer::make(strlen((string) $header) + strlen($data))
|
||||
->write((string) $header, 0)
|
||||
->write($data, 12);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initilizes the buffer with encryption.
|
||||
*/
|
||||
protected function initBufferEncryption(string $data, string $key): void
|
||||
{
|
||||
$data = (string) $data;
|
||||
$header = $this->buildHeader();
|
||||
$nonce = new Buffer(24);
|
||||
$nonce->write((string) $header, 0);
|
||||
|
||||
$data = \sodium_crypto_secretbox($data, (string) $nonce, $key);
|
||||
|
||||
$this->buffer = new Buffer(strlen((string) $header) + strlen($data));
|
||||
$this->buffer->write((string) $header, 0);
|
||||
$this->buffer->write($data, 12);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the header.
|
||||
*/
|
||||
protected function buildHeader(): Buffer
|
||||
{
|
||||
$header = new Buffer(HeaderValuesEnum::RTP_HEADER_OR_NONCE_LENGTH->value);
|
||||
$header[HeaderValuesEnum::RTP_VERSION_PAD_EXTEND_INDEX->value] = pack(FormatPackEnum::C->value, HeaderValuesEnum::RTP_VERSION_PAD_EXTEND->value);
|
||||
$header[HeaderValuesEnum::RTP_PAYLOAD_INDEX->value] = pack(FormatPackEnum::C->value, HeaderValuesEnum::RTP_PAYLOAD_TYPE->value);
|
||||
|
||||
return $header->writeShort($this->seq, HeaderValuesEnum::SEQ_INDEX->value)
|
||||
->writeUInt32BE($this->timestamp, HeaderValuesEnum::TIMESTAMP_OR_NONCE_INDEX->value)
|
||||
->writeUInt32BE($this->ssrc, HeaderValuesEnum::SSRC_INDEX->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the header.
|
||||
* If no message is provided, it will use the raw data of the packet.
|
||||
*/
|
||||
public function setHeader(?string $message = null): ?string
|
||||
{
|
||||
if (null === $message) {
|
||||
$message = $this->rawData ?? null;
|
||||
}
|
||||
|
||||
if (empty($message)) {
|
||||
// throw error here
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->headerSize = HeaderValuesEnum::RTP_HEADER_OR_NONCE_LENGTH->value;
|
||||
$firstByte = ord($message[0]);
|
||||
if (($firstByte >> 4) & 0x01) {
|
||||
$this->headerSize += 4;
|
||||
}
|
||||
|
||||
return substr($message, 0, $this->headerSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the header.
|
||||
*/
|
||||
public function getHeader(): ?string
|
||||
{
|
||||
return $this->header ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sequence.
|
||||
*/
|
||||
public function getSequence(): int
|
||||
{
|
||||
return $this->seq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timestamp.
|
||||
*/
|
||||
public function getTimestamp(): int
|
||||
{
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the SSRC.
|
||||
*/
|
||||
public function getSSRC(): int
|
||||
{
|
||||
return $this->ssrc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data.
|
||||
*/
|
||||
public function getData(): string
|
||||
{
|
||||
return $this->buffer->read(
|
||||
HeaderValuesEnum::RTP_HEADER_OR_NONCE_LENGTH->value,
|
||||
strlen((string) $this->buffer) - HeaderValuesEnum::RTP_HEADER_OR_NONCE_LENGTH->value
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a voice packet from data sent from Discord.
|
||||
*/
|
||||
public static function make(string $data): self
|
||||
{
|
||||
$n = new self('', 0, 0, 0);
|
||||
$buff = new Buffer($data);
|
||||
$n->setBuffer($buff);
|
||||
unset($buff);
|
||||
|
||||
return $n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the buffer.
|
||||
*/
|
||||
public function setBuffer(Buffer $buffer): self
|
||||
{
|
||||
$this->buffer = $buffer;
|
||||
|
||||
$this->seq = $this->buffer->readShort(HeaderValuesEnum::SEQ_INDEX->value);
|
||||
$this->timestamp = $this->buffer->readUInt(HeaderValuesEnum::TIMESTAMP_OR_NONCE_INDEX->value);
|
||||
$this->ssrc = $this->buffer->readUInt(HeaderValuesEnum::SSRC_INDEX->value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the decrypted audio data.
|
||||
* Will return null if the audio data is not decrypted and false on error.
|
||||
*/
|
||||
public function getAudioData(): string|false|null
|
||||
{
|
||||
return $this->decryptedAudio ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the encrypted audio data with header, ready for sending.
|
||||
* Will return null if the audio data is not encrypted.
|
||||
*/
|
||||
public function getEncryptedMessage(): string|null
|
||||
{
|
||||
return $this->rawData ?? null;
|
||||
}
|
||||
}
|
||||
313
vendor/discord-php-helpers/voice/src/Discord/Voice/Client/UDP.php
vendored
Normal file
313
vendor/discord-php-helpers/voice/src/Discord/Voice/Client/UDP.php
vendored
Normal file
@ -0,0 +1,313 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Client;
|
||||
|
||||
use Discord\Voice\ByteBuffer\Buffer;
|
||||
use Discord\WebSockets\Op;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use React\EventLoop\TimerInterface;
|
||||
use React\Datagram\Socket;
|
||||
use React\EventLoop\LoopInterface;
|
||||
|
||||
/**
|
||||
* Handles the UDP connection & events for Discord voice.
|
||||
* This class manages the UDP socket for sending and receiving audio data,
|
||||
* handling heartbeats, and managing the voice connection state.
|
||||
*
|
||||
* @since 10.19.0
|
||||
*/
|
||||
final class UDP extends Socket
|
||||
{
|
||||
/**
|
||||
* The Parent Voice WebSocket Client.
|
||||
*/
|
||||
public WS $ws;
|
||||
|
||||
/**
|
||||
* Silence Frame Remain Count.
|
||||
*/
|
||||
public int $silenceRemaining = 5;
|
||||
|
||||
/**
|
||||
* The Opus Silence Frame.
|
||||
*/
|
||||
public const string SILENCE_FRAME = "\0xF8\0xFF\0xFE";
|
||||
|
||||
/**
|
||||
* The stream time of the last packet.
|
||||
*/
|
||||
public int $streamTime = 0;
|
||||
|
||||
/**
|
||||
* Current heartbeat timer.
|
||||
*/
|
||||
public ?TimerInterface $heartbeat = null;
|
||||
|
||||
/**
|
||||
* Heartbeat interval in milliseconds.
|
||||
* The interval at which the heartbeat is sent.
|
||||
*/
|
||||
public ?int $hbInterval = null;
|
||||
|
||||
/**
|
||||
* Heartbeat sequence number.
|
||||
* This is used to keep track of the heartbeat messages sent.
|
||||
*/
|
||||
protected int $hbSequence = 0;
|
||||
|
||||
/**
|
||||
* The IP address of the UDP server.
|
||||
*/
|
||||
public string $ip;
|
||||
|
||||
/**
|
||||
* The port of the UDP server.
|
||||
*/
|
||||
public int $port;
|
||||
|
||||
/**
|
||||
* The SSRC identifier.
|
||||
* This is used to identify the source of the audio stream.
|
||||
*/
|
||||
public null|string|int $ssrc;
|
||||
|
||||
/**
|
||||
* @param \React\EventLoop\LoopInterface $loop
|
||||
* @param resource $socket
|
||||
* @param null|Buffer $buffer
|
||||
* @param null|WS $ws
|
||||
*/
|
||||
public function __construct($loop, $socket, $buffer = null, ?WS $ws = null)
|
||||
{
|
||||
parent::__construct($loop, $socket, $buffer);
|
||||
|
||||
if ($ws !== null) {
|
||||
$this->ws = $ws;
|
||||
|
||||
if (null === $this->hbInterval) {
|
||||
// Set the heartbeat interval to the default value if not set.
|
||||
$this->hbInterval = $this->ws->vc->heartbeatInterval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles incoming messages from the UDP server.
|
||||
* This is where we handle the audio data received from the server.
|
||||
*/
|
||||
public function handleMessages(string $secret): self
|
||||
{
|
||||
return $this->on('message', function (string $message) use ($secret) {
|
||||
if (strlen($message) <= 8) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->ws->vc->deaf) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->ws->vc->handleAudioData(new Packet($message, key: $secret));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the sending of the SSRC to the server.
|
||||
* This is necessary for the server to know which SSRC we are using.
|
||||
*/
|
||||
public function handleSsrcSending(): self
|
||||
{
|
||||
$buffer = new Buffer(74);
|
||||
$buffer[1] = "\x01";
|
||||
$buffer[3] = "\x46";
|
||||
$buffer->writeUInt32BE($this->ws->vc->ssrc, 4);
|
||||
$this->getLoop()->addTimer(0.1, fn () => $this->send($buffer->__toString()));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the heartbeat for the UDP client.
|
||||
* To keep the connection open and responsive.
|
||||
*/
|
||||
public function handleHeartbeat(): self
|
||||
{
|
||||
if (empty($this->hbInterval)) {
|
||||
$this->hbInterval = $this->ws->vc->heartbeatInterval;
|
||||
}
|
||||
|
||||
if (null === $this->getLoop()) {
|
||||
$this->getLogger()->error('No event loop found. Cannot handle heartbeat.');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->heartbeat = $this->getLoop()->addPeriodicTimer(
|
||||
$this->hbInterval / 1000,
|
||||
function (): void {
|
||||
$buffer = new Buffer(9);
|
||||
$buffer[0] = 0xC9;
|
||||
$buffer->writeUInt64LE($this->hbSequence, 1);
|
||||
++$this->hbSequence;
|
||||
|
||||
$this->send($buffer->__toString());
|
||||
$this->ws->vc->emit('udp-heartbeat', []);
|
||||
|
||||
$this->getLogger()->debug('sent UDP heartbeat');
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the first UDP message received from the server.
|
||||
* To discover which IP and port we should connect to.
|
||||
*
|
||||
* @see https://discord.com/developers/docs/topics/voice-connections#ip-discovery
|
||||
*/
|
||||
public function decodeOnce(): self
|
||||
{
|
||||
return $this->once('message', function (string $message) {
|
||||
/**
|
||||
* Unpacks the message into an array.
|
||||
*
|
||||
* C2 (unsigned char) | Type | 2 bytes | Values 0x1 and 0x2 indicate request and response, respectively
|
||||
* n (unsigned short) | Length | 2 bytes | Length of the following data
|
||||
* I (unsigned int) | SSRC | 4 bytes | The SSRC of the sender
|
||||
* A64 (string) | Address | 64 bytes | The IP address of the sender
|
||||
* n (unsigned short) | Port | 2 bytes | The port of the sender
|
||||
*
|
||||
* @see https://discord.com/developers/docs/topics/voice-connections#ip-discovery
|
||||
* @see https://www.php.net/manual/en/function.unpack.php
|
||||
* @see https://www.php.net/manual/en/function.pack.php For the formats
|
||||
*/
|
||||
$unpackedMessageArray = \unpack('C2Type/nLength/NSSRC/A64Address/nPort', $message);
|
||||
|
||||
$this->ws->vc->ssrc = $unpackedMessageArray['SSRC'];
|
||||
$ip = $unpackedMessageArray['Address'];
|
||||
$port = $unpackedMessageArray['Port'];
|
||||
|
||||
$this->getLogger()->debug('received our IP and port', ['ip' => $ip, 'port' => $port]);
|
||||
|
||||
$this->ws->send([
|
||||
'op' => Op::VOICE_SELECT_PROTOCOL,
|
||||
'd' => [
|
||||
'protocol' => 'udp',
|
||||
'data' => [
|
||||
'address' => $ip,
|
||||
'port' => $port,
|
||||
'mode' => $this->ws->mode,
|
||||
],
|
||||
],
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles errors that occur during UDP communication.
|
||||
*/
|
||||
public function handleErrors(): self
|
||||
{
|
||||
return $this->on('error', function (\Throwable $e): void {
|
||||
$this->getLogger()->error('UDP error', ['e' => $e->getMessage()]);
|
||||
$this->ws->vc->emit('udp-error', [$e]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert 5 frames of silence.
|
||||
*
|
||||
* @link https://discord.com/developers/docs/topics/voice-connections#voice-data-interpolation
|
||||
*/
|
||||
public function insertSilence(): void
|
||||
{
|
||||
while (--$this->silenceRemaining > 0) {
|
||||
$this->sendBuffer(self::SILENCE_FRAME);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a buffer to the UDP socket.
|
||||
*/
|
||||
public function sendBuffer(string $data): void
|
||||
{
|
||||
if (! $this->ws->vc->ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
$packet = new Packet(
|
||||
$data,
|
||||
$this->ws->vc->ssrc,
|
||||
$this->ws->vc->seq,
|
||||
$this->ws->vc->timestamp,
|
||||
false,
|
||||
$this->ws->secretKey,
|
||||
);
|
||||
$this->send($packet->getEncryptedMessage());
|
||||
|
||||
$this->streamTime = (int) microtime(true);
|
||||
|
||||
$this->ws->vc->emit('packet-sent', [$packet]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the UDP client and cancels the heartbeat timer.
|
||||
*/
|
||||
public function close(): void
|
||||
{
|
||||
if ($this->heartbeat) {
|
||||
$this->getLoop()->cancelTimer($this->heartbeat);
|
||||
$this->heartbeat = null;
|
||||
}
|
||||
|
||||
parent::close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the socket is already closed.
|
||||
*/
|
||||
public function isClosed(): bool
|
||||
{
|
||||
return ! $this->socket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the silence frames.
|
||||
*/
|
||||
public function refreshSilenceFrames(): void
|
||||
{
|
||||
if (! $this->ws->vc->paused) {
|
||||
// If the voice client is paused, we don't need to refresh the silence frames.
|
||||
return;
|
||||
}
|
||||
|
||||
$this->silenceRemaining = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the logger instance.
|
||||
*/
|
||||
private function getLogger(): LoggerInterface
|
||||
{
|
||||
return $this->ws->vc->discord->getLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the event loop instance.
|
||||
*/
|
||||
private function getLoop(): LoopInterface
|
||||
{
|
||||
return $this->ws->vc->discord->getLoop();
|
||||
}
|
||||
}
|
||||
36
vendor/discord-php-helpers/voice/src/Discord/Voice/Client/User.php
vendored
Normal file
36
vendor/discord-php-helpers/voice/src/Discord/Voice/Client/User.php
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Client;
|
||||
|
||||
use Discord\Discord;
|
||||
use Discord\Voice\ReceiveStream;
|
||||
use Discord\Voice\Speaking;
|
||||
use Discord\Voice\VoiceClient;
|
||||
use React\ChildProcess\Process;
|
||||
|
||||
/**
|
||||
* @since 10.19.0
|
||||
*/
|
||||
final class User
|
||||
{
|
||||
public function __construct(
|
||||
protected Discord $discord,
|
||||
protected VoiceClient $voiceClient,
|
||||
protected int $ssrc,
|
||||
protected Process $decoder,
|
||||
protected ReceiveStream $stream,
|
||||
protected ?Speaking $part = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
657
vendor/discord-php-helpers/voice/src/Discord/Voice/Client/WS.php
vendored
Normal file
657
vendor/discord-php-helpers/voice/src/Discord/Voice/Client/WS.php
vendored
Normal file
@ -0,0 +1,657 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Client;
|
||||
|
||||
use Discord\Discord;
|
||||
use Discord\Factory\SocketFactory;
|
||||
use Discord\Parts\Voice\UserConnected;
|
||||
use Discord\Voice\Any;
|
||||
use Discord\Voice\Client;
|
||||
use Discord\Voice\Flags;
|
||||
use Discord\Voice\Hello;
|
||||
use Discord\Voice\Platform;
|
||||
use Discord\Voice\Ready;
|
||||
use Discord\Voice\Resumed;
|
||||
use Discord\Voice\SessionDescription;
|
||||
use Discord\Voice\Speaking;
|
||||
use Discord\WebSockets\Op;
|
||||
use Discord\WebSockets\Payload;
|
||||
use Discord\WebSockets\VoicePayload;
|
||||
use Ratchet\Client\Connector;
|
||||
use Ratchet\Client\WebSocket;
|
||||
use Ratchet\RFC6455\Messaging\Message;
|
||||
use React\EventLoop\TimerInterface;
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
/**
|
||||
* Handles the Discord voice WebSocket connection.
|
||||
*
|
||||
* This class manages the WebSocket connection to the Discord voice gateway,
|
||||
* handling events, sending messages, and managing the voice connection state.
|
||||
*
|
||||
* @since 10.19.0
|
||||
*/
|
||||
final class WS
|
||||
{
|
||||
/**
|
||||
* The maximum DAVE protocol version supported.
|
||||
*/
|
||||
public const MAX_DAVE_PROTOCOL_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Dispatch table mapping Discord Voice Gateway opcodes to handler methods.
|
||||
*
|
||||
* @var array<int,string> Method name indexed by opcode constant.
|
||||
*/
|
||||
public const VOICE_OP_HANDLERS = [
|
||||
Op::VOICE_READY => 'handleReady',
|
||||
Op::VOICE_SESSION_DESCRIPTION => 'handleSessionDescription',
|
||||
Op::VOICE_SPEAKING => 'handleSpeaking',
|
||||
Op::VOICE_HEARTBEAT_ACK => 'handleHeartbeatAck',
|
||||
Op::VOICE_HELLO => 'handleHello',
|
||||
Op::VOICE_RESUMED => 'handleResumed',
|
||||
Op::VOICE_CLIENT_CONNECT => 'handleClientConnect',
|
||||
Op::VOICE_CLIENT_DISCONNECT => 'handleClientDisconnect',
|
||||
Op::VOICE_CLIENT_UNKNOWN_15 => 'handleAny',
|
||||
Op::VOICE_CLIENT_UNKNOWN_18 => 'handleFlags',
|
||||
Op::VOICE_CLIENT_PLATFORM => 'handlePlatform',
|
||||
Op::VOICE_DAVE_PREPARE_TRANSITION => 'handleDavePrepareTransition',
|
||||
Op::VOICE_DAVE_EXECUTE_TRANSITION => 'handleDaveExecuteTransition',
|
||||
Op::VOICE_DAVE_TRANSITION_READY => 'handleDaveTransitionReady',
|
||||
Op::VOICE_DAVE_PREPARE_EPOCH => 'handleDavePrepareEpoch',
|
||||
Op::VOICE_DAVE_MLS_EXTERNAL_SENDER => 'handleDaveMlsExternalSender',
|
||||
Op::VOICE_DAVE_MLS_KEY_PACKAGE => 'handleDaveMlsKeyPackage',
|
||||
Op::VOICE_DAVE_MLS_PROPOSALS => 'handleDaveMlsProposals',
|
||||
Op::VOICE_DAVE_MLS_COMMIT_WELCOME => 'handleDaveMlsCommitWelcome',
|
||||
Op::VOICE_DAVE_MLS_ANNOUNCE_COMMIT_TRANSITION => 'handleDaveMlsAnnounceCommitTransition',
|
||||
Op::VOICE_DAVE_MLS_WELCOME => 'handleDaveMlsWelcome',
|
||||
Op::VOICE_DAVE_MLS_INVALID_COMMIT_WELCOME => 'handleDaveMlsInvalidCommitWelcome',
|
||||
|
||||
Op::CLOSE_VOICE_DISCONNECTED => 'handleCloseVoiceDisconnected',
|
||||
];
|
||||
|
||||
/**
|
||||
* The SocketFactory instance for creating UDP sockets.
|
||||
*/
|
||||
protected SocketFactory $udpfac;
|
||||
|
||||
/**
|
||||
* The WebSocket instance for the voice connection.
|
||||
*/
|
||||
protected WebSocket $socket;
|
||||
|
||||
/**
|
||||
* The Discord voice gateway version.
|
||||
*
|
||||
* @see https://discord.com/developers/docs/topics/voice-connections#voice-gateway-versioning-gateway-versions
|
||||
*/
|
||||
protected static $version = 8;
|
||||
|
||||
/**
|
||||
* The Voice WebSocket mode.
|
||||
*
|
||||
* @link https://discord.com/developers/docs/topics/voice-connections#transport-encryption-modes
|
||||
*/
|
||||
public string $mode = 'aead_aes256_gcm_rtpsize';
|
||||
|
||||
/**
|
||||
* The secret key used for encrypting voice.
|
||||
*/
|
||||
public ?string $secretKey;
|
||||
|
||||
/**
|
||||
* The raw secret key.
|
||||
*/
|
||||
public ?array $rawKey;
|
||||
|
||||
/**
|
||||
* The SSRC identifier for the voice connection client.
|
||||
*/
|
||||
public null|string|int $ssrc;
|
||||
|
||||
/**
|
||||
* Indicates whether the login frame has been sent.
|
||||
*/
|
||||
private bool $sentLoginFrame = false;
|
||||
|
||||
/**
|
||||
* The heartbeat timer for the voice connection.
|
||||
*/
|
||||
protected TimerInterface $heartbeat;
|
||||
|
||||
/**
|
||||
* The heartbeat interval for the voice connection.
|
||||
*/
|
||||
protected $hbInterval;
|
||||
|
||||
/**
|
||||
* The heartbeat sequence number.
|
||||
*
|
||||
* This is used to track the sequence of heartbeat messages sent to the voice gateway.
|
||||
*/
|
||||
protected int $hbSequence = 0;
|
||||
|
||||
/**
|
||||
* The WebSocket connection for the voice client.
|
||||
*
|
||||
* This is used to send and receive messages over the WebSocket connection.
|
||||
*/
|
||||
public function __construct(
|
||||
public Client $vc,
|
||||
protected ?Discord $discord = null,
|
||||
public ?array $data = [],
|
||||
) {
|
||||
$this->data ??= $this->vc->data;
|
||||
$this->discord ??= $this->vc->discord;
|
||||
|
||||
if (! isset($this->data['endpoint'])) {
|
||||
throw new \InvalidArgumentException('Endpoint is required for the voice WebSocket connection.');
|
||||
}
|
||||
|
||||
$this->discord->logger->debug('Creating new voice websocket', ['endpoint' => $this->data['endpoint']]);
|
||||
|
||||
$f = new Connector();
|
||||
|
||||
/** @var PromiseInterface<WebSocket> */
|
||||
$f('wss://'.$this->data['endpoint'].'?v='.self::$version)->then(
|
||||
fn (WebSocket $ws) => $this->handleConnection($ws),
|
||||
fn (\Throwable $e) => $this->discord->logger->error(
|
||||
'Failed to connect to voice gateway: {error}',
|
||||
['error' => $e->getMessage()]
|
||||
) && $this->vc->emit('error', arguments: [$e])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of the WS class.
|
||||
*
|
||||
* @param Client $vc
|
||||
* @param null|Discord $discord
|
||||
* @param null|array $data
|
||||
*
|
||||
* @return WS
|
||||
*/
|
||||
public static function make(Client $vc, ?Discord $discord = null, ?array $data = null): self
|
||||
{
|
||||
return new self($vc, $discord, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a WebSocket connection.
|
||||
*/
|
||||
public function handleConnection(WebSocket $ws): void
|
||||
{
|
||||
$this->discord->logger->debug('connected to voice websocket');
|
||||
|
||||
$this->udpfac = new SocketFactory(ws: $this);
|
||||
|
||||
$this->socket = $this->vc->ws = $ws;
|
||||
|
||||
$ws->on('message', function (Message $message): void {
|
||||
if (($data = json_decode($message->getPayload(), true)) === false) {
|
||||
return;
|
||||
}
|
||||
$data = Payload::fromArray($data);
|
||||
|
||||
$this->vc->emit('ws-message', [$message, $this->vc]);
|
||||
|
||||
if (isset(self::VOICE_OP_HANDLERS[$data->op])) {
|
||||
$handler = self::VOICE_OP_HANDLERS[$data->op];
|
||||
$this->$handler($data);
|
||||
} else {
|
||||
$this->discord->getLogger()->debug('unknown voice op', ['op' => $data->op]);
|
||||
$this->handleUndocumented($data);
|
||||
}
|
||||
});
|
||||
|
||||
$ws->on('error', function ($e): void {
|
||||
$this->discord->logger->error('error with voice websocket', ['e' => $e->getMessage()]);
|
||||
$this->vc->emit('ws-error', [$e]);
|
||||
});
|
||||
|
||||
$ws->on('close', [$this, 'handleClose']);
|
||||
|
||||
if (! $this->sentLoginFrame) {
|
||||
$this->handleSendingOfLoginFrame();
|
||||
$this->sentLoginFrame = true;
|
||||
} elseif (isset(
|
||||
$this->data['token'],
|
||||
$this->data['seq'],
|
||||
$this->discord->voice_sessions[$this->vc->channel->guild_id]
|
||||
)) {
|
||||
$this->handleResume();
|
||||
} else {
|
||||
$this->discord->getLogger()->debug('existing voice session or data not found, re-sending identify', ['guild_id' => $this->vc->channel->guild_id]);
|
||||
$this->handleSendingOfLoginFrame();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the voice websocket.
|
||||
*/
|
||||
public function send(VoicePayload|array $data): void
|
||||
{
|
||||
$this->socket->send(json_encode($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the "ready" event for the voice client, initializing UDP connection and heartbeat.
|
||||
*
|
||||
* @param Payload $data The data object containing voice server connection details:
|
||||
* - $data->d['ssrc']: The synchronization source identifier.
|
||||
* - $data->d['ip']: The IP address for the UDP connection.
|
||||
* - $data->d['port']: The port for the UDP connection.
|
||||
* - $data->d['modes']: Supported encryption modes.
|
||||
*/
|
||||
protected function handleReady(Payload $data): void
|
||||
{
|
||||
/** @var Ready */
|
||||
$ready = $this->discord->factory(Ready::class, (array) $data->d, true);
|
||||
|
||||
$this->vc->ssrc = $ready->ssrc;
|
||||
$this->discord->logger->debug('received voice ready packet', ['data' => json_decode(json_encode($data->d), true)]);
|
||||
|
||||
/** @var PromiseInterface */
|
||||
$this->udpfac->createClient("{$ready->ip}:".$ready->port)->then(function (UDP $client) use ($ready): void {
|
||||
$this->vc->udp = $client;
|
||||
$client->handleSsrcSending()
|
||||
->handleHeartbeat()
|
||||
->handleErrors()
|
||||
->decodeOnce();
|
||||
|
||||
$client->ip = $ready->ip;
|
||||
$client->port = $ready->port;
|
||||
$client->ssrc = $ready->ssrc;
|
||||
}, function (\Throwable $e): void {
|
||||
$this->discord->logger->error('error while connecting to udp', ['e' => $e->getMessage()]);
|
||||
$this->vc->emit('error', [$e]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the session description packet received from the Discord voice server.
|
||||
*
|
||||
* @param Payload $data
|
||||
*/
|
||||
protected function handleSessionDescription(Payload $data): void
|
||||
{
|
||||
/** @var SessionDescription */
|
||||
$sd = $this->discord->factory(SessionDescription::class, (array) $data->d, true);
|
||||
|
||||
$this->vc->ready = true;
|
||||
$this->mode = $sd->mode === $this->mode ? $this->mode : 'aead_aes256_gcm_rtpsize';
|
||||
$this->rawKey = $data->d['secret_key'];
|
||||
$this->secretKey = $sd->secret_key;
|
||||
|
||||
$this->discord->logger->debug('received description packet, vc ready', ['data' => $sd->__debugInfo()]);
|
||||
|
||||
if (! $this->vc->reconnecting) {
|
||||
$this->vc->emit('ready', [$this->vc]);
|
||||
} else {
|
||||
$this->vc->reconnecting = false;
|
||||
$this->vc->emit('resumed', [$this->vc]);
|
||||
# TODO: check if this can fix the reconnect issue
|
||||
//$this->vc->emit('ready', [$this->vc]);
|
||||
}
|
||||
|
||||
if (! $this->vc->deaf && $this->secretKey) {
|
||||
$this->vc->udp->handleMessages($this->secretKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the speaking state of a user.
|
||||
*
|
||||
* @param Payload $data The data object received from the WebSocket.
|
||||
*/
|
||||
protected function handleSpeaking(Payload $data): void
|
||||
{
|
||||
/** @var Speaking */
|
||||
$speaking = $this->discord->factory(Speaking::class, (array) $data->d, true);
|
||||
|
||||
$this->discord->logger->debug('received speaking packet', ['data' => json_decode(json_encode($data->d), true)]);
|
||||
$this->vc->speakingStatus[$speaking->user_id] = $speaking;
|
||||
$this->vc->emit('speaking', [$speaking->speaking, $speaking->user_id, $this->vc]);
|
||||
$this->vc->emit("speaking.{$speaking->user_id}", [$speaking->speaking, $this->vc]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the heartbeat acknowledgement from the voice WebSocket connection.
|
||||
*
|
||||
* @param Payload $data
|
||||
*/
|
||||
public function handleHeartbeatAck(Payload $data): void
|
||||
{
|
||||
$diff = (microtime(true) - $data->d['t']) * 1000;
|
||||
|
||||
$this->discord->logger->debug('received heartbeat ack', ['response_time' => $diff]);
|
||||
$this->vc->emit('ws-ping', [$diff]);
|
||||
$this->vc->emit('ws-heartbeat-ack', [$data->d['t']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the "Hello" event from the Discord voice server.
|
||||
*
|
||||
* @param Payload $data
|
||||
*/
|
||||
protected function handleHello(Payload $data): void
|
||||
{
|
||||
/** @var Hello */
|
||||
$hello = $this->discord->factory(Hello::class, (array) $data->d, true);
|
||||
|
||||
$this->hbInterval = $this->vc->heartbeatInterval = $hello->heartbeat_interval;
|
||||
$this->sendHeartbeat();
|
||||
$this->heartbeat = $this->discord->loop->addPeriodicTimer(
|
||||
$this->hbInterval / 1000,
|
||||
fn () => $this->sendHeartbeat()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the 'resumed' event for the voice client.
|
||||
*
|
||||
* @param Payload $data
|
||||
*/
|
||||
protected function handleResumed(Payload $data): void
|
||||
{
|
||||
/** @var Resumed */
|
||||
$resumed = $this->discord->factory(Resumed::class, (array) $data->d, true);
|
||||
$this->discord->getLogger()->debug('received resumed packet', ['data' => $resumed]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the event when a client connects to the voice server.
|
||||
*
|
||||
* @param Payload $data
|
||||
*/
|
||||
protected function handleClientConnect(Payload $data): void
|
||||
{
|
||||
$this->discord->getLogger()->debug('received client connect packet', ['data' => $data]);
|
||||
// "d" contains an array with ['user_ids' => array<string>]
|
||||
$this->vc->users = array_map(fn (int $userId) => $this->discord->getFactory()->part(UserConnected::class, ['user_id' => $userId]), $data->d['user_ids']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the event when a client disconnects from the voice server.
|
||||
*
|
||||
* @param Payload $data
|
||||
*/
|
||||
protected function handleClientDisconnect(Payload $data): void
|
||||
{
|
||||
$this->discord->logger->debug('received client disconnected packet', ['data' => $data]);
|
||||
unset($this->vc->clientsConnected[$data->d['user_id']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the any event from the voice server.
|
||||
*
|
||||
* @param Payload $data
|
||||
*/
|
||||
public function handleAny(Payload $data): void
|
||||
{
|
||||
$any = $this->discord->factory(Any::class, (array) $data->d, true);
|
||||
|
||||
$this->discord->logger->debug('received any packet', ['data' => $any->__debugInfo()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the flags event from the voice server.
|
||||
*
|
||||
* @param Payload $data
|
||||
*/
|
||||
protected function handleFlags(Payload $data): void
|
||||
{
|
||||
$flags = $this->discord->factory(Flags::class, (array) $data->d, true);
|
||||
|
||||
$this->discord->logger->debug('received flags packet', ['data' => $flags->__debugInfo()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the platform event from the voice server.
|
||||
*
|
||||
* @param Payload $data
|
||||
*/
|
||||
protected function handlePlatform(Payload $data): void
|
||||
{
|
||||
$platform = $this->discord->factory(Platform::class, (array) $data->d, true);
|
||||
|
||||
$this->discord->logger->debug('received platform packet', ['data' => $platform->__debugInfo()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles undocumented voice opcodes not intended for use by bots.
|
||||
*
|
||||
* @param Payload $data
|
||||
*/
|
||||
protected function handleUndocumented(Payload $data): void
|
||||
{
|
||||
}
|
||||
|
||||
protected function handleDavePrepareTransition($data): void
|
||||
{
|
||||
$this->discord->logger->debug('DAVE Prepare Transition', ['data' => $data]);
|
||||
// Prepare local state necessary to perform the transition
|
||||
$this->send(VoicePayload::new(
|
||||
Op::VOICE_DAVE_TRANSITION_READY,
|
||||
['transition_id' => $data->d['transition_id']],
|
||||
));
|
||||
}
|
||||
|
||||
protected function handleDaveExecuteTransition($data): void
|
||||
{
|
||||
$this->discord->logger->debug('DAVE Execute Transition', ['data' => $data]);
|
||||
// Execute the transition
|
||||
// Update local state to reflect the new protocol context
|
||||
}
|
||||
|
||||
protected function handleDaveTransitionReady($data): void
|
||||
{
|
||||
$this->discord->logger->debug('DAVE Transition Ready', ['data' => $data]);
|
||||
// Handle transition ready state
|
||||
}
|
||||
|
||||
protected function handleDavePrepareEpoch($data): void
|
||||
{
|
||||
$this->discord->logger->debug('DAVE Prepare Epoch', ['data' => $data]);
|
||||
// Prepare local MLS group with parameters appropriate for the DAVE protocol version
|
||||
$this->send(VoicePayload::new(
|
||||
Op::VOICE_DAVE_MLS_KEY_PACKAGE,
|
||||
[
|
||||
'epoch_id' => $data->d['epoch_id'],
|
||||
//'key_package' => $this->generateKeyPackage(),
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
protected function handleDaveMlsExternalSender($data): void
|
||||
{
|
||||
$this->discord->logger->debug('DAVE MLS External Sender', ['data' => $data]);
|
||||
// Handle external sender public key and credential
|
||||
}
|
||||
|
||||
protected function handleDaveMlsKeyPackage($data): void
|
||||
{
|
||||
$this->discord->logger->debug('DAVE MLS Key Package', ['data' => $data]);
|
||||
// Handle MLS key package
|
||||
}
|
||||
|
||||
protected function handleDaveMlsProposals($data): void
|
||||
{
|
||||
$this->discord->logger->debug('DAVE MLS Proposals', ['data' => $data]);
|
||||
// Handle MLS proposals
|
||||
$this->send(VoicePayload::new(
|
||||
Op::VOICE_DAVE_MLS_COMMIT_WELCOME,
|
||||
[
|
||||
//'commit' => $this->generateCommit(),
|
||||
//'welcome' => $this->generateWelcome(),
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
protected function handleDaveMlsCommitWelcome($data): void
|
||||
{
|
||||
$this->discord->logger->debug('DAVE MLS Commit Welcome', ['data' => $data]);
|
||||
// Handle MLS commit and welcome messages
|
||||
}
|
||||
|
||||
protected function handleDaveMlsAnnounceCommitTransition($data)
|
||||
{
|
||||
// Handle MLS announce commit transition
|
||||
$this->discord->logger->debug('DAVE MLS Announce Commit Transition', ['data' => $data]);
|
||||
}
|
||||
|
||||
protected function handleDaveMlsWelcome($data)
|
||||
{
|
||||
// Handle MLS welcome message
|
||||
$this->discord->logger->debug('DAVE MLS Welcome', ['data' => $data]);
|
||||
}
|
||||
|
||||
protected function handleDaveMlsInvalidCommitWelcome($data)
|
||||
{
|
||||
$this->discord->logger->debug('DAVE MLS Invalid Commit Welcome', ['data' => $data]);
|
||||
// Handle invalid commit or welcome message
|
||||
// Reset local group state and generate a new key package
|
||||
$this->send(VoicePayload::new(
|
||||
Op::VOICE_DAVE_MLS_KEY_PACKAGE,
|
||||
[
|
||||
//'key_package' => $this->generateKeyPackage(),
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a heartbeat to the voice WebSocket.
|
||||
*/
|
||||
public function sendHeartbeat(): void
|
||||
{
|
||||
$this->send(VoicePayload::new(
|
||||
Op::VOICE_HEARTBEAT,
|
||||
[
|
||||
't' => (int) microtime(true),
|
||||
'seq_ack' => ++$this->hbSequence,
|
||||
]
|
||||
));
|
||||
$this->discord->logger->debug('sending heartbeat');
|
||||
$this->vc->emit('ws-heartbeat', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the close event of the WebSocket connection.
|
||||
*
|
||||
* @param int $op The opcode of the close event.
|
||||
* @param string $reason The reason for closing the connection.
|
||||
*/
|
||||
public function handleClose(int $op, string $reason): void
|
||||
{
|
||||
$this->discord->logger->warning('voice websocket closed', ['op' => $op, 'reason' => $reason]);
|
||||
$this->vc->emit('ws-close', [$op, $reason, $this]);
|
||||
|
||||
$this->vc->clientsConnected = [];
|
||||
|
||||
// Cancel heartbeat timers
|
||||
if (null !== $this->vc->heartbeat) {
|
||||
$this->discord->loop->cancelTimer($this->vc->heartbeat);
|
||||
$this->vc->heartbeat = null;
|
||||
}
|
||||
|
||||
// Close UDP socket.
|
||||
if (isset($this->vc->udp)) {
|
||||
$this->discord->logger->warning('closing UDP client');
|
||||
$this->vc->udp->close();
|
||||
}
|
||||
|
||||
$this->socket->close();
|
||||
$this->discord->voice_sessions[$this->vc->channel->guild_id] = null;
|
||||
|
||||
// Don't reconnect on a critical opcode or if closed by user.
|
||||
if (in_array($op, Op::getCriticalVoiceCloseCodes()) || $this?->vc->userClose) {
|
||||
$this->discord->logger->warning('received critical opcode - not reconnecting', ['op' => $op, 'reason' => $reason]);
|
||||
if ($op === Op::CLOSE_INVALID_SESSION) {
|
||||
$this->discord->logger->debug('sessions', ['voice_sessions' => $this->discord->voice_sessions]);
|
||||
}
|
||||
$this->vc->voice_sessions[$this->vc->channel->guild_id] = null;
|
||||
// prevent race conditions
|
||||
if ($this->vc->ready) {
|
||||
$this->vc->close();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->discord->logger->warning('reconnecting in 2 seconds');
|
||||
|
||||
// Retry connect after 2 seconds
|
||||
$this->discord->loop->addTimer(2, function (): void {
|
||||
$this->vc->reconnecting = true;
|
||||
$this->vc->sentLoginFrame = false;
|
||||
$this->sentLoginFrame = false;
|
||||
|
||||
$this->vc->boot();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles sending the login frame to the voice WebSocket.
|
||||
*
|
||||
* This method sends the initial identification payload to the voice gateway
|
||||
* to establish the voice connection.
|
||||
*
|
||||
* @link https://discord.com/developers/docs/topics/voice-connections#establishing-a-voice-websocket-connection-example-voice-identify-payload
|
||||
*/
|
||||
public function handleSendingOfLoginFrame(): void
|
||||
{
|
||||
if ($this->sentLoginFrame) {
|
||||
return;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'server_id' => $this->vc->channel->guild_id,
|
||||
'user_id' => $this->data['user_id'],
|
||||
'token' => $this->data['token'],
|
||||
'max_dave_protocol_version' => self::MAX_DAVE_PROTOCOL_VERSION,
|
||||
];
|
||||
if (isset($this->discord->voice_sessions[$this->vc->channel->guild_id])) {
|
||||
$this->data['session'] = $this->discord->voice_sessions[$this->vc->channel->guild_id];
|
||||
$data['session_id'] = $this->data['session'];
|
||||
}
|
||||
|
||||
$payload = VoicePayload::new(Op::VOICE_IDENTIFY, $data);
|
||||
|
||||
$this->discord->logger->debug('sending identify', ['packet' => $payload->__debugInfo()]);
|
||||
|
||||
$this->send($payload);
|
||||
$this->sentLoginFrame = true;
|
||||
$this->vc->sentLoginFrame = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes a previously established voice connection.
|
||||
*/
|
||||
protected function handleResume(): void
|
||||
{
|
||||
$payload = Payload::new(
|
||||
Op::VOICE_RESUME,
|
||||
[
|
||||
'server_id' => $this->vc->channel->guild_id,
|
||||
'session_id' => $this->discord->voice_sessions[$this->vc->channel->guild_id],
|
||||
'token' => $this->data['token'],
|
||||
'seq_ack' => $this->data['seq'],
|
||||
]
|
||||
);
|
||||
|
||||
$this->discord->logger->debug('sending identify (resume)', ['packet' => $payload->__debugInfo()]);
|
||||
|
||||
$this->send($payload);
|
||||
}
|
||||
}
|
||||
32
vendor/discord-php-helpers/voice/src/Discord/Voice/Exceptions/BufferTimedOutException.php
vendored
Normal file
32
vendor/discord-php-helpers/voice/src/Discord/Voice/Exceptions/BufferTimedOutException.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Exceptions;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Thrown when reading from a buffer times out.
|
||||
*
|
||||
* @since 10.0.0
|
||||
*/
|
||||
class BufferTimedOutException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* Create a new buffer timeout exception.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('Reading from the buffer timed out.');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Exceptions\Channels;
|
||||
|
||||
/**
|
||||
* Thrown when the Voice Client is already playing audio.
|
||||
*
|
||||
* @since 10.0.0
|
||||
*/
|
||||
final class AudioAlreadyPlayingException extends \RuntimeException
|
||||
{
|
||||
public function __construct(?string $message = null)
|
||||
{
|
||||
parent::__construct($message ?? 'Audio is already playing.');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Exceptions\Channels;
|
||||
|
||||
/**
|
||||
* Thrown when the selected Channel does not allow voice.
|
||||
*
|
||||
* @since 10.0.0
|
||||
*/
|
||||
final class CantJoinMoreThanOneChannelException extends \RuntimeException
|
||||
{
|
||||
public function __construct(?string $message = null)
|
||||
{
|
||||
parent::__construct($message ?? 'You cannot join more than one voice channel per guild/server.');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Exceptions\Channels;
|
||||
|
||||
/**
|
||||
* Thrown when the selected Channel does not allow voice.
|
||||
*
|
||||
* @since 10.0.0
|
||||
*/
|
||||
final class CantSpeakInChannelException extends \RuntimeException
|
||||
{
|
||||
public function __construct(?string $message = null)
|
||||
{
|
||||
parent::__construct($message ?? 'The current Channel doesn\'t have proper permissions for the Bot to speak in it.');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Exceptions\Channels;
|
||||
|
||||
/**
|
||||
* Thrown when the selected Channel does not allow voice.
|
||||
*
|
||||
* @since 10.0.0
|
||||
*/
|
||||
final class ChannelMustAllowVoiceException extends \RuntimeException
|
||||
{
|
||||
public function __construct(?string $message = null)
|
||||
{
|
||||
parent::__construct($message ?? 'Current Channel must allow voice.');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Exceptions\Channels;
|
||||
|
||||
/**
|
||||
* Thrown when the selected Channel does not allow voice.
|
||||
*
|
||||
* @since 10.0.0
|
||||
*/
|
||||
final class EnterChannelDeniedException extends \RuntimeException
|
||||
{
|
||||
public function __construct(?string $message = null)
|
||||
{
|
||||
parent::__construct($message ?? 'The current Channel doesn\'t have proper permissions for the Bot to connect to it.');
|
||||
}
|
||||
}
|
||||
25
vendor/discord-php-helpers/voice/src/Discord/Voice/Exceptions/ClientNotReadyException.php
vendored
Normal file
25
vendor/discord-php-helpers/voice/src/Discord/Voice/Exceptions/ClientNotReadyException.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Exceptions;
|
||||
|
||||
/**
|
||||
* Thrown when the Voice Client is not ready.
|
||||
*
|
||||
* @since 10.0.0
|
||||
*/
|
||||
final class ClientNotReadyException extends \RuntimeException
|
||||
{
|
||||
public function __construct(?string $message = null)
|
||||
{
|
||||
parent::__construct($message ?? 'Voice Client is not ready.');
|
||||
}
|
||||
}
|
||||
23
vendor/discord-php-helpers/voice/src/Discord/Voice/Exceptions/Libraries/DCANotFoundException.php
vendored
Normal file
23
vendor/discord-php-helpers/voice/src/Discord/Voice/Exceptions/Libraries/DCANotFoundException.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Exceptions\Libraries;
|
||||
|
||||
/**
|
||||
* Thrown when DCA could not be found.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
class DCANotFoundException extends \Exception
|
||||
{
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Exceptions\Libraries;
|
||||
|
||||
/**
|
||||
* Thrown when the FFmpeg binary cannot be found in your PATH.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
class FFmpegNotFoundException extends \Exception
|
||||
{
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Exceptions\Libraries;
|
||||
|
||||
/**
|
||||
* Thrown when libsodium or libsodium-php cannot be found.
|
||||
*
|
||||
* @since 3.2.1
|
||||
*/
|
||||
class LibSodiumNotFoundException extends \Exception
|
||||
{
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Exceptions\Libraries;
|
||||
|
||||
/**
|
||||
* Thrown when FFmpeg is not compiled with libopus.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
class OpusNotFoundException extends \Exception
|
||||
{
|
||||
}
|
||||
23
vendor/discord-php-helpers/voice/src/Discord/Voice/Exceptions/Libraries/OutdatedDCAException.php
vendored
Normal file
23
vendor/discord-php-helpers/voice/src/Discord/Voice/Exceptions/Libraries/OutdatedDCAException.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Exceptions\Libraries;
|
||||
|
||||
/**
|
||||
* Thrown when the installed DCA version is outdated.
|
||||
*
|
||||
* @since 3.2.0
|
||||
*/
|
||||
class OutdatedDCAException extends \Exception
|
||||
{
|
||||
}
|
||||
212
vendor/discord-php-helpers/voice/src/Discord/Voice/Helpers/Buffer.php
vendored
Normal file
212
vendor/discord-php-helpers/voice/src/Discord/Voice/Helpers/Buffer.php
vendored
Normal file
@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Helpers;
|
||||
|
||||
use Discord\Voice\Exceptions\BufferTimedOutException;
|
||||
use Evenement\EventEmitter;
|
||||
use React\EventLoop\Loop;
|
||||
use React\EventLoop\LoopInterface;
|
||||
use React\Promise\Deferred;
|
||||
use React\Promise\PromiseInterface;
|
||||
use React\Stream\WritableStreamInterface;
|
||||
|
||||
/**
|
||||
* @since 6.0.0
|
||||
*/
|
||||
class Buffer extends EventEmitter implements WritableStreamInterface
|
||||
{
|
||||
/**
|
||||
* Internal buffer.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $buffer = '';
|
||||
|
||||
/**
|
||||
* Array of deferred reads waiting to be resolved.
|
||||
*
|
||||
* @var Deferred[]|int[]
|
||||
*/
|
||||
protected $reads = [];
|
||||
|
||||
/**
|
||||
* Whether the buffer has been closed.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $closed = false;
|
||||
|
||||
/**
|
||||
* ReactPHP event loop.
|
||||
* Required for timeouts.
|
||||
*
|
||||
* @var LoopInterface
|
||||
*/
|
||||
protected $loop;
|
||||
|
||||
public function __construct(?LoopInterface $loop = null)
|
||||
{
|
||||
$this->loop = $loop ?? Loop::get();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function write($data): bool
|
||||
{
|
||||
if ($this->closed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->buffer .= (string) $data;
|
||||
|
||||
foreach ($this->reads as $key => [$deferred, $length]) {
|
||||
if (($output = $this->readRaw($length)) !== false) {
|
||||
$deferred->resolve($output);
|
||||
unset($this->reads[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads from the buffer and returns in a string.
|
||||
* Returns false if there were not enough bytes in the buffer.
|
||||
*
|
||||
* @param int $length Number of bytes to read.
|
||||
*
|
||||
* @return string|bool The bytes read, or false if not enough bytes are present.
|
||||
*/
|
||||
protected function readRaw(int $length)
|
||||
{
|
||||
if (strlen($this->buffer) >= $length) {
|
||||
$output = substr($this->buffer, 0, $length);
|
||||
$this->buffer = substr($this->buffer, $length);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads from the buffer and returns a promise.
|
||||
* The promise will resolve when there are enough bytes in the buffer to
|
||||
* read.
|
||||
*
|
||||
* @param int $length Number of bytes to read.
|
||||
* @param string|null $format Format to read the bytes in. See `pack()`.
|
||||
* @param int $timeout Time in milliseconds before the read times out.
|
||||
*
|
||||
* @return PromiseInterface<mixed, \RuntimeException>
|
||||
*
|
||||
* @throws \RuntimeException When there is an error unpacking the read bytes.
|
||||
*/
|
||||
public function read(int $length, ?string $format = null, ?int $timeout = -1): PromiseInterface
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
|
||||
if (($output = $this->readRaw($length)) !== false) {
|
||||
$deferred->resolve($output);
|
||||
} else {
|
||||
$this->reads[] = [$deferred, $length];
|
||||
|
||||
if ($timeout > 0 && $this->loop !== null) {
|
||||
$timer = $this->loop->addTimer($timeout / 1000, function () use ($deferred) {
|
||||
$deferred->reject(new BufferTimedOutException());
|
||||
});
|
||||
|
||||
$deferred->promise()->then(function () use ($timer) {
|
||||
$this->loop->cancelTimer($timer);
|
||||
});
|
||||
} elseif ($timeout === 0) {
|
||||
$deferred->reject(new BufferTimedOutException());
|
||||
}
|
||||
}
|
||||
|
||||
return $deferred->promise()->then(function ($d) use ($format) {
|
||||
if ($format !== null) {
|
||||
$unpacked = unpack($format, $d);
|
||||
|
||||
if ($unpacked === false) {
|
||||
throw new \RuntimeException('Error unpacking buffer.');
|
||||
}
|
||||
|
||||
return reset($unpacked);
|
||||
}
|
||||
|
||||
return $d;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a signed 32-bit integer from the buffer.
|
||||
*
|
||||
* @param int $timeout Time in milliseconds before the read times out.
|
||||
*
|
||||
* @return PromiseInterface<int, \RuntimeException>
|
||||
*
|
||||
* @throws \RuntimeException When there is an error unpacking the read bytes.
|
||||
*/
|
||||
public function readInt32(int $timeout = -1): PromiseInterface
|
||||
{
|
||||
return $this->read(4, 'l', $timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a signed 16-bit integer from the buffer.
|
||||
*
|
||||
* @param int $timeout Time in milliseconds before the read times out.
|
||||
*
|
||||
* @return PromiseInterface<int, \RuntimeException>
|
||||
*
|
||||
* @throws \RuntimeException When there is an error unpacking the read bytes.
|
||||
*/
|
||||
public function readInt16(int $timeout = -1): PromiseInterface
|
||||
{
|
||||
return $this->read(2, 'v', $timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isWritable()
|
||||
{
|
||||
return $this->closed;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function end($data = null): void
|
||||
{
|
||||
$this->write($data);
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function close(): void
|
||||
{
|
||||
if ($this->closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->buffer = [];
|
||||
$this->closed = true;
|
||||
$this->emit('close');
|
||||
}
|
||||
}
|
||||
28
vendor/discord-php-helpers/voice/src/Discord/Voice/Helpers/ByteBuffer/AbstractBuffer.php
vendored
Normal file
28
vendor/discord-php-helpers/voice/src/Discord/Voice/Helpers/ByteBuffer/AbstractBuffer.php
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Helpers\ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author alexandre433
|
||||
*/
|
||||
abstract class AbstractBuffer implements ReadableBuffer, WriteableBuffer
|
||||
{
|
||||
abstract public function __construct($argument);
|
||||
|
||||
abstract public function __toString(): string;
|
||||
|
||||
abstract public function length(): int;
|
||||
|
||||
abstract public function getLastEmptyPosition(): int;
|
||||
}
|
||||
309
vendor/discord-php-helpers/voice/src/Discord/Voice/Helpers/ByteBuffer/Buffer.php
vendored
Normal file
309
vendor/discord-php-helpers/voice/src/Discord/Voice/Helpers/ByteBuffer/Buffer.php
vendored
Normal file
@ -0,0 +1,309 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Helpers\ByteBuffer;
|
||||
|
||||
use Discord\Voice\Helpers\FormatPackEnum;
|
||||
|
||||
/**
|
||||
* Helper class for handling binary data.
|
||||
*
|
||||
* @author alexandre433
|
||||
*
|
||||
* @throws \InvalidArgumentException If invalid arguments are provided or buffer overflows.
|
||||
*/
|
||||
class Buffer extends AbstractBuffer implements \ArrayAccess
|
||||
{
|
||||
use BufferArrayAccessTrait;
|
||||
|
||||
protected \SplFixedArray $buffer;
|
||||
|
||||
public function __construct($argument)
|
||||
{
|
||||
is_string($argument)
|
||||
? $this->initializeStructs(strlen($argument), $argument)
|
||||
: (is_int($argument)
|
||||
? $this->initializeStructs($argument, pack(FormatPackEnum::x->value."$argument"))
|
||||
: throw new \InvalidArgumentException('Constructor argument must be an binary string or integer'));
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return implode('', iterator_to_array($this->buffer, false));
|
||||
}
|
||||
|
||||
public static function make($argument): static
|
||||
{
|
||||
return new static($argument);
|
||||
}
|
||||
|
||||
protected function initializeStructs($length, string $content): void
|
||||
{
|
||||
$this->buffer = new \SplFixedArray($length);
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$this->buffer[$i] = $content[$i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a value into the buffer at the specified offset.
|
||||
*
|
||||
* @param FormatPackEnum|string $format
|
||||
* @param mixed $value
|
||||
* @param int $offset
|
||||
* @param ?int $length
|
||||
* @return Buffer
|
||||
*/
|
||||
protected function insert($format, $value, int $offset, ?int $length = null): self
|
||||
{
|
||||
$bytes = pack($format?->value ?? $format, $value);
|
||||
|
||||
if (null === $length) {
|
||||
$length = strlen($bytes);
|
||||
}
|
||||
|
||||
for ($i = 0; $i < strlen($bytes); $i++) {
|
||||
$this->buffer[$offset++] = $bytes[$i];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a value from the buffer at the specified offset.
|
||||
*
|
||||
* @param FormatPackEnum|string $format
|
||||
* @param int $offset
|
||||
* @param int $length
|
||||
* @return mixed
|
||||
*/
|
||||
protected function extract(FormatPackEnum|string $format, int $offset, int $length)
|
||||
{
|
||||
$encoded = '';
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$encoded .= $this->buffer->offsetGet($offset + $i);
|
||||
}
|
||||
|
||||
if ($format == FormatPackEnum::N && PHP_INT_SIZE <= 4) {
|
||||
[, $h, $l] = unpack('n*', $encoded);
|
||||
$result = $l + $h * 0x010000;
|
||||
} elseif ($format == FormatPackEnum::V && PHP_INT_SIZE <= 4) {
|
||||
[, $h, $l] = unpack('v*', $encoded);
|
||||
$result = $h + $l * 0x010000;
|
||||
} else {
|
||||
[, $result] = unpack($format?->value ?? $format, $encoded);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the actual value exceeds the expected maximum size.
|
||||
*
|
||||
* @param mixed $excpectedMax
|
||||
* @param mixed $actual
|
||||
* @throws \InvalidArgumentException
|
||||
* @return static
|
||||
*/
|
||||
protected function checkForOverSize($expectedMax, string|int $actual): self
|
||||
{
|
||||
if ($actual > $expectedMax) {
|
||||
throw new \InvalidArgumentException('actual exceeded expectedMax limit');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function length(): int
|
||||
{
|
||||
return $this->buffer->getSize();
|
||||
}
|
||||
|
||||
public function getLastEmptyPosition(): int
|
||||
{
|
||||
foreach ($this->buffer as $key => $value) {
|
||||
if (empty(trim($value))) {
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string to the buffer at the specified offset.
|
||||
*
|
||||
* @param string $value The value that will be written.
|
||||
* @param int|null $offset The offset that the value will be written at.
|
||||
* @return static
|
||||
*/
|
||||
public function write($value, ?int $offset = null): self
|
||||
{
|
||||
if (null === $offset) {
|
||||
$offset = $this->getLastEmptyPosition();
|
||||
}
|
||||
|
||||
$length = strlen($value);
|
||||
$this->insert('a'.$length, $value, $offset, $length);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an 8-bit signed integer to the buffer at the specified offset.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int|null $offset The offset that the value will be written at.
|
||||
* @return static
|
||||
*/
|
||||
public function writeInt8($value, ?int $offset = null): self
|
||||
{
|
||||
if (null === $offset) {
|
||||
$offset = $this->getLastEmptyPosition();
|
||||
}
|
||||
|
||||
$format = FormatPackEnum::C;
|
||||
$this->checkForOverSize(0xff, $value);
|
||||
$this->insert($format, $value, $offset, $format->getLength());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a 16-bit signed integer to the buffer at the specified offset.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int|null $offset The offset that the value will be written at.
|
||||
* @return static
|
||||
*/
|
||||
public function writeInt16BE($value, ?int $offset = null): self
|
||||
{
|
||||
if (null === $offset) {
|
||||
$offset = $this->getLastEmptyPosition();
|
||||
}
|
||||
|
||||
$format = FormatPackEnum::n;
|
||||
$this->checkForOverSize(0xffff, $value);
|
||||
$this->insert($format, $value, $offset, $format->getLength());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a 16-bit signed integer to the buffer at the specified offset.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int|null $offset The offset that the value will be written at.
|
||||
* @return static
|
||||
*/
|
||||
public function writeInt16LE($value, ?int $offset = null): self
|
||||
{
|
||||
if (null === $offset) {
|
||||
$offset = $this->getLastEmptyPosition();
|
||||
}
|
||||
|
||||
$format = FormatPackEnum::v;
|
||||
$this->checkForOverSize(0xffff, $value);
|
||||
$this->insert($format, $value, $offset, $format->getLength());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a 32-bit signed integer to the buffer at the specified offset.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int|null $offset The offset that the value will be written at.
|
||||
* @return static
|
||||
*/
|
||||
public function writeInt32BE($value, ?int $offset = null): self
|
||||
{
|
||||
if (null === $offset) {
|
||||
$offset = $this->getLastEmptyPosition();
|
||||
}
|
||||
|
||||
$format = FormatPackEnum::N;
|
||||
$this->checkForOverSize(0xffffffff, $value);
|
||||
$this->insert($format, $value, $offset, $format->getLength());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a 32-bit signed integer to the buffer at the specified offset.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int|null $offset The offset that the value will be written at.
|
||||
* @return static
|
||||
*/
|
||||
#[\Override]
|
||||
public function writeInt32LE($value, ?int $offset = null): self
|
||||
{
|
||||
if (null === $offset) {
|
||||
$offset = $this->getLastEmptyPosition();
|
||||
}
|
||||
|
||||
$format = FormatPackEnum::V;
|
||||
$this->checkForOverSize(0xffffffff, $value);
|
||||
$this->insert($format, $value, $offset, $format->getLength());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a string from the buffer at the specified offset.
|
||||
*
|
||||
* @param int $offset The offset to read from.
|
||||
* @param int $length The length of the string to read.
|
||||
* @return string The data read.
|
||||
*/
|
||||
public function read(int $offset, int $length)
|
||||
{
|
||||
return $this->extract('a'.$length, $offset, $length);
|
||||
}
|
||||
|
||||
public function readInt8(int $offset)
|
||||
{
|
||||
$format = FormatPackEnum::C;
|
||||
|
||||
return $this->extract($format, $offset, $format->getLength());
|
||||
}
|
||||
|
||||
public function readInt16BE(int $offset)
|
||||
{
|
||||
$format = FormatPackEnum::n;
|
||||
|
||||
return $this->extract($format, $offset, $format->getLength());
|
||||
}
|
||||
|
||||
public function readInt16LE(int $offset)
|
||||
{
|
||||
$format = FormatPackEnum::v;
|
||||
|
||||
return $this->extract($format, $offset, $format->getLength());
|
||||
}
|
||||
|
||||
public function readInt32BE(int $offset)
|
||||
{
|
||||
$format = FormatPackEnum::N;
|
||||
|
||||
return $this->extract($format, $offset, $format->getLength());
|
||||
}
|
||||
|
||||
public function readInt32LE(int $offset)
|
||||
{
|
||||
$format = FormatPackEnum::V;
|
||||
|
||||
return $this->extract($format, $offset, $format->getLength());
|
||||
}
|
||||
}
|
||||
218
vendor/discord-php-helpers/voice/src/Discord/Voice/Helpers/ByteBuffer/BufferArrayAccessTrait.php
vendored
Normal file
218
vendor/discord-php-helpers/voice/src/Discord/Voice/Helpers/ByteBuffer/BufferArrayAccessTrait.php
vendored
Normal file
@ -0,0 +1,218 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Helpers\ByteBuffer;
|
||||
|
||||
use Discord\Voice\Helpers\FormatPackEnum;
|
||||
|
||||
/**
|
||||
* @author Valithor Obsidion <valithor@discordphp.org>
|
||||
*/
|
||||
trait BufferArrayAccessTrait
|
||||
{
|
||||
/**
|
||||
* Writes a 32-bit unsigned integer with big endian.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int $offset The offset that the value will be written.
|
||||
*/
|
||||
public function writeUInt32BE(int $value, int $offset): self
|
||||
{
|
||||
return $this->insert(FormatPackEnum::I, $value, $offset, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a 64-bit unsigned integer with little endian.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int $offset The offset that the value will be written.
|
||||
*/
|
||||
public function writeUInt64LE(int $value, int $offset): self
|
||||
{
|
||||
return $this->insert(FormatPackEnum::P, $value, $offset, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a signed integer.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int $offset The offset that the value will be written.
|
||||
*/
|
||||
public function writeInt(int $value, int $offset): self
|
||||
{
|
||||
return $this->insert(FormatPackEnum::N, $value, $offset, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a unsigned integer.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int $offset The offset that the value will be written.
|
||||
*/
|
||||
public function writeUInt(int $value, int $offset): self
|
||||
{
|
||||
return $this->insert(FormatPackEnum::I, $value, $offset, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a signed integer.
|
||||
*
|
||||
* @param int $offset The offset to read from.
|
||||
*
|
||||
* @return int The data read.
|
||||
*/
|
||||
public function readInt(int $offset): int
|
||||
{
|
||||
return $this->extract(FormatPackEnum::N, $offset, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a signed integer.
|
||||
*
|
||||
* @param int $offset The offset to read from.
|
||||
*
|
||||
* @return int The data read.
|
||||
*/
|
||||
public function readUInt(int $offset): int
|
||||
{
|
||||
return $this->extract(FormatPackEnum::I, $offset, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an unsigned big endian short.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int $offset The offset that the value will be written.
|
||||
*/
|
||||
public function writeShort(int $value, int $offset): self
|
||||
{
|
||||
return $this->insert(FormatPackEnum::n, $value, $offset, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an unsigned big endian short.
|
||||
*
|
||||
* @param int $offset The offset to read from.
|
||||
*
|
||||
* @return int The data read.
|
||||
*/
|
||||
public function readShort(int $offset): int
|
||||
{
|
||||
return $this->extract(FormatPackEnum::n, $offset, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a unsigned integer with little endian.
|
||||
*
|
||||
* @param int $offset The offset that will be read.
|
||||
*
|
||||
* @return int The value that is at the specified offset.
|
||||
*/
|
||||
public function readUIntLE(int $offset): int
|
||||
{
|
||||
return $this->extract(FormatPackEnum::I, $offset, 3);
|
||||
}
|
||||
|
||||
public function readChar(int $offset): string
|
||||
{
|
||||
return $this->extract(FormatPackEnum::c, $offset, 1);
|
||||
}
|
||||
|
||||
public function readUChar(int $offset): string
|
||||
{
|
||||
return $this->extract(FormatPackEnum::C, $offset, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a char.
|
||||
*
|
||||
* @param string $value The value that will be written.
|
||||
* @param int $offset The offset that the value will be written.
|
||||
*/
|
||||
public function writeChar(string $value, int $offset): self
|
||||
{
|
||||
return $this->insert(FormatPackEnum::c, $value, $offset, FormatPackEnum::c->getLength());
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes raw binary to the buffer.
|
||||
*
|
||||
* @param int $value The value that will be written.
|
||||
* @param int $offset The offset that the value will be written at.
|
||||
*/
|
||||
public function writeRaw(int $value, int $offset): void
|
||||
{
|
||||
$this->buffer[$offset] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a binary string to the buffer.
|
||||
*
|
||||
* @param string $value The value that will be written.
|
||||
* @param int $offset The offset that the value will be written at.
|
||||
*/
|
||||
public function writeRawString(string $value, int $offset): void
|
||||
{
|
||||
for ($i = 0; $i < strlen($value); ++$i) {
|
||||
$this->buffer[$offset++] = $value[$i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an attribute via key. Used for \ArrayAccess.
|
||||
*
|
||||
* @param mixed $key The attribute key.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($key)
|
||||
{
|
||||
return $this->buffer[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an attribute exists via key. Used for \ArrayAccess.
|
||||
*
|
||||
* @param mixed $key The attribute key.
|
||||
*
|
||||
* @return bool Whether the offset exists.
|
||||
*/
|
||||
public function offsetExists($key): bool
|
||||
{
|
||||
return isset($this->buffer[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an attribute via key. Used for \ArrayAccess.
|
||||
*
|
||||
* @param mixed $key The attribute key.
|
||||
* @param mixed $value The attribute value.
|
||||
*/
|
||||
public function offsetSet($key, $value): void
|
||||
{
|
||||
$this->buffer[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets an attribute via key. Used for \ArrayAccess.
|
||||
*
|
||||
* @param string $key The attribute key.
|
||||
*/
|
||||
public function offsetUnset($key): void
|
||||
{
|
||||
if (isset($this->buffer[$key])) {
|
||||
unset($this->buffer[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
vendor/discord-php-helpers/voice/src/Discord/Voice/Helpers/ByteBuffer/ReadableBuffer.php
vendored
Normal file
32
vendor/discord-php-helpers/voice/src/Discord/Voice/Helpers/ByteBuffer/ReadableBuffer.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Helpers\ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author alexandre433
|
||||
*/
|
||||
interface ReadableBuffer
|
||||
{
|
||||
public function read(int $offset, int $length);
|
||||
|
||||
public function readInt8(int $offset);
|
||||
|
||||
public function readInt16BE(int $offset);
|
||||
|
||||
public function readInt16LE(int $offset);
|
||||
|
||||
public function readInt32BE(int $offset);
|
||||
|
||||
public function readInt32LE(int $offset);
|
||||
}
|
||||
67
vendor/discord-php-helpers/voice/src/Discord/Voice/Helpers/ByteBuffer/WriteableBuffer.php
vendored
Normal file
67
vendor/discord-php-helpers/voice/src/Discord/Voice/Helpers/ByteBuffer/WriteableBuffer.php
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Helpers\ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author alexandre433
|
||||
*/
|
||||
interface WriteableBuffer
|
||||
{
|
||||
public function write($value, ?int $offset = null): self;
|
||||
|
||||
/**
|
||||
* Write an int8 to the buffer.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param int|null $offset The offset to write the int8, if not provided the length of the buffer will be used
|
||||
* @return self
|
||||
*/
|
||||
public function writeInt8($value, ?int $offset = null): self;
|
||||
|
||||
/**
|
||||
* Write an int16 to the buffer in big-endian format.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param int|null $offset The offset to write the int8, if not provided the length of the buffer will be used
|
||||
* @return self
|
||||
*/
|
||||
public function writeInt16BE($value, ?int $offset = null): self;
|
||||
|
||||
/**
|
||||
* Write an int16 to the buffer in little-endian format.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param int|null $offset The offset to write the int8, if not provided the length of the buffer will be used
|
||||
* @return self
|
||||
*/
|
||||
public function writeInt16LE($value, ?int $offset = null): self;
|
||||
|
||||
/**
|
||||
* Write an int32 to the buffer in big-endian format.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param int|null $offset The offset to write the int8, if not provided the length of the buffer will be used
|
||||
* @return self
|
||||
*/
|
||||
public function writeInt32BE($value, ?int $offset = null): self;
|
||||
|
||||
/**
|
||||
* Write an int32 to the buffer in little-endian format.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param int|null $offset The offset to write the int8, if not provided the length of the buffer will be used
|
||||
* @return self
|
||||
*/
|
||||
public function writeInt32LE($value, ?int $offset = null): self;
|
||||
}
|
||||
178
vendor/discord-php-helpers/voice/src/Discord/Voice/Helpers/FormatPackEnum.php
vendored
Normal file
178
vendor/discord-php-helpers/voice/src/Discord/Voice/Helpers/FormatPackEnum.php
vendored
Normal file
@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice\Helpers;
|
||||
|
||||
/**
|
||||
* @link https://www.php.net/manual/en/function.pack.php
|
||||
*/
|
||||
enum FormatPackEnum: string
|
||||
{
|
||||
/**
|
||||
* NUL-padded string.
|
||||
*/
|
||||
case a = 'a';
|
||||
|
||||
/**
|
||||
* SPACE-padded string.
|
||||
*/
|
||||
case A = 'A';
|
||||
|
||||
/**
|
||||
* Hex string, low nibble first.
|
||||
*/
|
||||
case h = 'h';
|
||||
|
||||
/**
|
||||
* Hex string, high nibble first.
|
||||
*/
|
||||
case H = 'H';
|
||||
|
||||
/**
|
||||
* signed char.
|
||||
*/
|
||||
case c = 'c';
|
||||
|
||||
/**
|
||||
* unsigned char.
|
||||
*/
|
||||
case C = 'C';
|
||||
|
||||
/**
|
||||
* signed short (always 16 bit, machine byte order).
|
||||
*/
|
||||
case s = 's';
|
||||
|
||||
/**
|
||||
* unsigned short (always 16 bit, machine byte order).
|
||||
*/
|
||||
case S = 'S';
|
||||
|
||||
/**
|
||||
* unsigned short (always 16 bit, big endian byte order).
|
||||
*/
|
||||
case n = 'n';
|
||||
|
||||
/**
|
||||
* unsigned short (always 16 bit, little endian byte order).
|
||||
*/
|
||||
case v = 'v';
|
||||
|
||||
/**
|
||||
* signed integer (machine dependent size and byte order).
|
||||
*/
|
||||
case i = 'i';
|
||||
|
||||
/**
|
||||
* unsigned integer (machine dependent size and byte order).
|
||||
*/
|
||||
case I = 'I';
|
||||
|
||||
/**
|
||||
* signed long (always 32 bit, machine byte order).
|
||||
*/
|
||||
case l = 'l';
|
||||
|
||||
/**
|
||||
* unsigned long (always 32 bit, machine byte order).
|
||||
*/
|
||||
case L = 'L';
|
||||
|
||||
/**
|
||||
* unsigned long (always 32 bit, big endian byte order).
|
||||
*/
|
||||
case N = 'N';
|
||||
|
||||
/**
|
||||
* unsigned long (always 32 bit, little endian byte order).
|
||||
*/
|
||||
case V = 'V';
|
||||
|
||||
/**
|
||||
* signed long long (always 64 bit, machine byte order).
|
||||
*/
|
||||
case q = 'q';
|
||||
|
||||
/**
|
||||
* unsigned long long (always 64 bit, machine byte order).
|
||||
*/
|
||||
case Q = 'Q';
|
||||
|
||||
/**
|
||||
* unsigned long long (always 64 bit, big endian byte order).
|
||||
*/
|
||||
case J = 'J';
|
||||
|
||||
/**
|
||||
* unsigned long long (always 64 bit, little endian byte order).
|
||||
*/
|
||||
case P = 'P';
|
||||
|
||||
/**
|
||||
* float (machine dependent size and representation).
|
||||
*/
|
||||
case f = 'f';
|
||||
|
||||
/**
|
||||
* float (machine dependent size, little endian byte order).
|
||||
*/
|
||||
case g = 'g';
|
||||
|
||||
/**
|
||||
* float (machine dependent size, big endian byte order).
|
||||
*/
|
||||
case G = 'G';
|
||||
|
||||
/**
|
||||
* double (machine dependent size and representation).
|
||||
*/
|
||||
case d = 'd';
|
||||
|
||||
/**
|
||||
* double (machine dependent size, little endian byte order).
|
||||
*/
|
||||
case e = 'e';
|
||||
|
||||
/**
|
||||
* double (machine dependent size, big endian byte order).
|
||||
*/
|
||||
case E = 'E';
|
||||
|
||||
/**
|
||||
* NUL byte.
|
||||
*/
|
||||
case x = 'x';
|
||||
|
||||
/**
|
||||
* Back up one byte.
|
||||
*/
|
||||
case X = 'X';
|
||||
|
||||
/**
|
||||
* NUL-padded string.
|
||||
*/
|
||||
case Z = 'Z';
|
||||
|
||||
/**
|
||||
* NUL-fill to absolute position.
|
||||
*/
|
||||
case At = '@';
|
||||
|
||||
public function getLength(): int
|
||||
{
|
||||
return match ($this) {
|
||||
self::n, self::v => 2,
|
||||
self::N, self::V => 4,
|
||||
self::c, self::C => 1,
|
||||
default => throw new \InvalidArgumentException('Invalid format pack'),
|
||||
};
|
||||
}
|
||||
}
|
||||
226
vendor/discord-php-helpers/voice/src/Discord/Voice/Manager.php
vendored
Normal file
226
vendor/discord-php-helpers/voice/src/Discord/Voice/Manager.php
vendored
Normal file
@ -0,0 +1,226 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice;
|
||||
|
||||
use Discord\Discord;
|
||||
use Discord\Voice\Exceptions\Channels\CantJoinMoreThanOneChannelException;
|
||||
use Discord\Voice\Exceptions\Channels\CantSpeakInChannelException;
|
||||
use Discord\Voice\Exceptions\Channels\ChannelMustAllowVoiceException;
|
||||
use Discord\Voice\Exceptions\Channels\EnterChannelDeniedException;
|
||||
use Discord\Parts\Channel\Channel;
|
||||
use Discord\Parts\WebSockets\VoiceServerUpdate;
|
||||
use Discord\Parts\WebSockets\VoiceStateUpdate;
|
||||
use Discord\WebSockets\Event;
|
||||
use Discord\WebSockets\Op;
|
||||
use Discord\WebSockets\VoicePayload;
|
||||
use Evenement\EventEmitterTrait;
|
||||
use React\Promise\Deferred;
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
/**
|
||||
* Manages many voice clients for the bot.
|
||||
*
|
||||
* @requires libopus - Linux | NOT TESTED - WINDOWS
|
||||
* @requires FFMPEG - Linux | NOT TESTED - WINDOWS
|
||||
*
|
||||
* @since 10.19.0
|
||||
*/
|
||||
final class Manager
|
||||
{
|
||||
use EventEmitterTrait;
|
||||
|
||||
/**
|
||||
* @param Discord $discord
|
||||
* @param array<string, Client> $clients
|
||||
*/
|
||||
public function __construct(
|
||||
protected Discord $discord,
|
||||
public array $clients = [],
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the creation of a new voice client and joins the specified channel.
|
||||
*
|
||||
* @param \Discord\Parts\Channel\Channel $channel
|
||||
* @param \Discord\Discord $discord
|
||||
* @param array &$voice_sessions
|
||||
* @param bool $mute
|
||||
* @param bool $deaf
|
||||
*
|
||||
* @throws \Discord\Voice\Exceptions\Channels\ChannelMustAllowVoiceException
|
||||
* @throws \Discord\Voice\Exceptions\Channels\EnterChannelDeniedException
|
||||
* @throws \Discord\Voice\Exceptions\Channels\CantJoinMoreThanOneChannelException
|
||||
* @throws \Discord\Voice\Exceptions\Channels\CantSpeakInChannelException
|
||||
*
|
||||
* @return \React\Promise\PromiseInterface
|
||||
*/
|
||||
public function joinChannel(Channel $channel, Discord $discord, array &$voice_sessions, bool $mute = false, bool $deaf = true): PromiseInterface
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
|
||||
try {
|
||||
if (! $channel->isVoiceBased()) {
|
||||
throw new ChannelMustAllowVoiceException();
|
||||
}
|
||||
|
||||
$botperms = $channel->getBotPermissions();
|
||||
|
||||
if (! $botperms->connect) {
|
||||
throw new EnterChannelDeniedException();
|
||||
}
|
||||
|
||||
if (! $botperms->speak && ! $mute) {
|
||||
throw new CantSpeakInChannelException();
|
||||
}
|
||||
|
||||
if (isset($this->clients[$channel->guild_id])) {
|
||||
throw new CantJoinMoreThanOneChannelException();
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
$deferred->reject($th);
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
// The same as new Client(...)
|
||||
$this->clients[$channel->guild_id] = Client::make(
|
||||
$this->discord,
|
||||
$channel,
|
||||
$voice_sessions,
|
||||
['dnsConfig' => $discord->options['dnsConfig']],
|
||||
$deaf,
|
||||
$mute,
|
||||
$deferred,
|
||||
$this,
|
||||
false
|
||||
);
|
||||
|
||||
$discord->on(Event::VOICE_STATE_UPDATE, fn ($state) => $this->stateUpdate($state, $channel));
|
||||
// Creates Voice Client and waits for the voice server update.
|
||||
$discord->on(Event::VOICE_SERVER_UPDATE, fn ($state, Discord $discord) => $this->serverUpdate($state, $channel, $discord, $deferred));
|
||||
|
||||
$discord->send(VoicePayload::new(
|
||||
Op::OP_UPDATE_VOICE_STATE,
|
||||
[
|
||||
'guild_id' => $channel->guild_id,
|
||||
'channel_id' => $channel->id,
|
||||
'self_mute' => $mute,
|
||||
'self_deaf' => $deaf,
|
||||
],
|
||||
));
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the voice client for a given guild id.
|
||||
*
|
||||
* @param string|int $guildId
|
||||
*
|
||||
* @return \Discord\Voice\Client|null
|
||||
*/
|
||||
public function getClient(string|int|Channel $guildChannelOrId): ?Client
|
||||
{
|
||||
if ($guildChannelOrId instanceof Channel) {
|
||||
$guildChannelOrId = $guildChannelOrId->guild_id;
|
||||
}
|
||||
|
||||
if (! isset($this->clients[$guildChannelOrId])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->clients[$guildChannelOrId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the voice state update event to update session information for the voice client.
|
||||
*
|
||||
* @param \Discord\Parts\WebSockets\VoiceStateUpdate $state
|
||||
* @param \Discord\Parts\Channel\Channel $channel
|
||||
*/
|
||||
public function stateUpdate(VoiceStateUpdate $state, Channel $channel): void
|
||||
{
|
||||
if ($state->guild_id != $channel->guild_id) {
|
||||
return; // This voice state update isn't for our guild.
|
||||
}
|
||||
|
||||
$client = $this->getClient($channel);
|
||||
if (! $client) {
|
||||
return; // We might have left the voice channel already.
|
||||
}
|
||||
|
||||
$client->setData([
|
||||
'session' => $state->session_id,
|
||||
'deaf' => $state->deaf,
|
||||
'mute' => $state->mute,
|
||||
]);
|
||||
|
||||
$this->discord->getLogger()->info('received session id for voice session', ['guild' => $channel->guild_id, 'session_id' => $state->session_id]);
|
||||
$this->discord->voice_sessions[$channel->guild_id] = $state->session_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the voice server update event to create a new voice client with the provided state.
|
||||
*
|
||||
* @param \Discord\Parts\WebSockets\VoiceServerUpdate $state
|
||||
* @param \Discord\Parts\Channel\Channel $channel
|
||||
* @param \Discord\Discord $discord
|
||||
* @param \React\Promise\Deferred $deferred
|
||||
*/
|
||||
protected function serverUpdate(VoiceServerUpdate $state, Channel $channel, Discord $discord, Deferred $deferred): void
|
||||
{
|
||||
if ($state->guild_id !== $channel->guild_id) {
|
||||
return; // This voice server update isn't for our guild.
|
||||
}
|
||||
|
||||
$client = $this->getClient($channel);
|
||||
if (! $client) {
|
||||
return; // We might have left the voice channel already.
|
||||
}
|
||||
|
||||
$this->discord->getLogger()->info('received token and endpoint for voice session', [
|
||||
'guild' => $channel->guild_id,
|
||||
'token' => '*****',
|
||||
'endpoint' => $state->endpoint,
|
||||
]);
|
||||
|
||||
$client->once('ready', function () use (&$client, $deferred, $channel) {
|
||||
$this->discord->logger->info('voice manager is ready');
|
||||
$this->discord->voice->clients[$channel->guild_id] = $client;
|
||||
$deferred->resolve($client);
|
||||
});
|
||||
$client->once('error', function ($e) use ($deferred) {
|
||||
$this->discord->logger->error('error initializing voice manager', ['e' => $e->getMessage()]);
|
||||
$deferred->reject($e);
|
||||
});
|
||||
$client->once('close', function () use ($channel) {
|
||||
$this->discord->logger->warning('voice manager closed');
|
||||
unset($this->discord->voice->clients[$channel->guild_id]);
|
||||
unset($this->discord->voice_sessions[$channel->guild_id]);
|
||||
});
|
||||
|
||||
$client->setData(
|
||||
array_merge(
|
||||
$client->data,
|
||||
[
|
||||
'token' => $state->token,
|
||||
'endpoint' => $state->endpoint,
|
||||
'session' => $client->data['session'] ?? null,
|
||||
],
|
||||
['dnsConfig' => $discord->options['dnsConfig']]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
144
vendor/discord-php-helpers/voice/src/Discord/Voice/OggPage.php
vendored
Normal file
144
vendor/discord-php-helpers/voice/src/Discord/Voice/OggPage.php
vendored
Normal file
@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice;
|
||||
|
||||
use Discord\Voice\Helpers\Buffer;
|
||||
use Generator;
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
/**
|
||||
* Represents a page in an Ogg container.
|
||||
*
|
||||
* @link https://www.rfc-editor.org/rfc/rfc3533
|
||||
*
|
||||
* @since 10.0.0
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class OggPage
|
||||
{
|
||||
/**
|
||||
* Binary format string used to parse header.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private const FORMAT = 'Cversion/Cheader_type/Pgranule_position/Vbitstream_sn/Vpage_seq/Vcsum/Cpage_segments';
|
||||
|
||||
/**
|
||||
* Create a new Ogg page.
|
||||
*
|
||||
* @param int $version The version number of the Ogg file format used in this stream.
|
||||
* @param int $headerType Identifies the specific type of this page.
|
||||
* @param int $granulePosition Contains position information.
|
||||
* @param int $bitstreamSn Contains the unique serial number by which the logical bitstream is identified.
|
||||
* @param int $pageSeq Contains the sequence number of the page so the decoder can identify page loss.
|
||||
* @param int $checksum Contains a 32 bit CRC checksum of the page (including header with zero CRC field and page content).
|
||||
* @param int[] $pageSegments A list of page segment lengths which were contained within the page.
|
||||
* @param string $segmentData The data of all the page segments concatenated.
|
||||
*
|
||||
* @link https://www.rfc-editor.org/rfc/rfc3533#section-6 The Ogg page format
|
||||
*/
|
||||
private function __construct(
|
||||
private int $version,
|
||||
private int $headerType,
|
||||
private int $granulePosition,
|
||||
private int $bitstreamSn,
|
||||
private int $pageSeq,
|
||||
private int $checksum,
|
||||
private array $pageSegments,
|
||||
public string $segmentData,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an Ogg page from a buffer.
|
||||
*
|
||||
* @param Buffer $buffer Buffer to read the Ogg page from.
|
||||
* @param ?int $timeout Time in milliseconds before a buffer read times out.
|
||||
*
|
||||
* @return PromiseInterface<OggPage> Promise containing the Ogg page.
|
||||
*
|
||||
* @throws \UnexpectedValueException If the buffer is out of sync and an invalid header is read.
|
||||
*/
|
||||
public static function fromBuffer(Buffer $buffer, ?int $timeout = -1): PromiseInterface
|
||||
{
|
||||
$header = null;
|
||||
$pageSegments = [];
|
||||
|
||||
return $buffer->read(4, timeout: $timeout)->then(function ($magic) use ($buffer, $timeout) {
|
||||
if ($magic !== 'OggS') {
|
||||
throw new \UnexpectedValueException("Invalid Ogg page header, expected OggS got {$magic}.");
|
||||
}
|
||||
|
||||
return $buffer->read(23, timeout: $timeout);
|
||||
})
|
||||
// Reading header
|
||||
->then(function ($data) use ($buffer, &$header, $timeout) {
|
||||
$header = unpack(self::FORMAT, $data);
|
||||
|
||||
return $buffer->read($header['page_segments'], timeout: $timeout);
|
||||
})
|
||||
// Reading page segment lengths
|
||||
->then(function ($data) use ($buffer, &$pageSegments, $timeout) {
|
||||
$pageSegments = unpack('C*', $data);
|
||||
$data = array_sum($pageSegments);
|
||||
|
||||
return $buffer->read($data, timeout: $timeout);
|
||||
})
|
||||
// Reading segment data
|
||||
->then(function ($data) use (&$header, &$pageSegments) {
|
||||
return new OggPage(
|
||||
$header['version'],
|
||||
$header['header_type'],
|
||||
$header['granule_position'],
|
||||
$header['bitstream_sn'],
|
||||
$header['page_seq'],
|
||||
$header['csum'],
|
||||
$pageSegments,
|
||||
$data
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through the packets contained within the stream, yielding an
|
||||
* array containing binary data and whether the packet is complete, from a
|
||||
* generator.
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function iterPackets()
|
||||
{
|
||||
$packetLen = 0;
|
||||
$offset = 0;
|
||||
$partial = true;
|
||||
|
||||
foreach ($this->pageSegments as $seg) {
|
||||
$packetLen += $seg;
|
||||
if ($seg === 255) {
|
||||
$partial = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
yield [substr($this->segmentData, $offset, $packetLen), true];
|
||||
$offset += $packetLen;
|
||||
$packetLen = 0;
|
||||
$partial = false;
|
||||
}
|
||||
|
||||
if ($partial) {
|
||||
yield [substr($this->segmentData, $offset), false];
|
||||
}
|
||||
}
|
||||
}
|
||||
145
vendor/discord-php-helpers/voice/src/Discord/Voice/OggStream.php
vendored
Normal file
145
vendor/discord-php-helpers/voice/src/Discord/Voice/OggStream.php
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice;
|
||||
|
||||
use Discord\Voice\Exceptions\BufferTimedOutException;
|
||||
use Discord\Voice\Helpers\Buffer;
|
||||
use React\Promise\PromiseInterface;
|
||||
use React\Promise\Promise;
|
||||
|
||||
use function React\Promise\resolve;
|
||||
|
||||
/**
|
||||
* Represents an Ogg Opus stream.
|
||||
*
|
||||
* @link https://www.rfc-editor.org/rfc/rfc7845
|
||||
*
|
||||
* @since 10.0.0
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class OggStream
|
||||
{
|
||||
/**
|
||||
* Leftover bytes from the previous Ogg packet.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private string $leftover = '';
|
||||
|
||||
/**
|
||||
* Buffer of packets that have been parsed and split into Opus chunks.
|
||||
*
|
||||
* @var string[]|null
|
||||
*/
|
||||
private ?array $packets = [];
|
||||
|
||||
/**
|
||||
* Create a new Ogg Opus stream.
|
||||
*
|
||||
* @param Buffer $buffer Buffer to read Ogg Opus packets from.
|
||||
* @param OpusHead $header The header that has already been read from `$buffer`.
|
||||
* @param OpusTags $tags The tags that have already been read from `$buffer`.
|
||||
*/
|
||||
private function __construct(
|
||||
private Buffer $buffer,
|
||||
public OpusHead $header,
|
||||
public OpusTags $tags
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Ogg Opus stream from a buffer. This will read the Opus
|
||||
* header and the Opus tags and return a new Ogg stream ready to read Opus
|
||||
* packets.
|
||||
*
|
||||
* @param Buffer $buffer Buffer to read Ogg Opus packets from.
|
||||
* @param ?int $timeout Time in milliseconds before a buffer read times out.
|
||||
*
|
||||
* @return PromiseInterface<OggStream> A promise containing the Ogg stream.
|
||||
*/
|
||||
public static function fromBuffer(Buffer $buffer, ?int $timeout = -1): PromiseInterface
|
||||
{
|
||||
/** @var OpusHead */
|
||||
$header = null;
|
||||
|
||||
return OggPage::fromBuffer($buffer, $timeout)->then(function (OggPage $page) use (&$header, $buffer, $timeout) {
|
||||
$header = new OpusHead($page->segmentData);
|
||||
|
||||
return OggPage::fromBuffer($buffer, $timeout);
|
||||
})->then(function (OggPage $page) use (&$header, $buffer) {
|
||||
$tags = new OpusTags($page->segmentData);
|
||||
|
||||
return new OggStream($buffer, $header, $tags);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to get a packet from the Ogg stream.
|
||||
*
|
||||
* @return PromiseInterface<string|null> Promise containing an Opus packet. If null, indicates EOF.
|
||||
*/
|
||||
public function getPacket(): PromiseInterface
|
||||
{
|
||||
if ($this->packets === null) {
|
||||
return resolve(null);
|
||||
} elseif (count($this->packets) > 0) {
|
||||
return resolve(array_shift($this->packets));
|
||||
}
|
||||
|
||||
return $this->parsePackets()->then(function ($packets) {
|
||||
if ($packets === null) {
|
||||
$this->packets = null;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->packets = array_merge($this->packets, $packets);
|
||||
|
||||
return $this->getPacket();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to read an Ogg page from the buffer and parse it into Opus
|
||||
* packets.
|
||||
*
|
||||
* @return PromiseInterface<string[]|null> Promise containing an array of Opus packets.
|
||||
*/
|
||||
private function parsePackets(): PromiseInterface
|
||||
{
|
||||
return new Promise(function ($resolve, $reject) {
|
||||
OggPage::fromBuffer($this->buffer, timeout: 0)->then(function ($page) use ($resolve) {
|
||||
$packets = [];
|
||||
$partial = $this->leftover;
|
||||
foreach ($page->iterPackets() as [$data, $complete]) {
|
||||
$partial .= $data;
|
||||
if ($complete) {
|
||||
$packets[] = $partial;
|
||||
$partial = '';
|
||||
}
|
||||
}
|
||||
$this->leftover = $partial;
|
||||
|
||||
$resolve($packets);
|
||||
}, function (\Exception $e) use ($resolve, $reject) {
|
||||
if ($e instanceof BufferTimedOutException) {
|
||||
$resolve(null);
|
||||
} else {
|
||||
$reject($e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
137
vendor/discord-php-helpers/voice/src/Discord/Voice/OpusHead.php
vendored
Normal file
137
vendor/discord-php-helpers/voice/src/Discord/Voice/OpusHead.php
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is a part of the DiscordPHP project.
|
||||
*
|
||||
* Copyright (c) 2015-present David Cole <david.cole1340@gmail.com>
|
||||
*
|
||||
* This file is subject to the MIT license that is bundled
|
||||
* with this source code in the LICENSE.md file.
|
||||
*/
|
||||
|
||||
namespace Discord\Voice;
|
||||
|
||||
/**
|
||||
* Represents the header attached to an Opus Ogg file.
|
||||
*
|
||||
* @link https://www.rfc-editor.org/rfc/rfc7845#section-5.1 Identification Header
|
||||
*
|
||||
* @since 10.0.0
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class OpusHead
|
||||
{
|
||||
/**
|
||||
* Binary format string used to parse header.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private const FORMAT = 'Cversion/Cchannel_count/vpre_skip/Vsample_rate/voutput_gain/Cchannel_map_family';
|
||||
|
||||
/**
|
||||
* Binary format string used to parse optional header.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private const STREAM_COUNT_FORMAT = 'Cstream_count/Ctwo_channel_stream_count';
|
||||
|
||||
/**
|
||||
* Version number of Opus file. Should always be 1.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public int $version;
|
||||
|
||||
/**
|
||||
* Number of channels in the Opus file.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public int $channelCount;
|
||||
|
||||
/**
|
||||
* The number of samples (at 48 kHz) to discard from the decoder output when
|
||||
* starting playback.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public int $preSkip;
|
||||
|
||||
/**
|
||||
* The sample rate of the original input (before encoding), in Hz. This
|
||||
* field is _not_ the sample rate to use for playback of the encoded data.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public int $sampleRate;
|
||||
|
||||
/**
|
||||
* The gain to be applied when decoding.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public int $outputGain;
|
||||
|
||||
/**
|
||||
* Indicates the order and semantic meaning of the output channels.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public int $channelMapFamily;
|
||||
|
||||
/**
|
||||
* The total number of streams encoded in each Ogg packet.
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
public ?int $streamCount = null;
|
||||
|
||||
/**
|
||||
* The number of streams whose decoders are to be configured to produce two
|
||||
* channels (stereo).
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
public ?int $twoChannelStreamCount = null;
|
||||
|
||||
/**
|
||||
* Contains one octet per output channel, indicating which decoded channel
|
||||
* is to be used for each one.
|
||||
*
|
||||
* @var int[]|null
|
||||
*/
|
||||
public ?array $cmap = null;
|
||||
|
||||
/**
|
||||
* Create an instance of OpusHead from a binary string.
|
||||
*
|
||||
* @param string $data Binary string of data.
|
||||
*
|
||||
* @throws \UnexpectedValueException If the binary data was missing the magic bytes.
|
||||
*/
|
||||
public function __construct(string $data)
|
||||
{
|
||||
$magic = substr($data, 0, 8);
|
||||
if ($magic != 'OpusHead') {
|
||||
throw new \UnexpectedValueException("Expected OpusHead, found {$magic}.");
|
||||
}
|
||||
|
||||
$head = unpack(self::FORMAT, $data, 8);
|
||||
$this->version = $head['version'];
|
||||
$this->channelCount = $head['channel_count'];
|
||||
$this->preSkip = $head['pre_skip'];
|
||||
$this->sampleRate = $head['sample_rate'];
|
||||
$this->outputGain = $head['output_gain'];
|
||||
$this->channelMapFamily = $head['channel_map_family'];
|
||||
|
||||
if ($head['channel_map_family'] > 0) {
|
||||
$stream_counts = unpack(self::STREAM_COUNT_FORMAT, $data, 19);
|
||||
$this->streamCount = $stream_counts['stream_count'];
|
||||
$this->twoChannelStreamCount = $stream_counts['two_channel_stream_count'];
|
||||
$this->cmap = array_values(unpack('C*', $data, 21));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user