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(); ?>