mirror of
https://github.com/pacnpal/markov-discord.git
synced 2025-12-20 11:01:04 -05:00
Update 0.5.0: Fix !help, training restriction, some new functionality.
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -65,3 +65,5 @@ config.json
|
|||||||
# error output file
|
# error output file
|
||||||
error.json
|
error.json
|
||||||
markovDB.json
|
markovDB.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/settings.json
|
||||||
|
|||||||
87
README.md
87
README.md
@@ -1,19 +1,95 @@
|
|||||||
# MarkBot for Discord
|
# MarkBot for Discord
|
||||||
This is a super rough prototype. A Markov chain bot using markov-strings. Just uploading here so it can run and generate training data.
|
A Markov chain bot using markov-strings.
|
||||||
|
|
||||||
# Setup
|
# Setup
|
||||||
|
First, create a [Discord bot application](https://discordapp.com/developers/applications/).
|
||||||
|
## Windows
|
||||||
|
### Requirements
|
||||||
|
* [Node.js 8.0+ (Current)](https://nodejs.org/en/download/current/)
|
||||||
|
|
||||||
## Configuration
|
### Setup
|
||||||
Create a file called `config.json` in the project directory with the contents:
|
1. Install Node.js 8.0 or newer.
|
||||||
|
1. Download this repository using git in a command prompt
|
||||||
|
```cmd
|
||||||
|
git clone https://github.com/charlocharlie/markov-discord.git
|
||||||
|
```
|
||||||
|
or by just downloading and extracting the [project zip](https://github.com/charlocharlie/markov-discord/archive/master.zip) from GitHub.
|
||||||
|
1. Open a command prompt in the `markov-discord` folder.
|
||||||
|
```sh
|
||||||
|
# Install Windows build tools
|
||||||
|
npm install --global --production windows-build-tools
|
||||||
|
# NPM install non-development packages
|
||||||
|
npm install --production
|
||||||
|
```
|
||||||
|
1. Create a file called `config.json` in the project directory with the contents:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"prefix":"!mark",
|
||||||
|
"game":"\"!mark help\" for help",
|
||||||
|
"token":"k5NzE2NDg1MTIwMjc0ODQ0Nj.DSnXwg.ttNotARealToken5p3WfDoUxhiH"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Feel free to change the command prefix, game display. Add your bot token.
|
||||||
|
1. Run the bot:
|
||||||
|
```sh
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Debian Linux
|
||||||
|
### Requirements
|
||||||
|
* Node.js 8.0+
|
||||||
|
* Python 2.7 (for erlpack)
|
||||||
|
* C++ build tools (for erlpack)
|
||||||
|
|
||||||
|
### Download
|
||||||
|
```sh
|
||||||
|
# Clone this repository
|
||||||
|
git clone https://github.com/charlocharlie/markov-discord.git
|
||||||
|
cd markov-discord
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Configure
|
||||||
|
Create a file called `config.json` in the project directory with the contents:
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"prefix":"!mark",
|
"prefix":"!mark",
|
||||||
"game":"\"!mark help\" for help",
|
"game":"\"!mark help\" for help",
|
||||||
"token":"k5NzE2NDg1MTIwMjc0ODQ0Nj.DSnXwg.ttNotARealToken5p3WfDoUxhiH"
|
"token":"k5NzE2NDg1MTIwMjc0ODQ0Nj.DSnXwg.ttNotARealToken5p3WfDoUxhiH"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
Feel free to change the command prefix, game display. Add your bot token.
|
||||||
|
|
||||||
|
### Install and Run
|
||||||
|
```sh
|
||||||
|
# Install Node.js if you haven't already
|
||||||
|
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
|
||||||
|
nvm install node
|
||||||
|
|
||||||
|
# NPM install non-development packages
|
||||||
|
npm install --production
|
||||||
|
|
||||||
|
# If you run into build errors, install the following packages:
|
||||||
|
sudo apt-get install python -y
|
||||||
|
sudo apt-get install build-essential -y
|
||||||
|
|
||||||
|
# Start the program
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Changelog
|
||||||
|
### 0.5.0
|
||||||
|
Fixed bug where `!mark help` didn't work.
|
||||||
|
Only admins can train.
|
||||||
|
The bot responds when mentioned.
|
||||||
|
The bot cannot mention @everyone.
|
||||||
|
Added version number to help.
|
||||||
|
Added `!mark tts` for a quieter TTS response.
|
||||||
|
Readme overhaul.
|
||||||
|
Simpler config loading.
|
||||||
|
|
||||||
## Changelog
|
|
||||||
### 0.4.0
|
### 0.4.0
|
||||||
Huge refactor.
|
Huge refactor.
|
||||||
Added `!mark debug` which sends debug info alongside the message.
|
Added `!mark debug` which sends debug info alongside the message.
|
||||||
@@ -28,3 +104,6 @@ Deleted messages no longer persist in the database longer than 24 hours.
|
|||||||
|
|
||||||
### 0.2.0
|
### 0.2.0
|
||||||
Updated training algorithm and data structure.
|
Updated training algorithm and data structure.
|
||||||
|
|
||||||
|
# Thanks
|
||||||
|
Thanks to [BotMaker-for-Discord](https://github.com/CorySanin/BotMaker-for-Discord) which I used as a reference when during development.
|
||||||
65
index.js
65
index.js
@@ -2,6 +2,8 @@ const Discord = require('discord.js'); // https://discord.js.org/#/docs/main/sta
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const Markov = require('markov-strings');
|
const Markov = require('markov-strings');
|
||||||
const schedule = require('node-schedule');
|
const schedule = require('node-schedule');
|
||||||
|
const { version } = require('./package.json');
|
||||||
|
const cfg = require('./config');
|
||||||
|
|
||||||
const client = new Discord.Client();
|
const client = new Discord.Client();
|
||||||
// const ZEROWIDTH_SPACE = String.fromCharCode(parseInt('200B', 16));
|
// const ZEROWIDTH_SPACE = String.fromCharCode(parseInt('200B', 16));
|
||||||
@@ -85,16 +87,23 @@ function regenMarkov() {
|
|||||||
* Loads the config settings from disk
|
* Loads the config settings from disk
|
||||||
*/
|
*/
|
||||||
function loadConfig() {
|
function loadConfig() {
|
||||||
const cfgfile = 'config.json';
|
|
||||||
if (fs.existsSync(cfgfile)) {
|
|
||||||
const cfg = JSON.parse(fs.readFileSync(cfgfile, 'utf8'));
|
|
||||||
PREFIX = cfg.prefix;
|
PREFIX = cfg.prefix;
|
||||||
GAME = cfg.game;
|
GAME = cfg.game;
|
||||||
// regenMarkov()
|
// regenMarkov()
|
||||||
client.login(cfg.token);
|
client.login(cfg.token);
|
||||||
} else {
|
}
|
||||||
console.log(`Oh no!!! ${cfgfile} could not be found!`);
|
|
||||||
}
|
/**
|
||||||
|
* Checks if the author of a message as moderator-like permissions.
|
||||||
|
* @param {Message} message Message object to get the sender of the message.
|
||||||
|
* @return {Boolean} True if the sender is a moderator.
|
||||||
|
*/
|
||||||
|
function isModerator(message) {
|
||||||
|
const { member } = message;
|
||||||
|
return member.hasPermission('ADMINISTRATOR')
|
||||||
|
|| member.hasPermission('MANAGE_CHANNELS')
|
||||||
|
|| member.hasPermission('KICK_MEMBERS')
|
||||||
|
|| member.hasPermission('MOVE_MEMBERS');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -120,6 +129,8 @@ function validateMessage(message) {
|
|||||||
command = 'invite';
|
command = 'invite';
|
||||||
} else if (split[1] === 'debug') {
|
} else if (split[1] === 'debug') {
|
||||||
command = 'debug';
|
command = 'debug';
|
||||||
|
} else if (split[1] === 'tts') {
|
||||||
|
command = 'tts';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return command;
|
return command;
|
||||||
@@ -170,17 +181,39 @@ async function fetchMessages(message) {
|
|||||||
* General Markov-chain response function
|
* General Markov-chain response function
|
||||||
* @param {Message} message The message that invoked the action, used for channel info.
|
* @param {Message} message The message that invoked the action, used for channel info.
|
||||||
* @param {Boolean} debug Sends debug info as a message if true.
|
* @param {Boolean} debug Sends debug info as a message if true.
|
||||||
|
* @param {Boolean} tts If the message should be sent as TTS. Defaults to the TTS setting of the
|
||||||
|
* invoking message.
|
||||||
|
* @param {Array<String>} filterWords Array of words that the message generated will be filtered on.
|
||||||
*/
|
*/
|
||||||
function generateResponse(message, debug = false) {
|
function generateResponse(message, debug = false, tts = message.tts, filterWords) {
|
||||||
console.log('Responding...');
|
console.log('Responding...');
|
||||||
markov.generateSentence().then((result) => {
|
const options = {};
|
||||||
|
if (filterWords) {
|
||||||
|
options.filter = (result) => {
|
||||||
|
for (let i = 0; i < filterWords.length; i++) {
|
||||||
|
if (result.string.includes(filterWords[i])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
options.maxTries = 5000;
|
||||||
|
}
|
||||||
|
markov.generateSentence(options).then((result) => {
|
||||||
console.log('Generated Result:', result);
|
console.log('Generated Result:', result);
|
||||||
const messageOpts = { tts: message.tts };
|
const messageOpts = { tts };
|
||||||
|
const attachmentRefs = result.refs.filter(ref => Object.prototype.hasOwnProperty.call(ref, 'attachment'));
|
||||||
|
if (attachmentRefs.length > 0) {
|
||||||
|
const randomRef = attachmentRefs[Math.floor(Math.random() * attachmentRefs.length)];
|
||||||
|
messageOpts.files = [{ attachment: randomRef.attachment }];
|
||||||
|
} else {
|
||||||
const randomMessage = markovDB[Math.floor(Math.random() * markovDB.length)];
|
const randomMessage = markovDB[Math.floor(Math.random() * markovDB.length)];
|
||||||
console.log('Random Message:', randomMessage);
|
|
||||||
if (Object.prototype.hasOwnProperty.call(randomMessage, 'attachment')) {
|
if (Object.prototype.hasOwnProperty.call(randomMessage, 'attachment')) {
|
||||||
messageOpts.files = [{ attachment: randomMessage.attachment }];
|
messageOpts.files = [{ attachment: randomMessage.attachment }];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.string.replace(/@everyone/g, '@everyοne'); // Replace @everyone with a homoglyph 'o'
|
||||||
message.channel.send(result.string, messageOpts);
|
message.channel.send(result.string, messageOpts);
|
||||||
if (debug) message.channel.send(`\`\`\`\n${JSON.stringify(result, null, 2)}\n\`\`\``);
|
if (debug) message.channel.send(`\`\`\`\n${JSON.stringify(result, null, 2)}\n\`\`\``);
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
@@ -213,7 +246,6 @@ client.on('message', (message) => {
|
|||||||
if (message.guild) {
|
if (message.guild) {
|
||||||
const command = validateMessage(message);
|
const command = validateMessage(message);
|
||||||
if (command === 'help') {
|
if (command === 'help') {
|
||||||
console.log(message.channel);
|
|
||||||
const richem = new Discord.RichEmbed()
|
const richem = new Discord.RichEmbed()
|
||||||
.setAuthor(client.user.username, client.user.avatarURL)
|
.setAuthor(client.user.username, client.user.avatarURL)
|
||||||
.setThumbnail(client.user.avatarURL)
|
.setThumbnail(client.user.avatarURL)
|
||||||
@@ -226,12 +258,14 @@ client.on('message', (message) => {
|
|||||||
+ 'this before shutting down to avoid any data loss. This automatically runs at midnight.')
|
+ 'this before shutting down to avoid any data loss. This automatically runs at midnight.')
|
||||||
.addField('!mark invite', 'Don\'t invite this bot to other servers. The database is shared '
|
.addField('!mark invite', 'Don\'t invite this bot to other servers. The database is shared '
|
||||||
+ 'between all servers and text channels.')
|
+ 'between all servers and text channels.')
|
||||||
.addBlankField('!mark debug', 'Runs the !mark command and follows it up with debug info.');
|
.addField('!mark debug', 'Runs the !mark command and follows it up with debug info.')
|
||||||
|
.setFooter(`Markov Discord v${version} by Charlie Laabs`);
|
||||||
message.channel.send(richem).catch(() => {
|
message.channel.send(richem).catch(() => {
|
||||||
message.author.send(richem);
|
message.author.send(richem);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (command === 'train') {
|
if (command === 'train') {
|
||||||
|
if (isModerator(message)) {
|
||||||
console.log('Training...');
|
console.log('Training...');
|
||||||
fileObj = {
|
fileObj = {
|
||||||
messages: [],
|
messages: [],
|
||||||
@@ -239,9 +273,13 @@ client.on('message', (message) => {
|
|||||||
fs.writeFileSync('markovDB.json', JSON.stringify(fileObj), 'utf-8');
|
fs.writeFileSync('markovDB.json', JSON.stringify(fileObj), 'utf-8');
|
||||||
fetchMessages(message);
|
fetchMessages(message);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (command === 'respond') {
|
if (command === 'respond') {
|
||||||
generateResponse(message);
|
generateResponse(message);
|
||||||
}
|
}
|
||||||
|
if (command === 'tts') {
|
||||||
|
generateResponse(message, false, true);
|
||||||
|
}
|
||||||
if (command === 'debug') {
|
if (command === 'debug') {
|
||||||
generateResponse(message, true);
|
generateResponse(message, true);
|
||||||
}
|
}
|
||||||
@@ -260,6 +298,9 @@ client.on('message', (message) => {
|
|||||||
dbObj.attachment = message.attachments.values().next().value.url;
|
dbObj.attachment = message.attachments.values().next().value.url;
|
||||||
}
|
}
|
||||||
messageCache.push(dbObj);
|
messageCache.push(dbObj);
|
||||||
|
if (message.isMentioned(client.user)) {
|
||||||
|
generateResponse(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (command === inviteCmd) {
|
if (command === inviteCmd) {
|
||||||
|
|||||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "markbot",
|
"name": "markbot",
|
||||||
"version": "0.4.0",
|
"version": "0.5.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "markbot",
|
"name": "markbot",
|
||||||
"version": "0.4.0",
|
"version": "0.5.0",
|
||||||
"description": "A conversational Markov chain bot for Discord",
|
"description": "A conversational Markov chain bot for Discord",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
Reference in New Issue
Block a user