Merge branch 'bot-module'

This commit is contained in:
benji7425 2017-05-05 13:42:51 +01:00
commit f2878bc319
13 changed files with 524 additions and 344 deletions

2
.vscode/launch.json vendored
View File

@ -5,7 +5,7 @@
"name": "Launch",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}\\feed-bot.js",
"program": "${workspaceRoot}\\wrapper\\index.js",
"stopOnEntry": false,
"args": [],
"cwd": "${workspaceRoot}",

View File

@ -15,8 +15,7 @@
"help": "!help"
},
"developerCommands": {
"logUpload": "!logsplease",
"cacheList": "!cacheList"
"cacheList": "!cached"
},
"developers": [
"117966411548196870"

View File

@ -1,293 +0,0 @@
//external library imports
var Dns = require("dns"); //for connectivity checking
var Url = require("url"); //for url parsing
var Uri = require("urijs"); //for finding urls within message strings
var Discord = require("discord.io"); //for obvious reasons
var FeedRead = require("feed-read"); //for rss feed reading
var JsonFile = require("jsonfile"); //reading/writing json
//my imports
var Log = require("./log.js"); //some very simple logging functions I made
var BotConfig = require("./bot-config.json"); //bot config file containing bot token
var Config = require("./config.json"); //config file containing other settings
var DiscordClient = {
bot: null,
startup: function () {
//check if we can connect to discordapp.com to authenticate the bot
Dns.resolve("discordapp.com", function (err) {
if (err)
Log.error("CONNECTION ERROR: Unable to locate discordapp.com to authenticate the bot", err);
else {
//if there was no error, go ahead and create and authenticate the bot
DiscordClient.bot = new Discord.Client({
token: BotConfig.token,
autorun: true
});
//set up the bot's event handlers
DiscordClient.bot.on("ready", DiscordClient.onReady);
DiscordClient.bot.on("disconnect", DiscordClient.onDisconnect);
DiscordClient.bot.on("message", DiscordClient.onMessage);
}
});
},
onReady: function () {
Log.info("Registered/connected bot " + DiscordClient.bot.username + " - (" + DiscordClient.bot.id + ")");
DiscordClient.checkPastMessagesForLinks(); //we need to check past messages for links on startup, but also on reconnect because we don't know what has happened during the downtime
//set the interval function to check the feed
intervalFunc = () => {
Feed.check((err, articles) => {
Links.validate(err, articles, DiscordClient.post);
});
};
},
onDisconnect: function (err, code) {
Log.event("Bot was disconnected! " + (err ? err : "") + (code ? code : "No disconnect code provided.") + "\nClearing the feed timer and starting reconnect timer", "Discord.io");
intervalFunc = DiscordClient.startup; //reassign the interval function to try restart the bot every 5 sec
},
onMessage: function (user, userID, channelID, message) {
//contains a link, and is not the latest link from the rss feed
if (channelID === Config.channelID && Links.messageContainsLink(message) && (message !== Links.latestFromFeedlatestFeedLink)) {
Log.event("Detected posted link in this message: " + message, "Discord.io");
//extract the url from the string, and cache it
Uri.withinString(message, function (url) {
Links.cache(Links.standardise(url));
return url;
});
}
else {
//iterate over all of our message triggers to see if the message sent requires any action
for (var i = 0; i < DiscordClient.messageTriggers.length; i++) {
var messageTrigger = DiscordClient.messageTriggers[i];
if (message === messageTrigger.message) {
//check if its locked to a channel or to a specific user
if ((messageTrigger.channelID && messageTrigger.channelID === channelID) || (messageTrigger.userIDs && messageTrigger.userIDs.includes(userID)))
messageTrigger.action(user, userID, channelID, message);
}
}
}
},
checkPastMessagesForLinks: function () {
var limit = 100;
Log.info("Attempting to check past " + limit + " messages for links");
//get the last however many messsages from our discord channel
DiscordClient.bot.getMessages({
channelID: Config.channelID,
limit: limit
}, function (err, messages) {
if (err) Log.error("Error fetching discord messages.", err);
else {
Log.info("Pulled last " + messages.length + " messages, scanning for links");
var messageContents = messages.map((x) => { return x.content; }).reverse(); //extract an array of strings from the array of message objects
for (var messageIdx in messageContents) {
var message = messageContents[messageIdx];
if (Links.messageContainsLink(message)) //test if the message contains a url
//detect the url inside the string, and cache it
Uri.withinString(message, function (url) {
Links.cache(url);
return url;
});
}
}
});
},
post: function (link) {
//send a messsage containing the new feed link to our discord channel
DiscordClient.bot.sendMessage({
to: Config.channelID,
message: Subscriptions.mention() + link
});
},
//actions to perform when certain messages are detected, along with channel or user requirements
messageTriggers: [
{
message: Config.userCommands.subscribe,
action: (user, userID, channelID, message) => { if (Config.allowSubscriptions) Subscriptions.subscribe(user, userID, channelID, message); },
channelID: Config.channelID
},
{
message: Config.userCommands.unsubscribe,
action: (user, userID, channelID, message) => { if (Config.allowSubscriptions) Subscriptions.unsubscribe(user, userID, channelID, message); },
channelID: Config.channelID
},
{
message: Config.userCommands.help,
action: (user, userID, channelID, message) => {
DiscordClient.bot.sendMessage({
to: Config.channelID,
message: Config.userCommands.join(" + ")
});
},
channelID: Config.channelID
},
{
message: Config.developerCommands.logUpload,
action: (user, userID, channelID, message) => {
DiscordClient.bot.uploadFile({
to: channelID,
file: Config.logFile
});
},
userIDs: Config.developers
},
{
message: Config.developerCommands.cacheList,
action: (user, userID, channelID, message) => {
DiscordClient.bot.sendMessage({
to: channelID,
message: Links.cached.join(", ")
});
},
userIDs: Config.developers
}
]
};
var Subscriptions = {
subscribe: function (user, userID, channelID, message) {
DiscordClient.bot.addToRole({
serverID: Config.serverID,
userID: userID,
roleID: Config.subscribersRoleID
},
(err) => {
if (err) Log.raw(err); //log the error if there is an error
else { //else go ahead and confirm subscription
Log.event("Subscribed user " + (user ? user + "(" + userID + ")" : userID));
DiscordClient.bot.sendMessage({
to: channelID,
message: "You have successfully subscribed"
}, (err, response) => { setTimeout(() => { DiscordClient.bot.deleteMessage({ channelID: channelID, messageID: response.id }); }, Config.messageDeleteDelay); }); //delete the subscription confirmation message after a delay
}
});
},
unsubscribe: function (user, userID, channelID, message) {
DiscordClient.bot.removeFromRole({
serverID: Config.serverID,
userID: userID,
roleID: Config.subscribersRoleID
},
(err) => {
if (err) Log.raw(err); //log the error if there is an error
else { //else go ahead and confirm un-subscription
Log.event("Unsubscribed user " + (user ? user + "(" + userID + ")" : userID));
DiscordClient.bot.sendMessage({
to: channelID,
message: "You have successfully unsubscribed"
}, (err, response) => { setTimeout(() => { DiscordClient.bot.deleteMessage({ channelID: channelID, messageID: response.id }); }, Config.messageDeleteDelay); }); //delete the un-subscription confirmation message after a delay
}
});
},
mention: function () {
return Config.allowSubscriptions ? "<@&" + Config.subscribersRoleID + "> " : "";
}
};
var YouTube = {
url: {
share: "http://youtu.be/",
full: "http://www.youtube.com/watch?v=",
createFullUrl: function (shareUrl) {
return shareUrl.replace(YouTube.url.share, YouTube.url.full);
},
createShareUrl: function (fullUrl) {
var shareUrl = fullUrl.replace(YouTube.url.full, YouTube.url.share);
if (shareUrl.includes("&")) shareUrl = shareUrl.slice(0, fullUrl.indexOf("&"));
return shareUrl;
}
},
};
var Links = {
standardise: function (link) {
link = link.replace("https://", "http://"); //cheaty way to get around http and https not matching
if (Config.youtubeMode) link = link.split("&")[0]; //quick way to chop off stuff like &feature=youtube etc
return link;
},
messageContainsLink: function (message) {
var messageLower = message.toLowerCase();
return messageLower.includes("http://") || messageLower.includes("https://") || messageLower.includes("www.");
},
cached: [],
latestFeedLink: "",
cache: function (link) {
link = Links.standardise(link);
if (Config.youtubeMode) link = YouTube.url.createShareUrl(link);
//store the new link if not stored already
if (!Links.isCached(link)) {
Links.cached.push(link);
Log.info("Cached URL: " + link);
}
if (Links.cached.length > Config.numLinksToCache) Links.cached.shift(); //get rid of the first array element if we have reached our cache limit
},
isCached: function (link) {
link = Links.standardise(link);
if (Config.youtubeMode)
return Links.cached.includes(YouTube.url.createShareUrl(link));
return Links.cached.includes(link);
},
validate: function (err, articles, callback) {
if (err) Log.error("FEED ERROR: Error reading RSS feed.", err);
else {
var latestLink = Links.standardise(articles[0].link);
if (Config.youtubeMode) latestLink = YouTube.url.createShareUrl(latestLink);
//make sure we don't spam the latest link
if (latestLink === Links.latestFeedLink)
return;
//make sure the latest link hasn't been posted already
if (Links.isCached(latestLink)) {
Log.info("Didn't post new feed link because already detected as posted " + latestLink);
}
else {
callback(latestLink);
Links.cache(latestLink); //make sure the link is cached, so it doesn't get posted again
}
Links.latestFeedLink = latestLink; //ensure our latest feed link variable is up to date, so we can track when the feed updates
}
}
};
var Feed = {
urlObj: Url.parse(Config.feedUrl),
check: function (callback) {
Dns.resolve(Feed.urlObj.host, function (err) { //check that we have an internet connection (well not exactly - check that we have a connection to the host of the feedUrl)
if (err) Log.error("CONNECTION ERROR: Cannot resolve host.", err);
else FeedRead(Config.feedUrl, callback);
});
}
};
var intervalFunc = () => { }; //do nothing by default
//IIFE to kickstart the bot when the app loads
(function () {
DiscordClient.startup();
setInterval(() => { intervalFunc(); }, Config.pollingInterval);
})();

264
index.js Normal file
View File

@ -0,0 +1,264 @@
//external library imports
var Dns = require("dns"); //for connectivity checking
var Url = require("url"); //for url parsing
var Uri = require("urijs"); //for finding urls within message strings
var FeedRead = require("feed-read"); //for rss feed reading
var JsonFile = require("jsonfile"); //reading/writing json
var Console = require("console");
//my imports
var Config = require("./config.json"); //config file containing other settings
module.exports = {
onReady: (bot) => {
Actions.checkPastMessagesForLinks(bot); //we need to check past messages for links on startup, but also on reconnect because we don't know what has happened during the downtime
//set the interval function to check the feed
intervalFunc = () => {
Feed.check((err, articles) => {
Links.validate(err, articles, (latestLink) => Actions.post(bot, latestLink));
});
};
setInterval(() => { intervalFunc(); }, Config.pollingInterval);
},
onMessage: function (bot, user, userID, channelID, message) {
//contains a link, and is not the latest link from the rss feed
if (channelID === Config.channelID && Links.messageContainsLink(message) && (message !== Links.latestFromFeedlatestFeedLink)) {
Console.info("Detected posted link in this message: " + message, "Discord.io");
//extract the url from the string, and cache it
Uri.withinString(message, function (url) {
Links.cache(Links.standardise(url));
return url;
});
}
},
commands: [
{
command: Config.userCommands.subscribe,
type: "equals",
action: (bot, user, userID, channelID, message) => { if (Config.allowSubscriptions) Subscriptions.subscribe(bot, user, userID, channelID, message); },
channelIDs: [Config.channelID]
},
{
command: Config.userCommands.unsubscribe,
type: "equals",
action: (bot, user, userID, channelID, message) => { if (Config.allowSubscriptions) Subscriptions.unsubscribe(bot, user, userID, channelID, message); },
channelIDs: [Config.channelID]
},
{
command: Config.userCommands.help,
type: "equals",
action: (bot, user, userID, channelID, message) => {
bot.sendMessage({
to: Config.channelID,
message: "Available commands: " + getValues(Config.userCommands).join(", ")
});
},
channelIDs: [Config.channelID]
},
{
command: Config.developerCommands.logUpload,
type: "equals",
action: (bot, user, userID, channelID, message) => {
bot.uploadFile({
to: channelID,
file: Config.logFile
});
},
userIDs: Config.developers
},
{
command: Config.developerCommands.cacheList,
type: "equals",
action: (bot, user, userID, channelID, message) => {
bot.sendMessage({
to: channelID,
message: Links.cached.join(", ")
});
},
userIDs: Config.developers
}
]
};
var Actions = {
post: (bot, link) => {
//send a messsage containing the new feed link to our discord channel
bot.sendMessage({
to: Config.channelID,
message: Subscriptions.mention() + link
});
},
checkPastMessagesForLinks: (bot) => {
var limit = 100;
Console.info("Attempting to check past " + limit + " messages for links");
//get the last however many messsages from our discord channel
bot.getMessages({
channelID: Config.channelID,
limit: limit
}, function (err, messages) {
if (err) Console.error("Error fetching discord messages.", err);
else {
Console.info("Pulled last " + messages.length + " messages, scanning for links");
var messageContents = messages.map((x) => { return x.content; }).reverse(); //extract an array of strings from the array of message objects
for (var messageIdx in messageContents) {
var message = messageContents[messageIdx];
if (Links.messageContainsLink(message)) //test if the message contains a url
//detect the url inside the string, and cache it
Uri.withinString(message, function (url) {
Links.cache(url);
return url;
});
}
}
});
},
};
var Subscriptions = {
subscribe: function (bot, user, userID, channelID, message) {
bot.addToRole({
serverID: Config.serverID,
userID: userID,
roleID: Config.subscribersRoleID
},
(err) => {
if (err) Console.log(err); //log the error if there is an error
else { //else go ahead and confirm subscription
Console.info("Subscribed user " + (user ? user + "(" + userID + ")" : userID));
bot.sendMessage({
to: channelID,
message: "You have successfully subscribed"
}, (err, response) => { setTimeout(() => { bot.deleteMessage({ channelID: channelID, messageID: response.id }); }, Config.messageDeleteDelay); }); //delete the subscription confirmation message after a delay
}
});
},
unsubscribe: function (bot, user, userID, channelID, message) {
bot.removeFromRole({
serverID: Config.serverID,
userID: userID,
roleID: Config.subscribersRoleID
},
(err) => {
if (err) Console.log(err); //log the error if there is an error
else { //else go ahead and confirm un-subscription
Console.info("Unsubscribed user " + (user ? user + "(" + userID + ")" : userID));
bot.sendMessage({
to: channelID,
message: "You have successfully unsubscribed"
}, (err, response) => { setTimeout(() => { bot.deleteMessage({ channelID: channelID, messageID: response.id }); }, Config.messageDeleteDelay); }); //delete the un-subscription confirmation message after a delay
}
});
},
mention: function () {
return Config.allowSubscriptions ? "<@&" + Config.subscribersRoleID + "> " : "";
}
};
var YouTube = {
url: {
share: "http://youtu.be/",
full: "http://www.youtube.com/watch?v=",
createFullUrl: function (shareUrl) {
return shareUrl.replace(YouTube.url.share, YouTube.url.full);
},
createShareUrl: function (fullUrl) {
var shareUrl = fullUrl.replace(YouTube.url.full, YouTube.url.share);
if (shareUrl.includes("&")) shareUrl = shareUrl.slice(0, fullUrl.indexOf("&"));
return shareUrl;
}
},
};
var Links = {
standardise: function (link) {
link = link.replace("https://", "http://"); //cheaty way to get around http and https not matching
if (Config.youtubeMode) link = link.split("&")[0]; //quick way to chop off stuff like &feature=youtube etc
return link;
},
messageContainsLink: function (message) {
var messageLower = message.toLowerCase();
return messageLower.includes("http://") || messageLower.includes("https://") || messageLower.includes("www.");
},
cached: [],
latestFeedLink: "",
cache: function (link) {
link = Links.standardise(link);
if (Config.youtubeMode) link = YouTube.url.createShareUrl(link);
//store the new link if not stored already
if (!Links.isCached(link)) {
Links.cached.push(link);
Console.info("Cached URL: " + link);
}
if (Links.cached.length > Config.numLinksToCache) Links.cached.shift(); //get rid of the first array element if we have reached our cache limit
},
isCached: function (link) {
link = Links.standardise(link);
if (Config.youtubeMode)
return Links.cached.includes(YouTube.url.createShareUrl(link));
return Links.cached.includes(link);
},
validate: function (err, articles, callback) {
if (err) Console.error("FEED ERROR: Error reading RSS feed.", err);
else {
var latestLink = Links.standardise(articles[0].link);
if (Config.youtubeMode) latestLink = YouTube.url.createShareUrl(latestLink);
//make sure we don't spam the latest link
if (latestLink === Links.latestFeedLink)
return;
//make sure the latest link hasn't been posted already
if (Links.isCached(latestLink)) {
Console.info("Didn't post new feed link because already detected as posted " + latestLink);
}
else {
callback(latestLink);
Links.cache(latestLink); //make sure the link is cached, so it doesn't get posted again
}
Links.latestFeedLink = latestLink; //ensure our latest feed link variable is up to date, so we can track when the feed updates
}
}
};
var Feed = {
urlObj: Url.parse(Config.feedUrl),
check: function (callback) {
Dns.resolve(Feed.urlObj.host, function (err) { //check that we have an internet connection (well not exactly - check that we have a connection to the host of the feedUrl)
if (err) Console.error("CONNECTION ERROR: Cannot resolve host.", err);
else FeedRead(Config.feedUrl, callback);
});
}
};
var getValues = function (obj) {
var values = [];
for (var value in obj)
if (obj.hasOwnProperty(value))
values.push(obj[value]);
return values;
};
var intervalFunc = () => { }; //do nothing by default

47
log.js
View File

@ -1,47 +0,0 @@
//external library imports
var Console = require("console"); //access to debug console
var FileWriter = require("simple-file-writer"); //file writer for logging
//my imports
var Config = require("./config.json"); //config file containing other settings
var logWriter = new FileWriter(Config.logFile);
var latestLog = "";
function log(message) {
if (message && message !== latestLog) {
latestLog = message; //spam reduction
//attach a formatted date string to the beginning of everything we log
var dateMessage = new Date().toLocaleString() + " " + message;
Console.log(dateMessage);
logWriter.write(dateMessage + "\n");
}
}
function logRaw(obj) {
Console.log(obj);
}
module.exports = {
info: function (message) {
if (message)
log("[INFO] " + message);
},
event: function (message, sender) {
//if we received a message, log it - include sender information if it was passed
if (message) {
log("[EVENT] " + (sender ? sender + " has sent an event: " : "") + message);
}
},
error: function (message, innerEx) {
if (message) {
//log the message, attach innerEx information if it was passed
log("[ERROR] " + message + (innerEx ? ". Inner exception details: " + (innerEx.message || innerEx) : ""));
}
},
raw: function (obj) {
if (obj) logRaw(obj);
}
};

View File

@ -4,7 +4,7 @@
"description": "discord-feed-bot",
"main": "index.js",
"scripts": {
"start": "node feed-bot.js",
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {

30
wrapper/.eslintrc.json Normal file
View File

@ -0,0 +1,30 @@
{
"env": {
"browser": true,
"node": true,
"commonjs": true,
"es6": true
},
"extends": "eslint:recommended",
"parserOptions": {
"sourceType": "module"
},
"rules": {
"indent": [
"warn",
"tab",
{ "SwitchCase": 1}
],
"quotes": [
"warn",
"double"
],
"semi": [
"error",
"always"
],
"no-undef": "error",
"no-unused-vars": "warn",
"eqeqeq": ["error", "always"]
}
}

59
wrapper/.gitignore vendored Normal file
View File

@ -0,0 +1,59 @@
# Project specific
token.json
# Created by https://www.gitignore.io/api/node
### Node ###
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# End of https://www.gitignore.io/api/node
# Project-specific cache for use with shrinkpack - https://github.com/JamieMason/shrinkpack

11
wrapper/.gitrepo Normal file
View File

@ -0,0 +1,11 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
;
[subrepo]
remote = git@github.com:benji7425/shell-discord-bot.git
branch = master
commit = 4946884723f9c66d3c2713f109aab2cf60bd81f4
parent = 258eff2d63b5804e1e423e9418b117ad4212d8ff
cmdver = 0.3.1

3
wrapper/.npmrc Normal file
View File

@ -0,0 +1,3 @@
save=true
sace-exact=true
cache=node_cache

83
wrapper/README.md Normal file
View File

@ -0,0 +1,83 @@
# Shell Discord bot
The purpose of this is to act as a shell for other bot modules, so that a single bot user account can be used for a multi-function bot.
## Setup
- Fork/clone/merge this repo into a new one
- `npm install --save discord.io`
- `npm shrinkwrap --dev`
- `shrinkpack .`
- Create *token.json* with your discord token: `{ "token": "1234567890" }`
## Creating a bot module
Interfacing with each bot module is done by the properties in its module.exports. Available properties are:
| property | property type | parameters | description |
|--------------|---------------|---------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|
| onReady | method | none | called on the bot ready event |
| onDisconnect | method | none | called when the bot disconnects |
| onMessage | method | bot, user, userID, channelID, message | called when the bot receives a message - identical to the 'action' property of a command, but triggered on every message (see below) |
| commands | array | N/A | commands a user can invoke - like the onMessage event, but only triggered on expected commands (see below) |
### Commands
A command object contains a command, and an action to invoke if the message contains that command. Each command object needs a certain set of properties:
| property | optional? | value | description |
|------------|-----------|--------------------------|----------------------------------------------------------------------------------------------------|
| command | required | string | Command to look for in the message |
| type | required | "equals" or "startsWith" | Describes whether we are looking for the message to be the exact command, or just to start with it |
| action | required | function | Callback to invoke if the command is matched (see below) |
| channelIDs | optional | array of strings | If this property is present, the command will only be triggered if sent in one of these channels |
| userIDs | optional | array of strings | If this property is present, the command will only be triggered if made by one of these users |
#### Actions
An action is a callback function called if the specified command is found. It must take these parameters:
| parameter | type | description |
|-----------|--------|----------------------------------------------------------------------|
| bot | object | The [discord.io](https://github.com/izy521/discord.io) client object |
| user | string | Username of the user who sent the message |
| userID | string | User ID of the user who sent the message |
| channelID | string | Channel ID of the channel the message was sent in |
| message | string | The message/command that was sent |
Example 1:
```JavaScript
{
command: "ping",
type: "equals",
action: (bot, user, userID, channelID, message) => {
bot.sendMessage({
to: channelID,
message: "pong"
})
},
userIDs: ["1234567890"]
}
```
The above example will only call *action* if the user with ID 1234567890 sends "ping"
Example 2:
```JavaScript
{
command: "!define",
type: "startsWith",
action: (bot, user, userID, channelID, message) => {
bot.sendMessage({
to: channelID,
message: getDefinition(message.split(" ")[1])
})
}
}
```
The above example expects the user to type '!define something', ie only checking for the message to start with '!define'. You are still passed the full message, so can split it up and read it however you want.
*action* will only be called if the message begins with '!define', but has no restrictions on which channel(s) or user(s) use it

67
wrapper/index.js Normal file
View File

@ -0,0 +1,67 @@
//node imports
const Console = require("console");
//external module imports
var Discord = require("discord.io");
var BotModules = [require("../index.js")];
var bot;
var EventHandlers = {
onReady: () => {
Console.info("Registered bot " + bot.username + " with id " + bot.id);
for (let i = 0, len = BotModules.length; i < len; i++) {
let botModule = BotModules[i];
if (botModule.onReady) botModule.onReady(bot);
}
},
onDisconnect: (err, code) => {
Console.error("Bot was disconnected!", err, code);
for (let i = 0, len = BotModules.length; i < len; i++) {
let botModule = BotModules[i];
if (botModule.onDisconnect) botModule.onDisconnect();
}
bot.connect();
},
onMessage: (user, userID, channelID, message) => {
for (let i = 0, iLen = BotModules.length; i < iLen; i++) {
let botModule = BotModules[i];
if (botModule.commands) {
for (let j = 0, jLen = botModule.commands.length; j < jLen; j++) {
let messageTrigger = botModule.commands[j];
if ((!messageTrigger.channelIDs && !messageTrigger.userIDs) //if we have neither channel nor user restraint, pass
|| (messageTrigger.channelIDs && messageTrigger.channelIDs.includes(channelID)) //otherwise, if we have a channel constraint, pass if we're allowed to respond in this channel
|| (messageTrigger.userIDs && messageTrigger.userIDs.includes(userID))) //otherwise, if we have a user constraint, pass if we're allowed to respond to this user
switch (messageTrigger.type) {
case "startsWith":
if (message.startsWith(messageTrigger.command))
messageTrigger.action(bot, user, userID, channelID, message);
break;
default:
if (message === messageTrigger.command)
messageTrigger.action(bot, user, userID, channelID, message);
}
}
}
if (botModule.onMessage) botModule.onMessage(bot, user, userID, channelID, message);
}
}
};
(() => {
bot = new Discord.Client({
token: require("./token.json").token,
autorun: true
});
bot.on("ready", EventHandlers.onReady);
bot.on("disconnect", EventHandlers.onDisconnect);
bot.on("message", EventHandlers.onMessage);
})();

4
wrapper/npm-shrinkwrap.json generated Normal file
View File

@ -0,0 +1,4 @@
{
"name": "node-boilerplate",
"version": "1.0.0"
}