Update 0.3: Added attachments, TTS, and message deletion support.

This commit is contained in:
Charlie Laabs
2018-05-29 14:13:59 -05:00
parent b1279b4f06
commit 6576bab911
4 changed files with 96 additions and 56 deletions

View File

@@ -1,2 +1,10 @@
# 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. This is a super rough prototype. A Markov chain bot using markov-strings. Just uploading here so it can run and generate training data.
## Changelog
### 0.3.0
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.

View File

@@ -26,12 +26,14 @@ let fileObj = {
let markovDB = [] let markovDB = []
let messageCache = [] let messageCache = []
let deletionCache = []
const markovOpts = { const markovOpts = {
maxLength: 400, maxLength: 400,
minWords: 3, minWords: 3,
minScore: 10 minScore: 10
} }
let markov = new Markov(markovDB, markovOpts); let markov
// let markov = new Markov(markovDB, markovOpts);
function regenMarkov() { function regenMarkov() {
console.log("Regenerating Markov corpus...") console.log("Regenerating Markov corpus...")
@@ -41,11 +43,15 @@ function regenMarkov() {
catch (err) { console.log(err) } catch (err) { console.log(err) }
// console.log("MessageCache", messageCache) // console.log("MessageCache", messageCache)
markovDB = fileObj.messages markovDB = fileObj.messages
markovDB = uniqueBy(markovDB.concat(messageCache), 'id')
deletionCache.forEach(id => {
let removeIndex = markovDB.map(function (item) { return item.id; }).indexOf(id)
// console.log('Remove Index:', removeIndex)
markovDB.splice(removeIndex, 1)
})
deletionCache = []
if (markovDB.length == 0) if (markovDB.length == 0)
markovDB.push({ string: 'hello', id: null }) markovDB.push({ string: 'hello', id: null })
//markovDB = uniqueArray(markovDB.concat(messageCache), 'id')
markovDB = uniqueBy(markovDB.concat(messageCache), 'id')
//markovDB = markovDB.concat(messageCache)
markov = new Markov(markovDB, markovOpts); markov = new Markov(markovDB, markovOpts);
markov.buildCorpusSync() markov.buildCorpusSync()
fileObj.messages = markovDB fileObj.messages = markovDB
@@ -53,7 +59,7 @@ function regenMarkov() {
// console.log(fileObj) // console.log(fileObj)
fs.writeFileSync('markovDB.json', JSON.stringify(fileObj), 'utf-8') fs.writeFileSync('markovDB.json', JSON.stringify(fileObj), 'utf-8')
fileObj = null; fileObj = null;
markovDB = [] // markovDB = []
messageCache = [] messageCache = []
console.log("Done regenerating Markov corpus.") console.log("Done regenerating Markov corpus.")
} }
@@ -97,7 +103,7 @@ client.on('message', message => {
.setAuthor(client.user.username, client.user.avatarURL) .setAuthor(client.user.username, client.user.avatarURL)
.setThumbnail(client.user.avatarURL) .setThumbnail(client.user.avatarURL)
.setDescription('A Markov chain chatbot that speaks based on previous chat input.') .setDescription('A Markov chain chatbot that speaks based on previous chat input.')
.addField('!mark', 'Generates a sentence to say based on the chat database') .addField('!mark', 'Generates a sentence to say based on the chat database. Send your message as TTS to recieve it as TTS.')
.addField('!mark train', 'Fetches the maximum amount of previous messages in the current text channel, adds it to the database, and regenerates the corpus. Takes about 2 minutes.') .addField('!mark train', 'Fetches the maximum amount of previous messages in the current text channel, adds it to the database, and regenerates the corpus. Takes about 2 minutes.')
.addField('!mark regen', 'Manually regenerates the corpus to add recent chat info. Run this before shutting down to avoid any data loss. This automatically runs at midnight.') .addField('!mark regen', 'Manually regenerates the corpus to add recent chat info. Run 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.') .addField('!mark invite', 'Don\'t invite this bot to other servers. The database is shared between all servers and text channels.')
@@ -108,13 +114,27 @@ client.on('message', message => {
} }
if (command === 'train') { if (command === 'train') {
console.log("Training...") console.log("Training...")
fileObj = {
messages: []
}
fs.writeFileSync('markovDB.json', JSON.stringify(fileObj), 'utf-8')
fetchMessageChunk(message, null, []) fetchMessageChunk(message, null, [])
} }
if (command === 'respond') { if (command === 'respond') {
console.log("Responding...") console.log("Responding...")
markov.generateSentence().then(result => { markov.generateSentence().then(result => {
console.log(result) console.log('Generated Result:', result)
message.channel.send(result.string) let messageOpts = {
tts: message.tts
}
let randomMessage = markovDB[Math.floor(Math.random() * markovDB.length)]
console.log('Random Message:', randomMessage)
if (randomMessage.hasOwnProperty('attachment')) {
messageOpts.files = [{
attachment: randomMessage.attachment
}]
}
message.channel.send(result.string, messageOpts)
}).catch(err => { }).catch(err => {
console.log(err) console.log(err)
if (err.message == 'Cannot build sentence with current corpus and options') if (err.message == 'Cannot build sentence with current corpus and options')
@@ -129,7 +149,14 @@ client.on('message', message => {
if (command === null) { if (command === null) {
console.log("Listening...") console.log("Listening...")
if (!message.author.bot) { if (!message.author.bot) {
messageCache.push({ string: message.content, id: message.id }) let dbObj = {
string: message.content,
id: message.id
}
if (message.attachments.size > 0) {
dbObj.attachment = message.attachments.values().next().value.url
}
messageCache.push(dbObj)
} }
} }
if (command === inviteCmd) { if (command === inviteCmd) {
@@ -146,6 +173,12 @@ client.on('message', message => {
} }
}) })
client.on('messageDelete', message => {
// console.log('Adding message ' + message.id + ' to deletion cache.')
deletionCache.push(message.id)
console.log('deletionCache:', deletionCache)
})
function validateMessage(message) { function validateMessage(message) {
let messageText = message.content.toLowerCase() let messageText = message.content.toLowerCase()
let command = null; let command = null;
@@ -170,7 +203,14 @@ function fetchMessageChunk(message, oldestMessageID, historyCache) {
message.channel.fetchMessages({ before: oldestMessageID, limit: 100 }) message.channel.fetchMessages({ before: oldestMessageID, limit: 100 })
.then(messages => { .then(messages => {
historyCache = historyCache.concat(messages.filter(elem => !elem.author.bot).map(elem => { historyCache = historyCache.concat(messages.filter(elem => !elem.author.bot).map(elem => {
return { string: elem.content, id: elem.id } let dbObj = {
string: elem.content,
id: elem.id
}
if (elem.attachments.size > 0) {
dbObj.attachment = elem.attachments.values().next().value.url
}
return dbObj
})); }));
oldestMessageID = messages.last().id oldestMessageID = messages.last().id
return historyCache.concat(fetchMessageChunk(message, oldestMessageID, historyCache)) return historyCache.concat(fetchMessageChunk(message, oldestMessageID, historyCache))

80
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "markbot", "name": "markbot",
"version": "0.2.0", "version": "0.3.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -41,27 +41,15 @@
} }
}, },
"discord.js": { "discord.js": {
"version": "11.3.0", "version": "11.3.2",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-11.3.0.tgz", "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-11.3.2.tgz",
"integrity": "sha512-xM3ydvb4urHjCP3N+Mgpw53a7ZGtmPwllwVgwPqdF2SHNPvTDr4So/+55VVx76s5eO9IMrOWpczsEuIr0/MAgQ==", "integrity": "sha512-Abw9CTMX3Jb47IeRffqx2VNSnXl/OsTdQzhvbw/JnqCyqc2imAocc7pX2HoRmgKd8CgSqsjBFBneusz/E16e6A==",
"requires": { "requires": {
"long": "3.2.0", "long": "4.0.0",
"prism-media": "0.0.1", "prism-media": "0.0.2",
"snekfetch": "3.6.1", "snekfetch": "3.6.4",
"tweetnacl": "1.0.0", "tweetnacl": "1.0.0",
"ws": "4.0.0" "ws": "4.1.0"
},
"dependencies": {
"ws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-4.0.0.tgz",
"integrity": "sha512-QYslsH44bH8O7/W2815u5DpnCpXWpEK44FmaHffNwgJI4JMaSZONgPBTOfrxJ29mXKbXak+LsJ2uAkDTYq2ptQ==",
"requires": {
"async-limiter": "1.0.0",
"safe-buffer": "5.1.1",
"ultron": "1.1.1"
}
}
} }
}, },
"erlpack": { "erlpack": {
@@ -85,14 +73,14 @@
} }
}, },
"lodash": { "lodash": {
"version": "4.17.4", "version": "4.17.10",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
"integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
}, },
"long": { "long": {
"version": "3.2.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=" "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
}, },
"long-timeout": { "long-timeout": {
"version": "0.1.1", "version": "0.1.1",
@@ -100,12 +88,12 @@
"integrity": "sha1-lyHXiLR+C8taJMLivuGg2lXatRQ=" "integrity": "sha1-lyHXiLR+C8taJMLivuGg2lXatRQ="
}, },
"markov-strings": { "markov-strings": {
"version": "1.3.5", "version": "1.5.0",
"resolved": "https://registry.npmjs.org/markov-strings/-/markov-strings-1.3.5.tgz", "resolved": "https://registry.npmjs.org/markov-strings/-/markov-strings-1.5.0.tgz",
"integrity": "sha512-cGe7DSh1RrkgCxu33xeI6RAhC0KCExGvS7enHkMyieNs0MrSGm9Gu5AYmVCFcWPM/+0SN34t38OdT+/YKrjxjQ==", "integrity": "sha512-fHrnpXvR2p3mkxZEGeQaipus+2Z+DnewLmxn1BosNRajwWza0dHX1NO3reYGsPB5BGtzDyR6lh3owFZyICs2mg==",
"requires": { "requires": {
"debug": "3.1.0", "debug": "3.1.0",
"lodash": "4.17.4" "lodash": "4.17.10"
} }
}, },
"moment": { "moment": {
@@ -147,19 +135,19 @@
"integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0="
}, },
"prism-media": { "prism-media": {
"version": "0.0.1", "version": "0.0.2",
"resolved": "https://registry.npmjs.org/prism-media/-/prism-media-0.0.1.tgz", "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-0.0.2.tgz",
"integrity": "sha1-o0JcnKvVDRxsAuVDlBoRiVZnvRA=" "integrity": "sha512-L6yc8P5NVG35ivzvfI7bcTYzqFV+K8gTfX9YaJbmIFfMXTs71RMnAupvTQPTCteGsiOy9QcNLkQyWjAafY/hCQ=="
}, },
"safe-buffer": { "safe-buffer": {
"version": "5.1.1", "version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}, },
"snekfetch": { "snekfetch": {
"version": "3.6.1", "version": "3.6.4",
"resolved": "https://registry.npmjs.org/snekfetch/-/snekfetch-3.6.1.tgz", "resolved": "https://registry.npmjs.org/snekfetch/-/snekfetch-3.6.4.tgz",
"integrity": "sha512-aLEvf1YR440pINb0LEo/SL2Q2s/A26+YEqPlx09A0XpGH7qWp8iqIFFolVILHn2yudWXJne9QWyQu+lzDp+ksQ==" "integrity": "sha512-NjxjITIj04Ffqid5lqr7XdgwM7X61c/Dns073Ly170bPQHLm6jkmelye/eglS++1nfTWktpP6Y2bFXjdPlQqdw=="
}, },
"sorted-array-functions": { "sorted-array-functions": {
"version": "1.1.0", "version": "1.1.0",
@@ -171,16 +159,20 @@
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.0.tgz", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.0.tgz",
"integrity": "sha1-cT2LgY2kIGh0C/aDhtBHnmb8ins=" "integrity": "sha1-cT2LgY2kIGh0C/aDhtBHnmb8ins="
}, },
"ultron": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
},
"unique-by": { "unique-by": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/unique-by/-/unique-by-1.0.0.tgz", "resolved": "https://registry.npmjs.org/unique-by/-/unique-by-1.0.0.tgz",
"integrity": "sha1-UiDIa6e8Vy+3E610ZRRwy2RCEr0=" "integrity": "sha1-UiDIa6e8Vy+3E610ZRRwy2RCEr0="
}, },
"ws": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz",
"integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==",
"requires": {
"async-limiter": "1.0.0",
"safe-buffer": "5.1.2"
}
},
"zlib-sync": { "zlib-sync": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/zlib-sync/-/zlib-sync-0.1.4.tgz", "resolved": "https://registry.npmjs.org/zlib-sync/-/zlib-sync-0.1.4.tgz",

View File

@@ -1,6 +1,6 @@
{ {
"name": "markbot", "name": "markbot",
"version": "0.2.0", "version": "0.3.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": {
@@ -16,7 +16,7 @@
"author": "Charlie Laabs <charlielaabs@gmail.com>", "author": "Charlie Laabs <charlielaabs@gmail.com>",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"discord.js": "^11.3.0", "discord.js": "^11.3.2",
"erlpack": "github:discordapp/erlpack", "erlpack": "github:discordapp/erlpack",
"markov-strings": "^1.3.5", "markov-strings": "^1.3.5",
"node-schedule": "^1.3.0", "node-schedule": "^1.3.0",