Если у вас есть свой Discord сервер, возможно вы уже написали простенького бота в Discord для ваших нужд. Как это сделать, я рассказал в этой статье. Однако, возможностей простого бота зачастую недостаточно, поэтому нам на помощь приходят интерактивные слэш команды в Discord. Если создать слэш команды, они будут появляться как подсказки, когда пользователь введет в поле для отправки сообщения символ слэш. В подсказке будет указана сама команда и ее описание, заданное вами. После отправки команды пользователем, он получит соответствующий ответ. В этой статье вы узнаете, как сделать интерактивные команды на PHP без использования каких-либо библиотек.
Для работы с интерактивными командами будет использоваться криптографический модуль Sodium, который включен по умолчанию в PHP, начиная с версии 7.2. Впрочем сейчас уже 2023 год и я надеюсь, что вы успели перейти на PHP 8. Давайте создадим 2 скрипта, чтобы реализовать слэш команды в Discord. Первый скрипт – это основной обработчик для этих команд, назовем его к примеру discord-commands.php
. А второй скрипт просто для того, чтобы регистрировать команды в Discord можно сказать, что он тестовый, назовем его discord-commands-manage.php
. Перед началом работы вам понадобятся данные приложения и бота из Discord Developer Portal. Добавьте в discord-commands-manage.php
ID вашего приложения, полученный на вкладке “General Information”:
$app_id = Application_ID_Here;
А в discord-commands.php
добавьте Public Key вашего приложения из той же вкладки:
$app_key = 'Public_Key_Here';
Затем перейдите на вкладку Bot и получите там токен. Обратите внимание, если вы уже используете бот, то не нужно нажимать “Reset Token” – просто скопируйте токен из других ваших скриптов, где работаете с ботом. Пропишите токен бота только в discord-commands-manage.php
.
$bot_token = 'Bot_Token_Here';
discord-commands-manage.php
Этот скрипт предназначен для того, чтобы добавлять или удалять слэш команды в Discord. Он содержит всего 2 функции и примеры их использования.
<?php
$app_id = Application_ID_Here;
$bot_token = 'Bot_Token_Here';
// добавляем команду /test с описанием "Тестовая команда"
AddCommand('test', 'Тестовая команда');
// ID команды всегда можно посмотреть в Discord, если у вас включен режим разработчика, но если вам зачем-то понадобится id прямо здесь, то используйте такой вариант:
// $cmd_id = AddCommand('test', 'Тестовая команда'); // в переменной $cmd_id будет находиться id созданной команды
// удаляем команду по ID (удалять можно только по ID команды)
DeleteCommand(1058231194334015609); // сюда просто вписываем длинный id команды
// функция для добавления команды
function AddCommand($name, $description) {
$api_url = 'https://discord.com/api/v10/applications/'.$GLOBALS['app_id'].'/commands';
$command = [
'name' => $name,
'type' => 1,
'description' => $description,
];
$ch = curl_init($api_url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($command));
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Authorization: Bot '.$GLOBALS['bot_token']));
$res = curl_exec($ch);
curl_close($ch);
return json_decode($res, true)['id'];
}
// функция для удаления команды
function DeleteCommand($id) {
$api_url = 'https://discord.com/api/v10/applications/'.$GLOBALS['app_id'].'/commands/'.$id;
$ch = curl_init($api_url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Authorization: Bot '.$GLOBALS['bot_token']));
curl_exec($ch);
curl_close($ch);
}
Теперь при помощи этого скрипта, добавьте любую тестовую команду в ваше приложение и получите ее ID, он нам понадобится в основном обработчике.
discord-commands.php
Этот скрипт будет отвечать на ваши слэш команды в Discord. Ниже представлен код скрипта с комментариями
<?php
$app_key = 'Public_Key_Here';
$input = file_get_contents('php://input'); // получаем данные входящего от Discord запроса
if (empty($input)) exit();
$result = endpointVerify($_SERVER, $input, $app_key); // выполняем верификацию
if (empty($result)) exit();
http_response_code($result['code']);
if ($result['data']['type'] == 1) { echo json_encode($result['data']); exit(); } // это нужно для проверочных запросов от Discord
// Обработка ваших команд
if ($result['data']['type'] == 2) {
$input_data = json_decode($input, true);
if ($input_data['data']['id'] == 1058231194334015609) { // сюда вписываете id команды /test
$answer = 'Это тестовый ответ на команду.'; // здесь пишете ответ, который пользователь получит при использовании команды
InteractionCallback($answer); // отправляете ответ
}
}
// Функции
function InteractionCallback($message) {
$data = ['type' => 4, 'data' => ['content' => $message]];
header('Content-Type: application/json');
exit(json_encode($data));
}
function endpointVerify(array $headers, string $data, string $publicKey): array
{
if (!isset($headers['HTTP_X_SIGNATURE_ED25519']) || !isset($headers['HTTP_X_SIGNATURE_TIMESTAMP'])) return ['code' => 401, 'data' => null];
$signature = $headers['HTTP_X_SIGNATURE_ED25519'];
$timestamp = $headers['HTTP_X_SIGNATURE_TIMESTAMP'];
if (!trim($signature, '0..9A..Fa..f') == '') return ['code' => 401, 'data' => null];
$message = $timestamp . $data;
$binarySignature = sodium_hex2bin($signature);
$binaryKey = sodium_hex2bin($publicKey);
if (!sodium_crypto_sign_verify_detached($binarySignature, $message, $binaryKey)) return ['code' => 401, 'data' => null];
$data = json_decode($data, true);
return match($data['type']) {
1 => ['code' => 200, 'data' => ['type' => 1]],
2 => ['code' => 200, 'data' => ['type' => 2]],
default => ['code' => 400, 'data' => null]
};
}
Если у вас PHP ниже 8 версии, функция endpointVerify
может вызвать ошибку даже при наличии Sodium. Если это случится, попробуйте использовать такой вариант функции:
function endpointVerify($headers, $data, $publicKey) {
if (!isset($headers['HTTP_X_SIGNATURE_ED25519']) || !isset($headers['HTTP_X_SIGNATURE_TIMESTAMP'])) return ['code' => 401, 'data' => null];
$signature = $headers['HTTP_X_SIGNATURE_ED25519'];
$timestamp = $headers['HTTP_X_SIGNATURE_TIMESTAMP'];
if (!trim($signature, '0..9A..Fa..f') == '') return ['code' => 401, 'data' => null];
$message = $timestamp . $data;
$binarySignature = sodium_hex2bin($signature);
$binaryKey = sodium_hex2bin($publicKey);
if (!sodium_crypto_sign_verify_detached($binarySignature, $message, $binaryKey)) return ['code' => 401, 'data' => null];
$data = json_decode($data, true);
switch ($data['type']) {
case 1: return ['code' => 200, 'data' => ['type' => 1]];
case 2: return ['code' => 200, 'data' => ['type' => 2]];
default: return ['code' => 400, 'data' => null];
}
}
Теперь в настройках вашего приложения на вкладке “General Information” вы должны указать ссылку до вашего скрипта discord-commands.php
в поле “Interactions Endpoint URL“. При сохранении настроек, Discord отправит проверочный запрос и если ваш обработчик ответит корректно, ссылка до него будет сохранена. В противном случае Discord выдаст ошибку.
Если вы заблаговременно добавили бот вашего приложения на свой сервер, а также успешно сохранили Interactions Endpoint URL, то теперь можете отправить команду /test
на вашем сервере и в ответ вы получите сообщение, которое прописано в коде обработчика.
Все описанное в этой статье прекрасно работает на Discord сервере нашего MSS Project. Конечно я еще не рассказал про эмбеды и кнопки, но это тянет на отдельную статью и я постараюсь как нибудь написать ее. Надеюсь материал был вам полезен. Удачи!