Update 0.6.0: Docker support

This commit is contained in:
Charlie Laabs
2019-05-25 15:05:37 -05:00
parent 6f3268dc07
commit 3459dd7413
8 changed files with 533 additions and 532 deletions

6
.dockerignore Normal file
View File

@@ -0,0 +1,6 @@
# Ignore everything
**/*
# Allow files and directories
!*.js
!*.json

18
Dockerfile Normal file
View File

@@ -0,0 +1,18 @@
FROM keymetrics/pm2:latest-stretch
# Create app directory
WORKDIR /usr/src/markbot
# Install app dependencies
COPY package*.json ./
# If you are building your code for production
ENV NPM_CONFIG_LOGLEVEL warn
RUN npm ci --only=production
# Bundle app source
COPY . .
RUN mkdir config
RUN ls -al
CMD [ "pm2-runtime", "start", "ecosystem.config.js" ]

View File

@@ -77,33 +77,61 @@ sudo apt-get install build-essential -y
npm start npm start
``` ```
## Docker
### Setup with source
1. Install Docker for your OS.
1. Download this repository using git in a command prompt
```sh
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 and run this one-liner:
```sh
docker run --rm -e TOKEN=YOUR.BOT.TOKEN -v config:/usr/src/markbot/config -it $(docker build -q .)
# Be patient as the build output is suppressed
```
### Setup with Docker Hub image
1. Install Docker for your OS.
1. Open a command prompts and run this one-liner:
```sh
docker build https://github.com/charlocharlie/markov-discord.git
```
# Changelog # Changelog
### 0.6.0
* Added Docker deploy functionality.
* Moved config and database to `./config` directory. Existing configs will be migrated.
* Config-less support via bot token located in an environment variable.
* Update dependencies.
* Change corpus regen time to 4 AM.
### 0.5.0 ### 0.5.0
Fixed bug where `!mark help` didn't work. * Fixed bug where `!mark help` didn't work.
Only admins can train. * Only admins can train.
The bot responds when mentioned. * The bot responds when mentioned.
The bot cannot mention @everyone. * The bot cannot mention @everyone.
Added version number to help. * Added version number to help.
Added `!mark tts` for a quieter TTS response. * Added `!mark tts` for a quieter TTS response.
Readme overhaul. * Readme overhaul.
Simpler config loading. * Simpler config loading.
### 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.
Converted the fetchMessages function to async/await (updating the requirement to Node.js 8). * Converted the fetchMessages function to async/await (updating the requirement to Node.js 8).
Updated module versions. * Updated module versions.
Added faster unique-array-by-property function * Added faster unique-array-by-property function
Added linting and linted the project. * Added linting and linted the project.
### 0.3.0 ### 0.3.0
Added TTS support and random message attachments. * Added TTS support and random message attachments.
Deleted messages no longer persist in the database longer than 24 hours. * 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
Thanks to [BotMaker-for-Discord](https://github.com/CorySanin/BotMaker-for-Discord) which I used as a reference when during development. Thanks to [BotMaker-for-Discord](https://github.com/CorySanin/BotMaker-for-Discord) which I used as a reference when during development.

4
config/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

12
ecosystem.config.js Normal file
View File

@@ -0,0 +1,12 @@
module.exports = {
apps: [{
name: 'markbot',
script: './index.js',
env: {
NODE_ENV: 'development',
},
env_production: {
NODE_ENV: 'production',
},
}],
};

View File

@@ -3,7 +3,6 @@ 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 { 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));
@@ -56,9 +55,17 @@ function uniqueBy(arr, propertyName) {
function regenMarkov() { function regenMarkov() {
console.log('Regenerating Markov corpus...'); console.log('Regenerating Markov corpus...');
try { try {
fileObj = JSON.parse(fs.readFileSync('markovDB.json', 'utf8')); fileObj = JSON.parse(fs.readFileSync('config/markovDB.json', 'utf8'));
} catch (err) { } catch (err) {
console.log(err); console.log('No markovDB.json, starting with initial values');
fileObj = {
messages: [
{
id: '0',
string: '',
},
],
};
} }
// console.log("MessageCache", messageCache) // console.log("MessageCache", messageCache)
markovDB = fileObj.messages; markovDB = fileObj.messages;
@@ -69,15 +76,12 @@ function regenMarkov() {
markovDB.splice(removeIndex, 1); markovDB.splice(removeIndex, 1);
}); });
deletionCache = []; deletionCache = [];
if (markovDB.length === 0) {
markovDB.push({ string: 'hello', id: null });
}
markov = new Markov(markovDB, markovOpts); markov = new Markov(markovDB, markovOpts);
markov.buildCorpusSync(); markov.buildCorpusSync();
fileObj.messages = markovDB; fileObj.messages = markovDB;
// console.log("WRITING THE FOLLOWING DATA:") // console.log("WRITING THE FOLLOWING DATA:")
// console.log(fileObj) // console.log(fileObj)
fs.writeFileSync('markovDB.json', JSON.stringify(fileObj), 'utf-8'); fs.writeFileSync('config/markovDB.json', JSON.stringify(fileObj), 'utf-8');
fileObj = null; fileObj = null;
messageCache = []; messageCache = [];
console.log('Done regenerating Markov corpus.'); console.log('Done regenerating Markov corpus.');
@@ -87,10 +91,30 @@ function regenMarkov() {
* Loads the config settings from disk * Loads the config settings from disk
*/ */
function loadConfig() { function loadConfig() {
PREFIX = cfg.prefix; // Move config if in legacy location
GAME = cfg.game; if (fs.existsSync('./config.json')) {
// regenMarkov() console.log('Copying config.json to new location in ./config');
client.login(cfg.token); fs.renameSync('./config.json', './config/config.json');
}
if (fs.existsSync('./markovDB.json')) {
console.log('Copying markovDB.json to new location in ./config');
fs.renameSync('./markovDB.json', './config/markovDB.json');
}
try {
// eslint-disable-next-line global-require
const cfg = require('./config/config.json');
PREFIX = cfg.prefix;
GAME = cfg.game;
client.login(cfg.token);
} catch (e) {
console.warn('Failed to use config.json. using default configuration with token environment variable');
PREFIX = '!mark';
GAME = '"!mark help" for help';
client.login(process.env.TOKEN);
}
} }
/** /**
@@ -235,7 +259,7 @@ client.on('error', (err) => {
const errText = `ERROR: ${err.name} - ${err.message}`; const errText = `ERROR: ${err.name} - ${err.message}`;
console.log(errText); console.log(errText);
errors.push(errText); errors.push(errText);
fs.writeFile('error.json', JSON.stringify(errors), (fsErr) => { fs.writeFile('./config/error.json', JSON.stringify(errors), (fsErr) => {
if (fsErr) { if (fsErr) {
console.log(`error writing to error file: ${fsErr.message}`); console.log(`error writing to error file: ${fsErr.message}`);
} }
@@ -270,7 +294,7 @@ client.on('message', (message) => {
fileObj = { fileObj = {
messages: [], messages: [],
}; };
fs.writeFileSync('markovDB.json', JSON.stringify(fileObj), 'utf-8'); fs.writeFileSync('config/markovDB.json', JSON.stringify(fileObj), 'utf-8');
fetchMessages(message); fetchMessages(message);
} }
} }
@@ -284,7 +308,6 @@ client.on('message', (message) => {
generateResponse(message, true); generateResponse(message, true);
} }
if (command === 'regen') { if (command === 'regen') {
console.log('Regenerating...');
regenMarkov(); regenMarkov();
} }
if (command === null) { if (command === null) {
@@ -324,4 +347,4 @@ client.on('messageDelete', (message) => {
}); });
loadConfig(); loadConfig();
schedule.scheduleJob('0 0 * * *', regenMarkov()); schedule.scheduleJob('0 4 * * *', regenMarkov());

888
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,35 +1,41 @@
{ {
"name": "markbot", "name": "markbot",
"version": "0.5.0", "version": "0.6.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": {
"start": "node index.js" "start": "node index.js",
"docker-up": "docker run --rm -e TOKEN=Mjc4MzU0MTU0NTYzNTY3NjM2.XLtWYQ.1pF0BMNDM0PO4kwPSEPelabRTEw -it $(docker build -q .)"
}, },
"repository": "https://github.com/charlocharlie/markov-discord.git", "repository": "https://github.com/charlocharlie/markov-discord.git",
"keywords": [ "keywords": [
"discord", "discord",
"markov", "markov",
"chain", "chain",
"bot" "markov-chain",
"bot",
"discord-js",
"discord-bot",
"markov-chain-bot",
"docker"
], ],
"author": "Charlie Laabs <charlielaabs@gmail.com>", "author": "Charlie Laabs <charlielaabs@gmail.com>",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"bufferutil": "^4.0.0", "bufferutil": "^4.0.1",
"discord.js": "^11.4.2", "discord.js": "^11.4.2",
"erlpack": "github:discordapp/erlpack", "erlpack": "github:discordapp/erlpack",
"markov-strings": "^1.5.0", "markov-strings": "^1.5.2",
"node-schedule": "^1.3.0", "node-schedule": "^1.3.2",
"zlib-sync": "^0.1.4" "zlib-sync": "^0.1.4"
}, },
"engines": { "engines": {
"node": ">=8.0.0" "node": ">=8.0.0"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^5.4.0", "eslint": "^5.16.0",
"eslint-config-airbnb-base": "^13.1.0", "eslint-config-airbnb-base": "^13.1.0",
"eslint-plugin-import": "^2.14.0" "eslint-plugin-import": "^2.17.2"
}, },
"eslintConfig": { "eslintConfig": {
"parserOptions": { "parserOptions": {