Skip to content

Commit

Permalink
Update 0.5.0: Fix !help, training restriction, some new functionality.
Browse files Browse the repository at this point in the history
  • Loading branch information
claabs committed Sep 22, 2018
1 parent 455c3e0 commit 6f3268d
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 32 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -65,3 +65,5 @@ config.json
# error output file
error.json
markovDB.json
.vscode/launch.json
.vscode/settings.json
89 changes: 84 additions & 5 deletions README.md
@@ -1,19 +1,95 @@
# 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
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
Create a file called `config.json` in the project directory with the contents:
### Setup
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",
"game":"\"!mark help\" for help",
"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
Huge refactor.
Added `!mark debug` which sends debug info alongside the message.
Expand All @@ -27,4 +103,7 @@ Added TTS support and random message attachments.
Deleted messages no longer persist in the database longer than 24 hours.

### 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.
91 changes: 66 additions & 25 deletions index.js
Expand Up @@ -2,6 +2,8 @@ const Discord = require('discord.js'); // https://discord.js.org/#/docs/main/sta
const fs = require('fs');
const Markov = require('markov-strings');
const schedule = require('node-schedule');
const { version } = require('./package.json');
const cfg = require('./config');

const client = new Discord.Client();
// const ZEROWIDTH_SPACE = String.fromCharCode(parseInt('200B', 16));
Expand Down Expand Up @@ -85,16 +87,23 @@ function regenMarkov() {
* Loads the config settings from disk
*/
function loadConfig() {
const cfgfile = 'config.json';
if (fs.existsSync(cfgfile)) {
const cfg = JSON.parse(fs.readFileSync(cfgfile, 'utf8'));
PREFIX = cfg.prefix;
GAME = cfg.game;
// regenMarkov()
client.login(cfg.token);
} else {
console.log(`Oh no!!! ${cfgfile} could not be found!`);
}
PREFIX = cfg.prefix;
GAME = cfg.game;
// regenMarkov()
client.login(cfg.token);
}

/**
* 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');
}

/**
Expand All @@ -120,6 +129,8 @@ function validateMessage(message) {
command = 'invite';
} else if (split[1] === 'debug') {
command = 'debug';
} else if (split[1] === 'tts') {
command = 'tts';
}
}
return command;
Expand Down Expand Up @@ -170,17 +181,39 @@ async function fetchMessages(message) {
* General Markov-chain response function
* @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} 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...');
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);
const messageOpts = { tts: message.tts };
const randomMessage = markovDB[Math.floor(Math.random() * markovDB.length)];
console.log('Random Message:', randomMessage);
if (Object.prototype.hasOwnProperty.call(randomMessage, 'attachment')) {
messageOpts.files = [{ attachment: randomMessage.attachment }];
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)];
if (Object.prototype.hasOwnProperty.call(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);
if (debug) message.channel.send(`\`\`\`\n${JSON.stringify(result, null, 2)}\n\`\`\``);
}).catch((err) => {
Expand Down Expand Up @@ -213,7 +246,6 @@ client.on('message', (message) => {
if (message.guild) {
const command = validateMessage(message);
if (command === 'help') {
console.log(message.channel);
const richem = new Discord.RichEmbed()
.setAuthor(client.user.username, client.user.avatarURL)
.setThumbnail(client.user.avatarURL)
Expand All @@ -226,22 +258,28 @@ client.on('message', (message) => {
+ '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 '
+ '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.author.send(richem);
});
}
if (command === 'train') {
console.log('Training...');
fileObj = {
messages: [],
};
fs.writeFileSync('markovDB.json', JSON.stringify(fileObj), 'utf-8');
fetchMessages(message);
if (isModerator(message)) {
console.log('Training...');
fileObj = {
messages: [],
};
fs.writeFileSync('markovDB.json', JSON.stringify(fileObj), 'utf-8');
fetchMessages(message);
}
}
if (command === 'respond') {
generateResponse(message);
}
if (command === 'tts') {
generateResponse(message, false, true);
}
if (command === 'debug') {
generateResponse(message, true);
}
Expand All @@ -260,6 +298,9 @@ client.on('message', (message) => {
dbObj.attachment = message.attachments.values().next().value.url;
}
messageCache.push(dbObj);
if (message.isMentioned(client.user)) {
generateResponse(message);
}
}
}
if (command === inviteCmd) {
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "markbot",
"version": "0.4.0",
"version": "0.5.0",
"description": "A conversational Markov chain bot for Discord",
"main": "index.js",
"scripts": {
Expand Down

0 comments on commit 6f3268d

Please sign in to comment.