9March
Discord php чат бот с обучением
Давно хотел сделать чат бота для discord, чтобы он реагировал на фразы и отвечал в юморной форме. Для этого было решено сделать базу вопросов и ответов "знаний" бота, которая будет постоянно наполняться пользователями чата.
Ответов на фразу или слово может быть множество, также, как и ключевых фраз связанных с ответами. То есть бота можно научить отвечать по-разному на одну фразу или целую серию похожих фраз. Если в базе будет несколько ответов, то они будут выводиться случайно. Также для большей интерактивности бот сохраняет в виде ответа и ссылки с картинками или emoji.
Для работы бота нужен доступ к серверу с установленным php и mysql, бот очень простой, поэтому особой мощности не потребуется. Ниже код с комментариями.

Пример реакции бота на текст в чате.

Дополнение базы данных бота новыми ответами.
Инструкция для использования и настройки бота в чате:
Добавление или дополнение данных:
"ключ" "текст"
ключ; текст
Дополнение ключа к тексту:
номер_данных "ключ"
Номер данных выводится при запросе любого из ключей.
Список всего по ключу:
номер_ключа
Удаление всех данных:
удалить номер_данных
Удаление ключа:
удалить ключ номер_ключа
Редактирование ключа:
редактировать ключ номер_ключа "новый ключ"
Номер ключа можно узнать в списке ключей
Удаление текста:
удалить текст номер_текста
Редактирование текста:
редактировать текст номертекста "новыйтекст"
Номер текста можно узнать в списке текстов
Весь код проекта с комментариями:
Ответов на фразу или слово может быть множество, также, как и ключевых фраз связанных с ответами. То есть бота можно научить отвечать по-разному на одну фразу или целую серию похожих фраз. Если в базе будет несколько ответов, то они будут выводиться случайно. Также для большей интерактивности бот сохраняет в виде ответа и ссылки с картинками или emoji.
Для работы бота нужен доступ к серверу с установленным php и mysql, бот очень простой, поэтому особой мощности не потребуется. Ниже код с комментариями.

Пример реакции бота на текст в чате.

