v0.7.0
This commit is contained in:
58
bot.js
Normal file
58
bot.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/** __ __ __ ___ ___
|
||||||
|
* / ' / \ |__) | |__ \_/
|
||||||
|
* \__. \__/ | \ | |___ / \
|
||||||
|
*
|
||||||
|
* @copyright © 2021 hepller
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Импорт модулей и функций
|
||||||
|
const {VK} = require('vk-io')
|
||||||
|
const Markov = require('markov-generator')
|
||||||
|
|
||||||
|
// Конфиг, паттерн и файл проекта
|
||||||
|
const config = require('./config')
|
||||||
|
const data = require(`./data`)
|
||||||
|
const project = require('./package')
|
||||||
|
|
||||||
|
/** VK API */
|
||||||
|
const vk = new VK({token: config.token, v: 5.131, apiLimit: 1})
|
||||||
|
|
||||||
|
// Префиксы для логирования
|
||||||
|
const succes_prefix = '\u001B[32m>\u001B[0m'
|
||||||
|
const warn_prefix = '\u001B[33m>\u001B[0m'
|
||||||
|
const error_prefix = '\u001B[31m>\u001B[0m'
|
||||||
|
|
||||||
|
// Сообщения при запуске
|
||||||
|
console.log(succes_prefix, `Cortex Bot v${project.version}`)
|
||||||
|
console.log(succes_prefix, 'VK API connection ...')
|
||||||
|
|
||||||
|
// Запуск обработчика событий ВК
|
||||||
|
vk.updates.startPolling().then(() => console.log(succes_prefix, 'Successfully connected'))
|
||||||
|
|
||||||
|
// Обработчик сообщений ВК
|
||||||
|
vk.updates.on('message_new', async ctx => {
|
||||||
|
|
||||||
|
// Игнорирование лишних сообщений
|
||||||
|
if (!ctx.isChat || ctx.isOutbox || ctx.isGroup) return
|
||||||
|
|
||||||
|
// Генерация сообщения с определенным шансом
|
||||||
|
if (Math.random() * 100 < config.chance) {
|
||||||
|
|
||||||
|
// Статус набора текста
|
||||||
|
ctx.setActivity()
|
||||||
|
|
||||||
|
/** Сгенерированный текст */
|
||||||
|
const sentence = new Markov({input: data, minLenght: config.min_words}).makeChain()
|
||||||
|
|
||||||
|
// Предупреждение при превышении лимита символов
|
||||||
|
if (sentence.split('').length > config.max_symbols) return console.warn(warn_prefix, `#${ctx.chatId} Symbols limit exceeded (${sentence.split('').length}/${config.max_symbols})`)
|
||||||
|
|
||||||
|
// Отправка сообщения
|
||||||
|
ctx.send(sentence)
|
||||||
|
.then(() => console.log(succes_prefix, `#${ctx.chatId} Text generated`))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Обработка возможных ошибок
|
||||||
|
process.on('uncaughtException', error => console.error(error_prefix, `Error: ${error.message}`))
|
||||||
|
process.on('unhandledRejection', error => console.error(error_prefix, `Error: ${error.message}`))
|
||||||
6
config.json
Normal file
6
config.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"token": "TOKEN",
|
||||||
|
"chance": 10,
|
||||||
|
"min_words": 2,
|
||||||
|
"max_symbols": 250
|
||||||
|
}
|
||||||
31
config.yml
31
config.yml
@@ -1,31 +0,0 @@
|
|||||||
# __ __ __ ___ ___
|
|
||||||
# / ' / \ |__) | |__ \_/
|
|
||||||
# \__. \__/ | \ | |___ / \
|
|
||||||
|
|
||||||
# VK API
|
|
||||||
vk_api:
|
|
||||||
# Токен
|
|
||||||
token: TOKEN
|
|
||||||
|
|
||||||
# Основное
|
|
||||||
general:
|
|
||||||
# Шанс генерации сообщения
|
|
||||||
chance: 20
|
|
||||||
# Минимальное кол-во слов в тексте
|
|
||||||
min_words: 5
|
|
||||||
# Максимальное кол-во символов в тексте
|
|
||||||
max_symbols: 250
|
|
||||||
# ID бесед, в которых бот активен
|
|
||||||
active_chats: all
|
|
||||||
# Сохранение сообщений пользователей
|
|
||||||
# * Одинаковые строки не сохраняются
|
|
||||||
data_save: true
|
|
||||||
|
|
||||||
# Формат текста
|
|
||||||
format:
|
|
||||||
# Строчные буквы
|
|
||||||
to_lower_case: true
|
|
||||||
# Заглавная буква в начале
|
|
||||||
capitalize_word: true
|
|
||||||
# Точка в конце
|
|
||||||
end_dot: true
|
|
||||||
134
core/core.js
134
core/core.js
@@ -1,134 +0,0 @@
|
|||||||
/** __ __ __ ___ ___
|
|
||||||
* / ' / \ |__) | |__ \_/
|
|
||||||
* \__. \__/ | \ | |___ / \
|
|
||||||
*
|
|
||||||
* @copyright © 2021 hepller
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Импорт модулей
|
|
||||||
import {VK} from 'vk-io'
|
|
||||||
import YAML from 'yaml'
|
|
||||||
import Markov from 'markov-generator'
|
|
||||||
import fs from 'fs'
|
|
||||||
import Logger from './logger.js'
|
|
||||||
|
|
||||||
/** Конфиг */
|
|
||||||
let config = YAML.parse(fs.readFileSync('config.yml', 'utf8'))
|
|
||||||
|
|
||||||
// БД и файл проекта
|
|
||||||
const db = JSON.parse(fs.readFileSync('data.json', 'utf8'))
|
|
||||||
const project = JSON.parse(fs.readFileSync('package.json', 'utf8'))
|
|
||||||
|
|
||||||
/** VK API */
|
|
||||||
const vk = new VK({token: config.vk_api.token, v: 5.130, apiLimit: 1})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Сохраняет БД
|
|
||||||
* @param {object} data БД
|
|
||||||
*/
|
|
||||||
db.write = (data) => {
|
|
||||||
fs.writeFileSync('data.json', JSON.stringify(data, null, '\t'))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Получает объект чата из БД
|
|
||||||
* @param {number} id ID чата
|
|
||||||
*/
|
|
||||||
db.getChat = (id) => {
|
|
||||||
|
|
||||||
// Поиск чата по ID
|
|
||||||
let chat = db.chats.find(chat => chat.id == id)
|
|
||||||
|
|
||||||
// Создание нового объекта чата при отсутствии
|
|
||||||
if (!chat) {
|
|
||||||
|
|
||||||
// Добавление в БД
|
|
||||||
db.chats.push({id: id, data: []})
|
|
||||||
|
|
||||||
// Запись БД
|
|
||||||
db.write(db)
|
|
||||||
|
|
||||||
// Получение из БД
|
|
||||||
chat = db.chats.find(chat => chat.id == id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Возвращение объекта чата
|
|
||||||
return chat
|
|
||||||
}
|
|
||||||
|
|
||||||
// Сообщения при запуске
|
|
||||||
Logger.info(`Cortex Bot v${project.version}`)
|
|
||||||
Logger.info('Подключение к VK API ...')
|
|
||||||
|
|
||||||
// Перезагрузка конфигурации при её изменении
|
|
||||||
fs.watchFile('config.yml', async () => {
|
|
||||||
|
|
||||||
// Парсинг
|
|
||||||
config = YAML.parse(fs.readFileSync('config.yml', 'utf8'))
|
|
||||||
|
|
||||||
// Запись в лог
|
|
||||||
Logger.info('Конфигурация перезагружена')
|
|
||||||
})
|
|
||||||
|
|
||||||
// Старт получения обновлений ВК
|
|
||||||
vk.updates.startPolling().then(() => Logger.info('VK API подключено'))
|
|
||||||
|
|
||||||
// Обработчик сообщений ВК
|
|
||||||
vk.updates.on('message_new', async ctx => {
|
|
||||||
|
|
||||||
// Игнорирование лишних сообщений
|
|
||||||
if (!ctx.isChat) return
|
|
||||||
if (config.general.active_chats != 'all' && !config.general.active_chats.includes(ctx.chatId)) return
|
|
||||||
|
|
||||||
// Логирование сообщений
|
|
||||||
Logger.info(`#${ctx.chatId} ${ctx.senderId} ${ctx.isOutbox ? '<-' : '->'} ${ctx?.text ? ctx.text : '<no_text>'} ${ctx?.attachments}`)
|
|
||||||
|
|
||||||
// Завершение функции, если сообщение исходящее или от сообщества
|
|
||||||
if (ctx.isOutbox || ctx.isGroup) return
|
|
||||||
|
|
||||||
// Объект пользователя
|
|
||||||
ctx.chat = await db.getChat(ctx.chatId)
|
|
||||||
|
|
||||||
// Запись сообщения в БД
|
|
||||||
if (ctx.text && config.general.data_save && !ctx.chat.data.includes(ctx.text)) {
|
|
||||||
|
|
||||||
// Запись в БД
|
|
||||||
ctx.chat.data.push(ctx.text)
|
|
||||||
|
|
||||||
// Сохранение БД
|
|
||||||
db.write(db)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Генерация сообщения с определенным шансом
|
|
||||||
if (Math.random() * 100 < config.general.chance) {
|
|
||||||
|
|
||||||
// Сообщение в консоль о генерации текста
|
|
||||||
Logger.info('Генерация текста ...')
|
|
||||||
|
|
||||||
// Генерация текста
|
|
||||||
// Если данных будет недостаточно произойдет ошибка <<Maximum call stack size exceeded>>
|
|
||||||
let sentence = new Markov({input: ctx.chat.data, minLenght: config.general.min_words}).makeChain()
|
|
||||||
|
|
||||||
// Форматирование текста
|
|
||||||
if (config.format.to_lower_case) sentence = sentence.toLowerCase()
|
|
||||||
if (config.format.capitalize_word) sentence = sentence.charAt(0).toUpperCase() + sentence.slice(1)
|
|
||||||
if (config.format.end_dot) sentence = sentence + '.'
|
|
||||||
|
|
||||||
// Предупреждение при превышении лимита символов
|
|
||||||
if (sentence.split('').length > config.general.max_symbols) return Logger.warn(`#${ctx.chatId} Превышен лимит символов (${sentence.split('').length}/${config.general.max_symbols})`)
|
|
||||||
|
|
||||||
// Статус набора текста
|
|
||||||
await ctx.setActivity()
|
|
||||||
|
|
||||||
// Задержка перед отправкой сообщений
|
|
||||||
// Высчитывается на основе количества символов в тексте для имитации написания
|
|
||||||
await new Promise(resolve => setTimeout(resolve, sentence.split('').length * 2 + '00'))
|
|
||||||
|
|
||||||
// Отправка сообщения
|
|
||||||
ctx.send(sentence)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Обработчики ошибок
|
|
||||||
process.on('uncaughtException', error => Logger.error(error))
|
|
||||||
process.on('unhandledRejection', error => Logger.error(error))
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
/** __ __ __ ___ ___
|
|
||||||
* / ' / \ |__) | |__ \_/
|
|
||||||
* \__. \__/ | \ | |___ / \
|
|
||||||
*
|
|
||||||
* @copyright © 2021 hepller
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** Получает текущее время */
|
|
||||||
const getTimeString = () => {
|
|
||||||
|
|
||||||
// Переменные
|
|
||||||
let hours = new Date().getHours()
|
|
||||||
let minutes = new Date().getMinutes()
|
|
||||||
let seconds = new Date().getSeconds()
|
|
||||||
|
|
||||||
// Исправление одинарных символов
|
|
||||||
if (hours < 10) hours = `0${hours}`
|
|
||||||
if (minutes < 10) minutes = `0${minutes}`
|
|
||||||
if (seconds < 10) seconds = `0${seconds}`
|
|
||||||
|
|
||||||
// Возвращение строки
|
|
||||||
return `${hours}:${minutes}:${seconds}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Логер */
|
|
||||||
export default class Logger {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Выводит в лог информацию
|
|
||||||
* @param {string} text - Текст сообщения
|
|
||||||
*/
|
|
||||||
static info(text) {
|
|
||||||
console.log(`${getTimeString()} INFO - ${text}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Выводит в лог предупреждения
|
|
||||||
* @param {string} text - Текст предупреждения
|
|
||||||
*/
|
|
||||||
static warn(text) {
|
|
||||||
console.log(`\u001B[33m${getTimeString()} WARN - ${text}\u001B[0m`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Выводит в лог ошибки
|
|
||||||
* @param {string} text - Текст ошибки
|
|
||||||
*/
|
|
||||||
static error(text) {
|
|
||||||
console.log(`\u001B[31m${getTimeString()} ERRO - ${text}\u001B[0m`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
15
package.json
15
package.json
@@ -1,12 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "cortex-bot",
|
"name": "cortex-bot",
|
||||||
"version": "0.6.5",
|
"version": "0.7.0",
|
||||||
"description": "vk delirium generator bot",
|
"description": "vk text generator bot",
|
||||||
"main": "core/core.js",
|
"main": "bot.js",
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"start": "node ."
|
|
||||||
},
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/hepller/cortex-bot.git"
|
"url": "git+https://github.com/hepller/cortex-bot.git"
|
||||||
@@ -15,7 +11,7 @@
|
|||||||
"vk",
|
"vk",
|
||||||
"bot",
|
"bot",
|
||||||
"markov",
|
"markov",
|
||||||
"delirium",
|
"messages",
|
||||||
"generator"
|
"generator"
|
||||||
],
|
],
|
||||||
"author": "hepller",
|
"author": "hepller",
|
||||||
@@ -26,7 +22,6 @@
|
|||||||
"homepage": "https://github.com/hepller/cortex-bot#readme",
|
"homepage": "https://github.com/hepller/cortex-bot#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"markov-generator": "^1.2.3",
|
"markov-generator": "^1.2.3",
|
||||||
"vk-io": "^4.3.0",
|
"vk-io": "^4.4.0"
|
||||||
"yaml": "^1.10.2"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
37
readme.md
37
readme.md
@@ -1,12 +1,43 @@
|
|||||||
# Cortex Bot
|
# Cortex Bot
|
||||||
|
|
||||||
Бот ВКонтакте для генерации случайных сообщений на основе цепей Маркова
|
Бот ВКонтакте для генерации сообщений с помощью цепей Маркова
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Важная информация
|
## Конфигурация
|
||||||
|
|
||||||
При недостатке данных для генерации может возникать ошибка `Maximum call stack size exceeded`
|
```json
|
||||||
|
{
|
||||||
|
"token": "TOKEN", // Токен доступа VK API
|
||||||
|
"chance": 10, // Шанс генерации сообщения
|
||||||
|
"min_words": 2, // Минимальное кол-во слов в сгенерированном сообщении
|
||||||
|
"max_symbols": 200, // Максимальное кол-во символов в сообщении
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Данные
|
||||||
|
|
||||||
|
Перед началом использования загрузите свой датасет с достаточным объемом текста в файл `data.json`
|
||||||
|
|
||||||
|
**Пример небольшого датасета:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
"Я вот подумал. Если быстро заморозить человека, то в какой момент он умрет?",
|
||||||
|
"есть еще три:\n\nгносеоногия (эпистемология) - о познании",
|
||||||
|
"логика - о мышлении (из нее как ы слеуют большинство наук естественных и абстрактных (базируются на логических законах)",
|
||||||
|
"ну вот тут открытый же вопрос. С т.з. медицины: есть критерии естественной смерти, которые гуглятся",
|
||||||
|
"а с т.з. онтологии - тут может готового ответа и не быть. В этом как бы и отличие - очень часто нет готовых и четких ответов",
|
||||||
|
"ну, например, когда между нейронами сигналы перестанут бегать",
|
||||||
|
"Читаю, но как бы там все неолнозначно",
|
||||||
|
"клеточная смерть - не очень катит, т.к. те же волосы и ногти долго еще растут",
|
||||||
|
"Пульса нету, сердечной активности нету"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
_При недостатке данных для генерации может возникать ошибка `Maximum call stack size exceeded`_
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user