Update naming and formatting for core .js files
This commit is contained in:
parent
1d5ba2561b
commit
1a60292860
35
.eslintrc
Normal file
35
.eslintrc
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"node": true,
|
||||||
|
"commonjs": true,
|
||||||
|
"es6": true
|
||||||
|
},
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"parserOptions": {
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"indent": [
|
||||||
|
"warn",
|
||||||
|
4,
|
||||||
|
{
|
||||||
|
"SwitchCase": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"quotes": [
|
||||||
|
"warn",
|
||||||
|
"double"
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"no-undef": "error",
|
||||||
|
"no-unused-vars": "warn",
|
||||||
|
"eqeqeq": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,102 +0,0 @@
|
||||||
const CoreUtil = require("./Util.js");
|
|
||||||
const Camo = require("camo");
|
|
||||||
const CronJob = require("cron").CronJob;
|
|
||||||
const Discord = require("discord.js");
|
|
||||||
const HandleGuildMessage = require("./HandleGuildMessage");
|
|
||||||
const InternalConfig = require("./internal-config.json");
|
|
||||||
const RequireAll = require("require-all");
|
|
||||||
const Util = require("./Util.js");
|
|
||||||
|
|
||||||
let neDB;
|
|
||||||
|
|
||||||
module.exports = class Client extends Discord.Client {
|
|
||||||
/**
|
|
||||||
* Construct a new Discord.Client with some added functionality
|
|
||||||
* @param {string} token bot token
|
|
||||||
* @param {string} commandsDir location of dir containing commands .js files
|
|
||||||
* @param {*} guildDataModel GuildData model to be used for app; must extend BaseGuildData
|
|
||||||
*/
|
|
||||||
constructor(token, commandsDir, guildDataModel) {
|
|
||||||
super({
|
|
||||||
messageCacheMaxSize: 16,
|
|
||||||
disabledEvents: InternalConfig.disabledEvents
|
|
||||||
});
|
|
||||||
|
|
||||||
this._token = token;
|
|
||||||
this.commandsDir = commandsDir;
|
|
||||||
this.guildDataModel = guildDataModel;
|
|
||||||
|
|
||||||
this.commands = RequireAll(this.commandsDir);
|
|
||||||
|
|
||||||
this.on("ready", this._onReady);
|
|
||||||
this.on("message", this._onMessage);
|
|
||||||
this.on("debug", this._onDebug);
|
|
||||||
this.on("guildCreate", this._onGuildCreate);
|
|
||||||
this.on("guildDelete", this._onGuildDelete);
|
|
||||||
process.on("uncaughtException", err => this._onUnhandledException(this, err));
|
|
||||||
}
|
|
||||||
|
|
||||||
_onReady() {
|
|
||||||
this.user.setGame(InternalConfig.website.replace(/^https?:\/\//, ""));
|
|
||||||
CoreUtil.dateLog(`Registered bot ${this.user.username}`);
|
|
||||||
|
|
||||||
this.removeDeletedGuilds();
|
|
||||||
}
|
|
||||||
|
|
||||||
_onMessage(message) {
|
|
||||||
if (message.channel.type === "text" && message.member)
|
|
||||||
HandleGuildMessage(this, message, this.commands);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onDebug(info) {
|
|
||||||
info = info.replace(/Authenticated using token [^ ]+/, "Authenticated using token [redacted]");
|
|
||||||
if (!InternalConfig.debugIgnores.some(x => info.startsWith(x)))
|
|
||||||
CoreUtil.dateDebug(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onGuildCreate(guild) {
|
|
||||||
CoreUtil.dateLog(`Added to guild ${guild.name}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onGuildDelete(guild) {
|
|
||||||
this.guildDataModel.findOneAndDelete({ guildID: guild.id });
|
|
||||||
|
|
||||||
CoreUtil.dateLog(`Removed from guild ${guild.name}, removing data for this guild`);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onUnhandledException(client, err) {
|
|
||||||
CoreUtil.dateError("Unhandled exception!\n", err);
|
|
||||||
CoreUtil.dateLog("Destroying existing client...");
|
|
||||||
client.destroy().then(() => {
|
|
||||||
CoreUtil.dateLog("Client destroyed, recreating...");
|
|
||||||
setTimeout(() => client.login(client._token), InternalConfig.reconnectTimeout);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bootstrap() {
|
|
||||||
Camo.connect("nedb://guilds-data").then(db => {
|
|
||||||
neDB = db;
|
|
||||||
new CronJob(InternalConfig.dbCompactionSchedule, compactCollections, null, true);
|
|
||||||
|
|
||||||
this.emit("beforeLogin");
|
|
||||||
this.login(this._token);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
removeDeletedGuilds() {
|
|
||||||
this.guildDataModel.find().then(guildDatas => {
|
|
||||||
for (let guildData of guildDatas)
|
|
||||||
if (!this.guilds.get(guildData.guildID))
|
|
||||||
guildData.delete();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function compactCollections() {
|
|
||||||
/*I realise it is a bit of a cheat to just access _collections in this manner, but in the absence of
|
|
||||||
camo actually having any kind of solution for this it's the easiest method I could come up with.
|
|
||||||
Maybe at some point in future I should fork camo and add this feature. The compaction function is NeDB only
|
|
||||||
and camo is designed to work with both NeDB and MongoDB, which is presumably why it doesn't alraedy exist */
|
|
||||||
for (let collectionName of Object.keys(neDB._collections))
|
|
||||||
neDB._collections[collectionName].persistence.compactDatafile();
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
module.exports = class Command {
|
|
||||||
constructor({ name, description, syntax, admin, invoke }) {
|
|
||||||
this.name = name;
|
|
||||||
this.description = description;
|
|
||||||
this.syntax = syntax;
|
|
||||||
this.admin = admin;
|
|
||||||
this.invoke = invoke;
|
|
||||||
|
|
||||||
const params = this.syntax.split(/ +/);
|
|
||||||
const optionalParams = params.filter(x => x.match(/^\[.+\]$/));
|
|
||||||
|
|
||||||
this.maxParamCount = params.length - 1;
|
|
||||||
this.expectedParamCount = this.maxParamCount - optionalParams.length;
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,53 +0,0 @@
|
||||||
const RequireAll = require("require-all");
|
|
||||||
|
|
||||||
const internalCommands = RequireAll(__dirname + "/core-commands");
|
|
||||||
|
|
||||||
function handleGuildMessage(client, message, commands) {
|
|
||||||
if (isCommand(message))
|
|
||||||
client.guildDataModel.findOne({ guildID: message.guild.id })
|
|
||||||
.then(guildData =>
|
|
||||||
handleGuildCommand(
|
|
||||||
client,
|
|
||||||
message,
|
|
||||||
Object.assign({}, internalCommands, commands),
|
|
||||||
guildData || client.guildDataModel.create({ guildID: message.guild.id })
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleGuildCommand(client, message, commands, guildData) {
|
|
||||||
const { botName, isMemberAdmin, params, command } = parseDetails(message, commands);
|
|
||||||
|
|
||||||
if (!command)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (params.length < command.expectedParamCount)
|
|
||||||
message.reply(`Incorrect syntax!\n**Expected:** *${botName} ${command.syntax}*\n**Need help?** *${botName} help*`);
|
|
||||||
|
|
||||||
else if (isMemberAdmin || !command.admin)
|
|
||||||
command.invoke({ message, params, guildData, client, commands, isMemberAdmin })
|
|
||||||
.then(response => {
|
|
||||||
guildData.save();
|
|
||||||
if (response) message.reply(response);
|
|
||||||
})
|
|
||||||
.catch(err => err && message.reply(err));
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseDetails(message, commands) {
|
|
||||||
const split = message.content.split(/ +/);
|
|
||||||
const commandName = Object.keys(commands).find(x =>
|
|
||||||
/**/ commands[x].name.toLowerCase() === (split[1] || "").toLowerCase());
|
|
||||||
|
|
||||||
return {
|
|
||||||
botName: "@" + (message.guild.me.nickname || message.guild.me.user.username),
|
|
||||||
isMemberAdmin: message.member.permissions.has("ADMINISTRATOR"),
|
|
||||||
params: split.slice(2, split.length),
|
|
||||||
command: commands[commandName]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCommand(message) {
|
|
||||||
//criteria for a command is bot being mentioned
|
|
||||||
return new RegExp(`^<@!?${/[0-9]{18}/.exec(message.guild.me.toString())[0]}>`).exec(message.content);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = handleGuildMessage;
|
|
|
@ -1,74 +0,0 @@
|
||||||
// @ts-ignore
|
|
||||||
const InternalConfig = require("./internal-config.json");
|
|
||||||
const Console = require("console");
|
|
||||||
const SimpleFileWriter = require("simple-file-writer");
|
|
||||||
|
|
||||||
const logWriter = new SimpleFileWriter("./console.log");
|
|
||||||
const debugLogWriter = new SimpleFileWriter("./debug.log");
|
|
||||||
|
|
||||||
function ask(client, textChannel, member, question) {
|
|
||||||
//return a promise which will resolve once the user next sends a message in this textChannel
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const cancelAsk = () => {
|
|
||||||
client.removeListener("message", handler);
|
|
||||||
textChannel.send("Response to question timed out");
|
|
||||||
};
|
|
||||||
|
|
||||||
const askTimeout = setTimeout(cancelAsk, InternalConfig.askTimeout);
|
|
||||||
|
|
||||||
const handler = responseMessage => {
|
|
||||||
if (responseMessage.channel.id === textChannel.id && responseMessage.member && responseMessage.member.id === member.id) {
|
|
||||||
clearTimeout(askTimeout);
|
|
||||||
resolve(responseMessage);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
client.on("message", handler);
|
|
||||||
|
|
||||||
textChannel.send(member.toString() + " " + question).catch(reject);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function dateLog(...args) {
|
|
||||||
doDateLog(Console.log, logWriter, args, "INFO");
|
|
||||||
}
|
|
||||||
|
|
||||||
function dateError(...args) {
|
|
||||||
doDateLog(Console.error, logWriter, args, "ERROR");
|
|
||||||
}
|
|
||||||
|
|
||||||
function dateDebugError(...args) {
|
|
||||||
doDateLog(null, null, args, "DEBUG ERROR");
|
|
||||||
}
|
|
||||||
|
|
||||||
function dateDebug(...args) {
|
|
||||||
doDateLog(null, null, args, "DEBUG");
|
|
||||||
}
|
|
||||||
|
|
||||||
function doDateLog(consoleMethod, fileWriter, args, prefix = "") {
|
|
||||||
args = formatArgs([`[${prefix}]`].concat(args));
|
|
||||||
|
|
||||||
if (consoleMethod !== null)
|
|
||||||
consoleMethod.apply(this, args);
|
|
||||||
|
|
||||||
if (fileWriter !== null)
|
|
||||||
fileWriter.write(formatArgsForFile(args));
|
|
||||||
|
|
||||||
debugLogWriter.write(formatArgsForFile(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatArgs(args) {
|
|
||||||
return [`[${new Date().toUTCString()}]`].concat(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatArgsForFile(args) {
|
|
||||||
return args.join(" ") + "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
dateError,
|
|
||||||
dateLog,
|
|
||||||
dateDebug,
|
|
||||||
dateDebugError,
|
|
||||||
ask
|
|
||||||
};
|
|
|
@ -2,7 +2,7 @@ const Camo = require("camo");
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
module.exports = class BaseEmbeddedData extends Camo.EmbeddedDocument {
|
module.exports = class BaseEmbeddedData extends Camo.EmbeddedDocument {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -1,9 +1,9 @@
|
||||||
const Camo = require("camo");
|
const Camo = require("camo");
|
||||||
|
|
||||||
module.exports = class BaseGuildData extends Camo.Document {
|
module.exports = class BaseGuildData extends Camo.Document {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.guildID = String;
|
this.guildID = String;
|
||||||
}
|
}
|
||||||
};
|
};
|
102
discord-bot-core/client.js
Normal file
102
discord-bot-core/client.js
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
const CoreUtil = require("./util.js");
|
||||||
|
const Camo = require("camo");
|
||||||
|
const CronJob = require("cron").CronJob;
|
||||||
|
const Discord = require("discord.js");
|
||||||
|
const HandleGuildMessage = require("./handle-guild-message.js");
|
||||||
|
// @ts-ignore
|
||||||
|
const InternalConfig = require("./internal-config.json");
|
||||||
|
const RequireAll = require("require-all");
|
||||||
|
|
||||||
|
let neDB;
|
||||||
|
|
||||||
|
module.exports = class Client extends Discord.Client {
|
||||||
|
/**
|
||||||
|
* Construct a new Discord.Client with some added functionality
|
||||||
|
* @param {string} token bot token
|
||||||
|
* @param {string} commandsDir location of dir containing commands .js files
|
||||||
|
* @param {*} guildDataModel GuildData model to be used for app; must extend BaseGuildData
|
||||||
|
*/
|
||||||
|
constructor(token, commandsDir, guildDataModel) {
|
||||||
|
super({
|
||||||
|
messageCacheMaxSize: 16,
|
||||||
|
disabledEvents: InternalConfig.disabledEvents
|
||||||
|
});
|
||||||
|
|
||||||
|
this._token = token;
|
||||||
|
this.commandsDir = commandsDir;
|
||||||
|
this.guildDataModel = guildDataModel;
|
||||||
|
|
||||||
|
this.commands = RequireAll(this.commandsDir);
|
||||||
|
|
||||||
|
this.on("ready", this._onReady);
|
||||||
|
this.on("message", this._onMessage);
|
||||||
|
this.on("debug", this._onDebug);
|
||||||
|
this.on("guildCreate", this._onGuildCreate);
|
||||||
|
this.on("guildDelete", this._onGuildDelete);
|
||||||
|
process.on("uncaughtException", err => this._onUnhandledException(this, err));
|
||||||
|
}
|
||||||
|
|
||||||
|
_onReady() {
|
||||||
|
this.user.setGame(InternalConfig.website.replace(/^https?:\/\//, ""));
|
||||||
|
CoreUtil.dateLog(`Registered bot ${this.user.username}`);
|
||||||
|
|
||||||
|
this.removeDeletedGuilds();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onMessage(message) {
|
||||||
|
if (message.channel.type === "text" && message.member)
|
||||||
|
HandleGuildMessage(this, message, this.commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onDebug(info) {
|
||||||
|
info = info.replace(/Authenticated using token [^ ]+/, "Authenticated using token [redacted]");
|
||||||
|
if (!InternalConfig.debugIgnores.some(x => info.startsWith(x)))
|
||||||
|
CoreUtil.dateDebug(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onGuildCreate(guild) {
|
||||||
|
CoreUtil.dateLog(`Added to guild ${guild.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onGuildDelete(guild) {
|
||||||
|
this.guildDataModel.findOneAndDelete({ guildID: guild.id });
|
||||||
|
|
||||||
|
CoreUtil.dateLog(`Removed from guild ${guild.name}, removing data for this guild`);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onUnhandledException(client, err) {
|
||||||
|
CoreUtil.dateError("Unhandled exception!\n", err);
|
||||||
|
CoreUtil.dateLog("Destroying existing client...");
|
||||||
|
client.destroy().then(() => {
|
||||||
|
CoreUtil.dateLog("Client destroyed, recreating...");
|
||||||
|
setTimeout(() => client.login(client._token), InternalConfig.reconnectTimeout);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bootstrap() {
|
||||||
|
Camo.connect("nedb://guilds-data").then(db => {
|
||||||
|
neDB = db;
|
||||||
|
new CronJob(InternalConfig.dbCompactionSchedule, compactCollections, null, true);
|
||||||
|
|
||||||
|
this.emit("beforeLogin");
|
||||||
|
this.login(this._token);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
removeDeletedGuilds() {
|
||||||
|
this.guildDataModel.find().then(guildDatas => {
|
||||||
|
for (let guildData of guildDatas)
|
||||||
|
if (!this.guilds.get(guildData.guildID))
|
||||||
|
guildData.delete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function compactCollections() {
|
||||||
|
/*I realise it is a bit of a cheat to just access _collections in this manner, but in the absence of
|
||||||
|
camo actually having any kind of solution for this it's the easiest method I could come up with.
|
||||||
|
Maybe at some point in future I should fork camo and add this feature. The compaction function is NeDB only
|
||||||
|
and camo is designed to work with both NeDB and MongoDB, which is presumably why it doesn't alraedy exist */
|
||||||
|
for (let collectionName of Object.keys(neDB._collections))
|
||||||
|
neDB._collections[collectionName].persistence.compactDatafile();
|
||||||
|
}
|
15
discord-bot-core/command.js
Normal file
15
discord-bot-core/command.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
module.exports = class Command {
|
||||||
|
constructor({ name, description, syntax, admin, invoke }) {
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
this.syntax = syntax;
|
||||||
|
this.admin = admin;
|
||||||
|
this.invoke = invoke;
|
||||||
|
|
||||||
|
const params = this.syntax.split(/ +/);
|
||||||
|
const optionalParams = params.filter(x => x.match(/^\[.+\]$/));
|
||||||
|
|
||||||
|
this.maxParamCount = params.length - 1;
|
||||||
|
this.expectedParamCount = this.maxParamCount - optionalParams.length;
|
||||||
|
}
|
||||||
|
};
|
53
discord-bot-core/handle-guild-message.js
Normal file
53
discord-bot-core/handle-guild-message.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
const RequireAll = require("require-all");
|
||||||
|
|
||||||
|
const internalCommands = RequireAll(__dirname + "/core-commands");
|
||||||
|
|
||||||
|
function handleGuildMessage(client, message, commands) {
|
||||||
|
if (isCommand(message))
|
||||||
|
client.guildDataModel.findOne({ guildID: message.guild.id })
|
||||||
|
.then(guildData =>
|
||||||
|
handleGuildCommand(
|
||||||
|
client,
|
||||||
|
message,
|
||||||
|
Object.assign({}, internalCommands, commands),
|
||||||
|
guildData || client.guildDataModel.create({ guildID: message.guild.id })
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleGuildCommand(client, message, commands, guildData) {
|
||||||
|
const { botName, isMemberAdmin, params, command } = parseDetails(message, commands);
|
||||||
|
|
||||||
|
if (!command)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (params.length < command.expectedParamCount)
|
||||||
|
message.reply(`Incorrect syntax!\n**Expected:** *${botName} ${command.syntax}*\n**Need help?** *${botName} help*`);
|
||||||
|
|
||||||
|
else if (isMemberAdmin || !command.admin)
|
||||||
|
command.invoke({ message, params, guildData, client, commands, isMemberAdmin })
|
||||||
|
.then(response => {
|
||||||
|
guildData.save();
|
||||||
|
if (response) message.reply(response);
|
||||||
|
})
|
||||||
|
.catch(err => err && message.reply(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseDetails(message, commands) {
|
||||||
|
const split = message.content.split(/ +/);
|
||||||
|
const commandName = Object.keys(commands).find(x =>
|
||||||
|
/**/ commands[x].name.toLowerCase() === (split[1] || "").toLowerCase());
|
||||||
|
|
||||||
|
return {
|
||||||
|
botName: "@" + (message.guild.me.nickname || message.guild.me.user.username),
|
||||||
|
isMemberAdmin: message.member.permissions.has("ADMINISTRATOR"),
|
||||||
|
params: split.slice(2, split.length),
|
||||||
|
command: commands[commandName]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCommand(message) {
|
||||||
|
//criteria for a command is bot being mentioned
|
||||||
|
return new RegExp(`^<@!?${/[0-9]{18}/.exec(message.guild.me.toString())[0]}>`).exec(message.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = handleGuildMessage;
|
|
@ -2,13 +2,13 @@
|
||||||
const InternalConfig = require("./internal-config.json");
|
const InternalConfig = require("./internal-config.json");
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Client: require("./Client.js"),
|
Client: require("./client.js"),
|
||||||
BaseGuildData: require("./BaseGuildData.js"),
|
BaseGuildData: require("./base-guild-data.js"),
|
||||||
BaseEmbeddedData: require("./BaseEmbeddedData.js"),
|
BaseEmbeddedData: require("./base-embedded-data.js"),
|
||||||
Command: require("./Command.js"),
|
Command: require("./command.js"),
|
||||||
util: require("./Util.js"),
|
util: require("./util.js"),
|
||||||
details: {
|
details: {
|
||||||
website: InternalConfig.website,
|
website: InternalConfig.website,
|
||||||
discordInvite: InternalConfig.discordInvite
|
discordInvite: InternalConfig.discordInvite
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
{
|
{
|
||||||
"dbCompactionSchedule": "0 * * * * *",
|
"dbCompactionSchedule": "0 * * * * *",
|
||||||
"restartSchedule": "0 0 0 * * *",
|
"restartSchedule": "0 0 0 * * *",
|
||||||
"restartTimeout": 5000,
|
"restartTimeout": 5000,
|
||||||
"website": "https://benji7425.github.io",
|
"website": "https://benji7425.github.io",
|
||||||
"discordInvite": "https://discord.gg/SSkbwSJ",
|
"discordInvite": "https://discord.gg/SSkbwSJ",
|
||||||
"debugIgnores": [
|
"debugIgnores": [
|
||||||
"[ws] [connection] Sending a heartbeat",
|
"[ws] [connection] Sending a heartbeat",
|
||||||
"[ws] [connection] Heartbeat acknowledged"
|
"[ws] [connection] Heartbeat acknowledged"
|
||||||
],
|
],
|
||||||
"disabledEvents": [
|
"disabledEvents": [
|
||||||
"CHANNEL_PINS_UPDATE",
|
"CHANNEL_PINS_UPDATE",
|
||||||
"GUILD_BAN_ADD",
|
"GUILD_BAN_ADD",
|
||||||
"GUILD_BAN_REMOVE",
|
"GUILD_BAN_REMOVE",
|
||||||
"PRESENCE_UPDATE",
|
"PRESENCE_UPDATE",
|
||||||
"TYPING_START",
|
"TYPING_START",
|
||||||
"USER_NOTE_UPDATE",
|
"USER_NOTE_UPDATE",
|
||||||
"USER_SETTINGS_UPDATE"
|
"USER_SETTINGS_UPDATE"
|
||||||
],
|
],
|
||||||
"askTimeout": 60000
|
"askTimeout": 60000
|
||||||
}
|
}
|
|
@ -11,23 +11,23 @@ restart();
|
||||||
new CronJob(InternalConfig.restartSchedule, restart, null, true);
|
new CronJob(InternalConfig.restartSchedule, restart, null, true);
|
||||||
|
|
||||||
function restart() {
|
function restart() {
|
||||||
ensureKilledInstance()
|
ensureKilledInstance()
|
||||||
.then(bootstrapNewInstance)
|
.then(bootstrapNewInstance)
|
||||||
.catch(DiscordUtil.dateError);
|
.catch(DiscordUtil.dateError);
|
||||||
}
|
}
|
||||||
|
|
||||||
function bootstrapNewInstance() {
|
function bootstrapNewInstance() {
|
||||||
instance = fork(process.argv[2]);
|
instance = fork(process.argv[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureKilledInstance() {
|
function ensureKilledInstance() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (instance) {
|
if (instance) {
|
||||||
instance.kill();
|
instance.kill();
|
||||||
DiscordUtil.dateLog(`Killed existing instance for scheduled restart in ${InternalConfig.restartTimeout / 1000} sec`);
|
DiscordUtil.dateLog(`Killed existing instance for scheduled restart in ${InternalConfig.restartTimeout / 1000} sec`);
|
||||||
setTimeout(resolve, InternalConfig.restartTimeout);
|
setTimeout(resolve, InternalConfig.restartTimeout);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
74
discord-bot-core/util.js
Normal file
74
discord-bot-core/util.js
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
// @ts-ignore
|
||||||
|
const InternalConfig = require("./internal-config.json");
|
||||||
|
const Console = require("console");
|
||||||
|
const SimpleFileWriter = require("simple-file-writer");
|
||||||
|
|
||||||
|
const logWriter = new SimpleFileWriter("./console.log");
|
||||||
|
const debugLogWriter = new SimpleFileWriter("./debug.log");
|
||||||
|
|
||||||
|
function ask(client, textChannel, member, question) {
|
||||||
|
//return a promise which will resolve once the user next sends a message in this textChannel
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const cancelAsk = () => {
|
||||||
|
client.removeListener("message", handler);
|
||||||
|
textChannel.send("Response to question timed out");
|
||||||
|
};
|
||||||
|
|
||||||
|
const askTimeout = setTimeout(cancelAsk, InternalConfig.askTimeout);
|
||||||
|
|
||||||
|
const handler = responseMessage => {
|
||||||
|
if (responseMessage.channel.id === textChannel.id && responseMessage.member && responseMessage.member.id === member.id) {
|
||||||
|
clearTimeout(askTimeout);
|
||||||
|
resolve(responseMessage);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
client.on("message", handler);
|
||||||
|
|
||||||
|
textChannel.send(member.toString() + " " + question).catch(reject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function dateLog(...args) {
|
||||||
|
doDateLog(Console.log, logWriter, args, "INFO");
|
||||||
|
}
|
||||||
|
|
||||||
|
function dateError(...args) {
|
||||||
|
doDateLog(Console.error, logWriter, args, "ERROR");
|
||||||
|
}
|
||||||
|
|
||||||
|
function dateDebugError(...args) {
|
||||||
|
doDateLog(null, null, args, "DEBUG ERROR");
|
||||||
|
}
|
||||||
|
|
||||||
|
function dateDebug(...args) {
|
||||||
|
doDateLog(null, null, args, "DEBUG");
|
||||||
|
}
|
||||||
|
|
||||||
|
function doDateLog(consoleMethod, fileWriter, args, prefix = "") {
|
||||||
|
args = formatArgs([`[${prefix}]`].concat(args));
|
||||||
|
|
||||||
|
if (consoleMethod !== null)
|
||||||
|
consoleMethod.apply(this, args);
|
||||||
|
|
||||||
|
if (fileWriter !== null)
|
||||||
|
fileWriter.write(formatArgsForFile(args));
|
||||||
|
|
||||||
|
debugLogWriter.write(formatArgsForFile(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatArgs(args) {
|
||||||
|
return [`[${new Date().toUTCString()}]`].concat(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatArgsForFile(args) {
|
||||||
|
return args.join(" ") + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
dateError,
|
||||||
|
dateLog,
|
||||||
|
dateDebug,
|
||||||
|
dateDebugError,
|
||||||
|
ask
|
||||||
|
};
|
982
package-lock.json
generated
982
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -9,6 +9,7 @@
|
||||||
"@types/node": "9.3.0",
|
"@types/node": "9.3.0",
|
||||||
"camo": "git+https://github.com/scottwrobinson/camo.git",
|
"camo": "git+https://github.com/scottwrobinson/camo.git",
|
||||||
"discord.js": "11.2.0",
|
"discord.js": "11.2.0",
|
||||||
|
"eslint": "4.16.0",
|
||||||
"get-urls": "7.0.0",
|
"get-urls": "7.0.0",
|
||||||
"jsonfile": "3.0.1",
|
"jsonfile": "3.0.1",
|
||||||
"rss-parser": "2.12.0",
|
"rss-parser": "2.12.0",
|
||||||
|
|
Loading…
Reference in a new issue