Дополнение базы данных бота новыми ответами.
Инструкция для использования и настройки бота в чате:
Добавление или дополнение данных:
"ключ" "текст"
ключ; текст
Дополнение ключа к тексту:
номер_данных "ключ"
Номер данных выводится при запросе любого из ключей.
Список всего по ключу:
номер_ключа
Удаление всех данных:
удалить номер_данных
Удаление ключа:
удалить ключ номер_ключа
Редактирование ключа:
редактировать ключ номер_ключа "новый ключ"
Номер ключа можно узнать в списке ключей
Удаление текста:
удалить текст номер_текста
Редактирование текста:
редактировать текст номертекста "новыйтекст"
Номер текста можно узнать в списке текстов
Весь код проекта с комментариями:
<?php
/*
Запуск бота только с консоли, для отладки в просмотра текста чата запускать так (после выхода с консоли скрипт прекратит работу):
php /var/www/discord_bot.php
Для запуска без отладки (после запуска консоль выдаст номер процесса, который можно использовать для его остановки в случае необходимости):
nohup php /var/www/discord_bot.php &
*/
#Так как бот запускается с консоли, для отладки ошибок кода можно вручную задать использование файла для лога ошибок
ini_set("log_errors", 1);
ini_set("log_errors_max_len", "1024");
ini_set("error_log", "/var/log/my_php_bot.log");
/*
Сначала нужно создать бота в discord: https://discord.com/developers/applications и подключить его к вашему чат серверу и включить режим разработчика в настройках дискорда, чтобы видеть id всех каналов чата
Для работы бота нужна библиотека discord_php: https://github.com/discord-php/DiscordPHP
Но так как мы не используем composer, нужно сконвертировать библиотеку перед использованием через https://php-download.com/
*/
require_once 'discord_php/autoload.php';
/*
Для простой работы с mysql нужна библиотека godb: https://github.com/vasa-c/godb-old которая требует mysqli в настройках php, также mb_string тк работаем с utf8
Также нужно создать базу данных для бота и создать две таблицы:
CREATE TABLE `texts` (
`text_n` int(10) NOT NULL,
`data_n` int(10) NOT NULL,
`text` text COLLATE utf8mb4_bin NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
CREATE TABLE `titles` (
`title_n` int(10) NOT NULL,
`data_n` int(10) NOT NULL,
`title` varchar(100) COLLATE utf8mb4_bin NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
ALTER TABLE `texts`
ADD PRIMARY KEY (`text_n`) USING BTREE,
ADD KEY `data_n` (`data_n`);
ALTER TABLE `texts` ADD FULLTEXT KEY `text` (`text`);
ALTER TABLE `titles`
ADD PRIMARY KEY (`title_n`),
ADD KEY `title` (`title`,`data_n`);
*/
require_once 'godb.php';
use Discord\Discord;
$discord = new Discord([
'token' => 'TOKEN бота',
'pmChannels' => true
]);
#Функция для очистки ненужных символов в начале и конце текста
function ClearSymbols($title)
{
return preg_replace('#[\!\?\.\,\(\)]+#', '', $title);
}
$discord->on('ready', function ($discord)
{
#Исполняется каждый раз, как в чат добавляется новое сообщение
$discord->on('message', function ($message, $discord)
{
#Подключение к базе данных
$db = new goDB("адрес_сервера", "имя_пользователя", "пароль", "название_базы");
$db->query("SET NAMES utf8mb4", array());
#Имя бота можно узнать с вывода в текста в консоль при обращении к боту, имя можно в дальнейшем использовать для команд
$bot_name = '\<@\!?450704734647484416\>';
#Но в данном случае обращение к боту просто удаляется с текста
$text = preg_replace('#' . $bot_name . ' #', '', $message->content);
$username = $message->author->username;
#id ветки в чате, где будуд работать команды добавления и редактирования бота
$bot_room = '811320121108725791';
#Настройка номера сервера чата
$guild = $discord->guilds->get('id', 'id_сервера');
$channel = $guild->channels->get('id', $message->channel->id);
$channel_id = $message->channel->id;
#Выключение бота
if($channel_id == $bot_room && $username == 'имя_администратора' && $message->content == 'die')
$discord->close();
#Вывод всего текста чата в консоль
echo "{$username}: {$message->content}" . "\n";
#Чтение чата, имя бота проверяется чтобы бот не циклился на бесконечное чтение и ответ на команду
if($username != 'имя_бота' && (mb_strlen($text) <= 40) && preg_match('#^(.{2,40})$#ui', $text, $res))
{
$title = trim(mb_strtolower($res[1]));
$title = ClearSymbols($title);
$temp = $db->query('SELECT data_n, text FROM titles LEFT JOIN texts USING(data_n) WHERE title=?', array($title), 'assoc');
if($temp)
{
$res = array();
foreach($temp as $val)
$res[] = $val['text'];
$res_index = array_rand($res);
#Если данные пришли с чат сервера
if($channel != '')
{
if($channel_id == $bot_room)
$channel->sendMessage('#' . $temp[0]['data_n'] . ' ' . $res[$res_index]);
else
$channel->sendMessage($res[array_rand($res)]);
}
else
{
#Если данные пришли с личных сообщений
$message->reply($res[array_rand($res)]);
}
}
}
#Показать всё по номеру данных
if($username != 'имя_бота' && $channel_id == $bot_room && preg_match('#^([0-9]{1,10})$#ui', $text, $res))
{
$data_n = $db->query('SELECT data_n FROM titles WHERE data_n=?i', array(mb_strtolower($res[1])), 'rowassoc');
if($data_n)
{
$titles = $db->query('SELECT title_n, title FROM titles WHERE data_n=?i', array(mb_strtolower($data_n['data_n'])), 'assoc');
$texts = $db->query('SELECT text_n, text FROM texts WHERE data_n=?i', array(mb_strtolower($data_n['data_n'])), 'assoc');
$data = 'Все ключи:' . "\n";
foreach($titles as $val)
$data .= '#' . $val['title_n'] . ' ' . $val['title'] . "\n";
$data .= "\n";
$data .= 'Все тексты по ключу:' . "\n";
foreach($texts as $val)
$data .= '#' . $val['text_n'] . ' ' . $val['text'] . "\n";
$channel->sendMessage($data);
}
}
#Запись
if($username != 'имя_бота' && $channel_id == $bot_room && (preg_match('#^"(.+)" "(.+)"#ui', $text, $res) || preg_match('#^(.+);(.+)#ui', $text, $res)))
{
$title = trim(mb_strtolower($res[1]));
$title = ClearSymbols($title);
$text = trim($res[2]);
if(mb_strlen($title) < 40)
{
#Если ключ уже есть
$title_check = $db->query('SELECT data_n FROM titles WHERE title=?', array($title), 'rowassoc');
if(!$title_check)
{
$last_data_n = $db->query('SELECT MAX(data_n)AS max_data_n FROM texts', array(), 'el');
$db->query('INSERT INTO titles SET data_n=?i, title=?', array($last_data_n + 1, $title));
$db->query('INSERT INTO texts SET data_n=?i, text=?', array($last_data_n + 1, $text));
$channel->sendMessage('добавлено: #' . ($last_data_n + 1) . ' ключ: "' . $title . '" данные: "' . $text . '"');
}
else
{
$db->query('INSERT INTO texts SET data_n=?i, text=?', array($title_check['data_n'], $text));
$channel->sendMessage('дополнено: #' . $title_check['data_n'] . ' данные: "' . $text . '"');
}
}
else
$channel->sendMessage('нужно меньше 40 символов в ключе');
}
#Добавить title
if($username != 'имя_бота' && $channel_id == $bot_room && preg_match('#^([0-9]+) "(.+)"#ui', $text, $res))
{
$data_n = $res[1];
$title = trim(mb_strtolower($res[2]));
$title = ClearSymbols($title);
if(mb_strlen($title) < 40)
{
$data_n_check = $db->query('SELECT data_n FROM titles WHERE data_n=?i', array($data_n), 'rowassoc');
if($data_n_check)
{
$title_check = $db->query('SELECT title FROM titles WHERE title=?', array($title), 'rowassoc');
if(!$title_check)
{
$db->query('INSERT INTO titles SET data_n=?i, title=?', array($data_n, $title));
$channel->sendMessage('дополнено: #' . $data_n . ' ' . ' ключ: "' . $title . '"');
}
else
$channel->sendMessage('такое ключ уже есть в: #' . $data_n);
}
}
else
$channel->sendMessage('нужно меньше 40 символов в ключе');
}
#Удаление данных
if($username != 'имя_бота' && $channel_id == $bot_room && preg_match('#^удалить данные ([0-9]+)$#ui', $text, $res))
{
$data_n = trim($res[1]);
$db->query('DELETE FROM titles WHERE data_n=?i', array($data_n));
$db->query('DELETE FROM texts WHERE data_n=?i', array($data_n));
$channel->sendMessage('данные #' . $data_n . ' удалёны');
}
#Удаление title
if($username != 'имя_бота' && $channel_id == $bot_room && preg_match('#^удалить ключ ([0-9]+)$#ui', $text, $res))
{
$title_n = trim($res[1]);
$db->query('DELETE FROM titles WHERE title_n=?i', array($title_n));
$channel->sendMessage('ключ #' . $title_n . ' удалён');
}
#Удаление text
if($username != 'имя_бота' && $channel_id == $bot_room && preg_match('#^удалить текст ([0-9]+)$#ui', $text, $res))
{
$text_n = trim($res[1]);
$db->query('DELETE FROM texts WHERE text_n=?i', array($text_n));
$channel->sendMessage('текст #' . $text_n . ' удалён');
}
#Редактирование title
if($username != 'имя_бота' && $channel_id == $bot_room && preg_match('#^редактировать ключ ([0-9]+) "(.+)"$#ui', $text, $res))
{
$title_n = trim($res[1]);
$title = trim(mb_strtolower($res[2]));
if(mb_strlen($title) < 40)
{
if(!$check = $db->query('SELECT title FROM titles WHERE title=?', array($title), 'rowassoc'))
{
$db->query('UPDATE titles SET title=? WHERE title_n=?i', array($title, $title_n));
$channel->sendMessage('ключ #' . $title_n . ' отредактирован');
}
else
$channel->sendMessage('такой ключ уже есть');
}
else
$channel->sendMessage('нужно меньше 40 символов в ключе');
}
#Редактирование text
if($username != 'имя_бота' && $channel_id == $bot_room && preg_match('#^редактировать текст ([0-9]+) "(.+)"$#ui', $text, $res))
{
$text_n = trim($res[1]);
$text = trim(mb_strtolower($res[2]));
$db->query('UPDATE texts SET text=? WHERE text_n=?i', array($text, $text_n));
$channel->sendMessage('текст #' . $text_n . ' отредактирован');
}
$db->close();
});
});
$discord->run();
?>
