Skip to content

Commit

Permalink
improved logging, refactored responses from config.js to responses.js…
Browse files Browse the repository at this point in the history
…on, improved code base
  • Loading branch information
AVMG20 committed Jan 12, 2022
1 parent 53ef9fb commit 48c7bd0
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 53 deletions.
65 changes: 45 additions & 20 deletions classes/ContentValidator.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,79 @@
const {Message} = require('discord.js')
const {Message, MessageAttachment} = require('discord.js')
const Tesseract = require('tesseract.js')
const axios = require('axios');
const {responses, urls, images} = require('../config')
const urlExpresion = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:\/?#[\]@!\$&'\(\)\*\+,;=.]+$/mg
const {urls, images} = require('../config')
const RegexParser = require("regex-parser");
const urlExpression = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:\/?#[\]@!\$&'\(\)\*\+,;=.]+$/mg

class ContentValidator {
/**
* @param {array} responses
*/
constructor(responses) {
this.responses = responses.map(response => {
return {
key : RegexParser(response.key),
content : response.content
}
})
}

/**
* @description determine the content of the message
* @param {Message} message
* @return {Promise<{key: RegExp, content: string}|null>}
*/
validateContent(message) {
return new Promise(async (resolve, reject) => {
//check if content is hastebin url
//check if message is url
if (urls.allowed_urls.findIndex(url => message.content.startsWith(url)) !== -1) {
if (message.content.match(urlExpresion)) {
if (message.content.match(urlExpression)) {
let text = await this.parseUrl(message).catch(reject);
return resolve(this.parseMessage(text));
return resolve(this.checkMatches(text));
}
}

//check if content has image
//check if message has image
if (message.attachments.size > 0) {
let attachment = message.attachments.first()
if (attachment.size < images.max_size_in_bytes && this.attachIsImage(attachment)) {
let text = await this.parseImage(message).catch(reject)
return resolve(this.parseMessage(text));
return resolve(this.checkMatches(text));
}
}

return resolve(this.parseMessage(message.content))
//treat message as regular text message
return resolve(this.checkMatches(message.content))
})


}

/**
* @description check if message attachment is an image
* @param {MessageAttachment} msgAttach
*/
attachIsImage(msgAttach) {
let url = msgAttach.url;
return url.indexOf("png", url.length - "png".length /*or 3*/) !== -1;
}


/**
* @description parse image to text, using tesseract
* @param {Message} message
* @return {Promise<string>}
*/
parseImage(message) {
let attachment = message.attachments.first()

return new Promise(async (resolve, reject) => {
console.log('[PARSER] parsing image to text.')
await message.react(images.message_reaction)

Tesseract.recognize(attachment.url, images.parse_language)
await Tesseract.recognize(attachment.url, images.parse_language)
.then(async ({data: {text}}) => {
await message.reactions.removeAll()
return resolve(text)
})
.catch(reject)


})

}
Expand All @@ -67,6 +83,7 @@ class ContentValidator {
* @return {Promise<string>}
*/
parseUrl(message) {
console.log('[PARSER] parsing url to text.')
return new Promise(async (resolve, reject) => {
let response = await axios.get(message.content, {
maxContentLength: urls.max_content_size_in_bytes
Expand All @@ -79,15 +96,23 @@ class ContentValidator {
}

/**
* @description search for a match, return the response or null
* @description search for a match in the provided text, return the response or null
* @param {string} text
* @return {{key: RegExp, content: string}|null}
*/
parseMessage(text) {
let index = responses.findIndex(response => text.match(response.key))
return index !== -1 ? responses[index] : null;
checkMatches(text) {
console.log('[PARSER] text: \n' + text)
let index = this.responses.findIndex(response => text.match(response.key))

if (index !== -1) {
console.log('[PARSER] match found!')
return this.responses[index];
} else {
console.log('[PARSER] no match found.')
return null;
}
}
}


module.exports = new ContentValidator()
module.exports = ContentValidator;
15 changes: 5 additions & 10 deletions config.example.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
module.exports = {
token: 'YOUR_BOT_TOKEN',

bot: { // https://discordjs.guide/popular-topics/faq.html#how-do-i-check-if-a-guild-member-has-a-specific-role
// check discord.js docs on valid bot properties
// https://discordjs.guide/popular-topics/faq.html#how-do-i-check-if-a-guild-member-has-a-specific-role
bot: {
activity: 'PLAYING',
activity_message: 'Smart Support',
activity_status: 'online',
Expand All @@ -15,7 +17,8 @@ module.exports = {
//configured allowed urls
urls: {
allowed_urls: [
'https://pastebin.com'
'https://pastebin.com',
'https://termbin.com'
],
max_content_size_in_bytes: 314572, //0.3mb
},
Expand All @@ -26,12 +29,4 @@ module.exports = {
parse_language: 'eng',
message_reaction: '👀'
},

//all keys have to be valid regex
responses: [
{
key: /^(?=.*hello)(?=.*world).*$/mgi,
content: `Hello World!`
},
]
}
19 changes: 10 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
const {Client, Intents} = require('discord.js');
const {token, bot, responses, support_channels} = require('./config');
const respond = require('./respond')
const contentValidator = require('./classes/ContentValidator')
const {token, bot, support_channels} = require('./config');
const responses = require('./responses.json')
const ContentValidator = require('./classes/ContentValidator')

// Create a new client instance
const contentValidator = new ContentValidator(responses);
const client = new Client({intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MESSAGES]});

// When the client is ready, run this code (only once)
Expand All @@ -12,19 +13,19 @@ client.once('ready', async () => {
await client.user.setStatus(bot.activity_status);

console.log(`Found '${responses.length}' responses!`)
console.log('Ready!');
console.log('Smart-Support-bot is ready! \n');
});

client.on('messageCreate', async (message) => {
if (message.author.bot) return;
if (!support_channels.includes(message.channelId)) return;

let response = await contentValidator.validateContent(message)
if (!response) return;

if (response) {
respond(message, response)
.then(message => console.log(`responded to '${message.attachments.size > 0 ? 'image' : message.content}'`))
.catch(message => console.log(`failed to respond to '${message.attachments.size > 0 ? 'image' : message.content}'`))
}
message.reply(response)
.then(message => console.log(`[BOT] responded to '${message.attachments.size > 0 ? 'image' : message.content}'`))
.catch(message => console.log(`[BOT] failed to respond to '${message.attachments.size > 0 ? 'image' : message.content}'`))
})


Expand Down
11 changes: 11 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"dotenv": "^10.0.0",
"nanoid": "^3.1.29",
"node-cache": "^5.1.2",
"regex-parser": "^2.2.11",
"tesseract.js": "^2.1.5"
}
}
5 changes: 3 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Smart support bot
A simple discord chat bot with an easy to use configuration. Written using discord.js
# Automated support bot
A simple discord chatbot with an easy-to-use configuration. Written using discord.js


reads text from:
- text
Expand Down
12 changes: 0 additions & 12 deletions respond.js

This file was deleted.

6 changes: 6 additions & 0 deletions responses.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"key": "/^(?=.*hello)(?=.*world).*$/mgi",
"content": "Hello World!"
}
]

0 comments on commit 48c7bd0

Please sign in to comment.