40 Commits

Author SHA1 Message Date
jcreek 0ffd8f15a3 refactor(*): Move command functions into commands.js 2021-02-28 13:11:57 +00:00
jcreek 57421bb33c refactor(*): Move helper methods into helpers.js 2021-02-28 13:11:26 +00:00
jcreek 3f6eab8e11 refactor(*): Move logger code into logger.js 2021-02-28 10:07:24 +00:00
jcreek fa1fada289 fix(*): Add log and try catch loop to prevent getting a user from a mention throwing uncleanly 2021-02-28 09:54:16 +00:00
jcreek eea9841481 feat(*): Clean up whitespace 2021-02-28 09:52:57 +00:00
jcreek 5f875bc91d feat(*): Replace some key messages with embed messages 2021-02-14 19:13:31 +00:00
jcreek dadc08d94c feat(*): Add helpers file with sendEmbedMessage function 2021-02-14 19:13:31 +00:00
jcreek 73906e1957 fix(*): Clear pokemon name and captain name when using the clear queue command 2021-02-14 19:13:31 +00:00
jcreek 84a8f2c22c fix(*): Clear pokemon name and captain ign when filling a queue 2021-02-14 19:13:31 +00:00
jcreek c6333e7d06 feat(*): Disable the automatic channel deletion 2021-02-14 19:13:31 +00:00
jcreek 36366499ed feat(*): Add manual update instructions to readme 2021-02-14 19:13:31 +00:00
jcreek 95e9003ce7 feat(*): Add future feature suggestions section to readme 2021-02-14 19:13:31 +00:00
jcreek 02dc03e5ef fix(*): Enable users with spaces in their username to delete their adventure channels
Why on earth does Discord even allow people to have spaces in their usernames?
2021-02-14 19:13:31 +00:00
jcreek d901ee4672 fix(*): Hand edge cases for the start command 2021-02-14 19:13:31 +00:00
jcreek 6865d021a3 fix(*): Use prefix rather than command text for help and adminhelp commands 2021-02-14 19:13:31 +00:00
jcreek c8dbcd9120 fix(*): Missed empty queue check for when a player leaves a queue they were the only one in 2021-02-14 19:13:31 +00:00
jcreek df6211d5e4 feat(*): Finish implementing start command 2021-02-14 19:13:31 +00:00
jcreek 17a8f98ca3 fix(*): Fix further missed lowercasing of channel names by Discord for text channels 2021-02-14 19:13:31 +00:00
Josh Creek 8d7df9d4ec feat(*): Remove potential feature from readme as it has been implemented 2021-02-14 19:13:31 +00:00
Josh Creek 2e5c468c8f feat(*): Add start command for voting to start the adventure before the queue is full 2021-02-14 19:13:31 +00:00
Josh Creek 3d51cc1551 feat(*): Update help commands to use the correct command symbol from the config file rather than hard-coding one 2021-02-14 19:13:31 +00:00
jcreek be885bc286 fix(*): Fix bug where text channel names are automatically lowercased by Discord so couldn't be deleted if the username had capital letters 2021-02-14 19:13:31 +00:00
jcreek f127e09858 fix(*): Fix broken channel deletion and add more helpful logs 2021-02-14 19:13:31 +00:00
jcreek 3191be31e1 feat(*): Add a dev token for debugging and testing in the dev discord 2021-02-14 19:13:31 +00:00
jcreek c48edec643 feat(*): Enable console logging even in production so Jord can see logs easily 2021-02-14 19:13:31 +00:00
jcreek c642d95335 feat(*): Improve logging and error handling 2021-02-14 19:13:31 +00:00
jcreek e08495bfc2 feat(*): Remove unused getCurrentDateAndTime function 2021-02-14 19:13:30 +00:00
jcreek 087fecf93a fix(*): Further fixes to elasticsearch logging to get the stacktrace coming through correctly 2021-02-14 19:13:30 +00:00
jcreek c3fcc15f06 fix(*): Make the elasticsearch logging render out log data correctly for filtering in kibana 2021-02-14 19:13:29 +00:00
jcreek 939a99b62d feat(*): Implement winston-elasticsearch for logging to Elasticsearch 2021-02-14 19:12:59 +00:00
jcreek 3aa3f7ac52 refactor(*): Move maxqueuesize to alphabetical order 2021-02-14 19:12:11 +00:00
jcreek 9459e2714c feat(*): Increase default channel delete time to 30 minutes 2021-02-14 19:11:53 +00:00
jcreek 6a3cec4a0e feat(*): Add config option to toggle auto deleting commands 2021-02-14 19:11:53 +00:00
jcreek be7ba9e28d feat(*): Add channel delete time to config file 2021-02-14 19:11:53 +00:00
jcreek c48c5f32a9 feat(*): Add config file content 2021-02-14 19:11:04 +00:00
jcreek 76a3431512 feat(*): Add timestamps to logs 2020-10-29 19:20:49 +00:00
jcreek 3c5aa62d33 feat(*): Added logs for creating and deleting channels 2020-10-29 19:20:37 +00:00
jcreek 62d9df4dcb refactor(*): Delete channels using names not ids 2020-10-29 19:20:24 +00:00
jcreek 2a915d9b31 feat(*): Change the message text 2020-10-29 19:19:57 +00:00
jcreek 54b30212c8 feat(*): Add v2.0 potential features section to readme 2020-10-27 00:01:20 +00:00
8 changed files with 856 additions and 311 deletions
+17 -2
View File
@@ -12,6 +12,7 @@ Commands:
* q pokemon ign - Join the queue (first player can set the pokemon and their in-game name) * q pokemon ign - Join the queue (first player can set the pokemon and their in-game name)
* lq - Leave the queue * lq - Leave the queue
* sq - Show the queue * sq - Show the queue
* start - Vote to start the adventure early, without the full number of players (all players in the queue must use this to start early)
* cq - Clear the queue (admin-only, not to be shared with normal users) * cq - Clear the queue (admin-only, not to be shared with normal users)
* ru - Remove a tagged user from the queue (admin-only, not to be shared with normal users) * ru - Remove a tagged user from the queue (admin-only, not to be shared with normal users)
* dc - Delete an adventure channel (only the captain can use this) * dc - Delete an adventure channel (only the captain can use this)
@@ -34,12 +35,24 @@ For a sanity check, you can run `docker images` and it should be displayed in th
## Running the Docker container ## Running the Docker container
Running the bot with -d runs the container in detatched mode (as in it runs in the background). If you want to see what is happening, remove that option. Running the bot with --detach runs the container in detatched mode (as in it runs in the background). If you want to see what is happening, remove that option.
`docker run -d denbot` `docker run --detach --name denbot denbot`
You can use CTRL+C to exit out of this command window. If you're using Windows, Docker Desktop will now show your bot under 'Containers/Apps', from where you can easily stop and start it using the GUI. You can use CTRL+C to exit out of this command window. If you're using Windows, Docker Desktop will now show your bot under 'Containers/Apps', from where you can easily stop and start it using the GUI.
## Updating
If doing it manually, in the folder with all the files run:
```
docker stop denbot
docker rm denbot
docker image rm denbot
docker build -t denbot .
docker run --detach --name denbot denbot
```
### More information ### More information
If you want more of a sanity check here are some following commands you can run the following commands: If you want more of a sanity check here are some following commands you can run the following commands:
@@ -51,4 +64,6 @@ To access the command line inside the docker container you can run:
`docker exec -it <container id> /bin/bash` `docker exec -it <container id> /bin/bash`
## Future feature suggestions
* If the party want to continue with the same people and the same link code they can do !again (only in the private chat) to run again and get the party for 30 more mins (denBot would also post the link code again so people can see it without scrolling) - using something like https://stackoverflow.com/questions/36563749/how-i-extend-settimeout-on-nodejs and an array of all the queues, removing those and their deletion functions each time an adventure channel is deleted
+362
View File
@@ -0,0 +1,362 @@
const {
checkQueue,
deleteChannel,
getChannelName,
getUserFromMention,
sendEmbedMessage,
} = require('./helpers.js');
let playerQueue = [];
let playerVotes = [];
let pokemonName = '';
let captainInGameName = '';
module.exports = {
matchCommand: function (Discord, config, logger, message, command, args) {
switch (command) {
case 'help':
commandHelp(Discord, config, logger, message, command, args);
break;
case 'adminhelp':
commandHelp(Discord, config, logger, message, command, args);
break;
case 'q':
commandJoinQueue(Discord, config, logger, message, command, args);
break;
case 'lq':
commandLeaveQueue(Discord, config, logger, message, command, args);
break;
case 'start':
commandStartEarly(Discord, config, logger, message, command, args);
break;
case 'sq':
commandShowQueue(Discord, config, logger, message, command, args);
break;
case 'cq':
commandClearQueue(Discord, config, logger, message, command, args);
break;
case 'ru':
commandRemoveUserFromQueue(Discord, config, logger, message, command, args);
break;
case 'dc':
commandDeleteChannel(Discord, config, logger, message, command, args);
break;
default:
message.channel.send('That command is not one I know yet!');
}
},
};
function commandHelp(Discord, config, logger, message, command, args) {
const helpMessage = `
The commands available to you are:
\`${config.prefix}q\` - Join the queue
\`${config.prefix}q <pokemon> <ign>\` - Join the queue (first player can set the pokemon and their in-game name)
\`${config.prefix}lq\` - Leave the queue
\`${config.prefix}sq\` - Show the queue
\`${config.prefix}start\` - Vote to start the adventure early, without the full number of players (all players in the queue must use this to start early)
\`${config.prefix}dc\` - Delete an adventure channel (only the captain can use this)
`;
message.channel.send(helpMessage);
}
function commandAdminHelp(Discord, config, logger, message, command, args) {
const helpMessage = `
The commands available to you are:
\`${config.prefix}q\` - Join the queue
\`${config.prefix}q <pokemon> <ign>\` - Join the queue (first player can set the pokemon and their in-game name)
\`${config.prefix}lq\` - Leave the queue
\`${config.prefix}sq\` - Show the queue
\`${config.prefix}start\` - Vote to start the adventure early, without the full number of players (all players in the queue must use this to start early)
\`${config.prefix}cq\` - Clear the queue (admin-only, not to be shared with normal users)
\`${config.prefix}ru\` - Remove a tagged user from the queue (admin-only, not to be shared with normal users)
\`${config.prefix}dc\` - Delete an adventure channel (only the captain can use this)
`;
message.channel.send(helpMessage);
}
function commandJoinQueue(Discord, config, logger, message, command, args) {
if (!playerQueue.includes(message.author)) {
// Delete the message with the bot command
if (config.deletecommandstoggle) {
message.delete();
}
logger.info(`${message.author.username} used command q in ${message.channel.name}`);
logger.info(`${message.author.username} joined the queue.`);
sendEmbedMessage(
logger,
message,
Discord,
'Queue in progress',
`${message.author.username} joined the queue.`
);
playerQueue.push(message.author);
if (playerQueue.length === 1 && args[0] && args[1]) {
// First player can submit pokemon and in-game name
pokemonName = args[0];
captainInGameName = args[1];
}
checkQueue(
message,
playerQueue,
playerVotes,
logger,
Discord,
pokemonName,
captainInGameName,
config
);
} else if (playerQueue.includes(message.author)) {
// Delete the message with the bot command
if (config.deletecommandstoggle) {
message.delete();
}
logger.info(`${message.author.username} used command q in ${message.channel.name}`);
message.channel.send(
`${message.author.username} is too keen, you're already in the queue mate!`
);
}
}
function commandLeaveQueue(Discord, config, logger, message, command, args) {
if (playerQueue.includes(message.author)) {
// Delete the message with the bot command
if (config.deletecommandstoggle) {
message.delete();
}
logger.info(`${message.author.username} used command lq in ${message.channel.name}`);
for (var i = 0; i < playerQueue.length; i++) {
if (playerQueue[i] === message.author) {
playerQueue.splice(i, 1);
i--;
}
}
for (var i = 0; i < playerVotes.length; i++) {
if (playerVotes[i] === message.author) {
playerVotes.splice(i, 1);
i--;
}
}
logger.info(`${message.author.username} left the queue.`);
sendEmbedMessage(
logger,
message,
Discord,
'Queue in progress',
`${message.author.username} left the queue.`
);
checkQueue(
message,
playerQueue,
playerVotes,
logger,
Discord,
pokemonName,
captainInGameName,
config
);
} else if (!playerQueue.includes(message.author)) {
// Delete the message with the bot command
if (config.deletecommandstoggle) {
message.delete();
}
logger.info(`${message.author.username} used command lq in ${message.channel.name}`);
message.channel.send(
`${message.author.username} you can't leave the queue before you've joined it!`
);
}
}
function commandStartEarly(Discord, config, logger, message, command, args) {
// Vote to start early with only the users currently in the queue
// Delete the message with the bot command
if (config.deletecommandstoggle) {
message.delete();
}
logger.info(`${message.author.username} used command ${command} in ${message.channel.name}`);
if (playerQueue.length == 0) {
message.channel.send(
`You can only vote to start an adventure early if there is a queue. You can start a queue by using the command ${config.prefix}help`
);
} else if (!playerQueue.includes(message.author)) {
message.channel.send(
`You can only vote to start an adventure early if you are in the queue. You can join the queue by using the command ${config.prefix}q`
);
} else if (playerVotes.length == 0) {
// First player to vote
playerVotes.push(message.author);
sendEmbedMessage(
logger,
message,
Discord,
'Voting to start early',
`${message.author.username} voted to start the adventure.\nAll other players in the queue must also vote to start the adventure for it to begin without ${config.maxqueuesize} players.`
);
} else if (playerVotes.includes(message.author.username)) {
sendEmbedMessage(
logger,
message,
Discord,
'Voting to start early',
`${message.author.username} has already voted to start the adventure.\nAll other players in the queue must also vote to start the adventure for it to begin without ${config.maxqueuesize} players.`
);
} else if (playerVotes.length < config.maxqueuesize) {
// Not the first player to vote
playerVotes.push(message.author);
if (playerVotes.length < playerQueue.length) {
// Still not all players voted
sendEmbedMessage(
logger,
message,
Discord,
'Voting to start early',
`${message.author.username} voted to start the adventure.\nAll other players in the queue must also vote to start the adventure for it to begin without ${config.maxqueuesize} players.`
);
} else {
// All players have voted, we can start the adventure early
checkQueue(
message,
playerQueue,
playerVotes,
logger,
Discord,
pokemonName,
captainInGameName,
config
);
}
}
}
function commandShowQueue(Discord, config, logger, message, command, args) {
// Delete the message with the bot command
if (config.deletecommandstoggle) {
message.delete();
}
logger.info(`${message.author.username} used command sq in ${message.channel.name}`);
checkQueue(
message,
playerQueue,
playerVotes,
logger,
Discord,
pokemonName,
captainInGameName,
config
);
}
function commandClearQueue(Discord, config, logger, message, command, args) {
// Delete the message with the bot command
if (config.deletecommandstoggle) {
message.delete();
}
logger.info(`${message.author.username} used command cq in ${message.channel.name}`);
if (playerQueue.length === 0) {
message.channel.send(`The queue is already empty...`);
logger.info(
`${message.author.username} attempted to clear the queue but it was already empty.`
);
} else {
playerQueue = [];
playerVotes = [];
message.channel.send(`The queue has been cleared!`);
logger.info(`${message.author.username} cleared the queue and the votes.`);
pokemonName = '';
captainInGameName = '';
logger.info('Emptied pokemon name and captain name');
}
}
function commandRemoveUserFromQueue(Discord, config, logger, message, command, args) {
// Remove a tagged user from the queue
if (args[0]) {
// Delete the message with the bot command
if (config.deletecommandstoggle) {
message.delete();
}
try {
const user = getUserFromMention(args[0], logger, message);
logger.info(`${message.author.username} used command ru ${user} in ${message.channel.name}`);
if (playerQueue.includes(user)) {
// Search for the user
for (var i = 0; i < playerQueue.length; i++) {
if (playerQueue[i].username === user.username) {
playerQueue.splice(i, 1);
i--;
}
}
for (var i = 0; i < playerVotes.length; i++) {
if (playerVotes[i].username === user.username) {
playerVotes.splice(i, 1);
i--;
}
}
message.channel.send(`${user.username} removed from queue.`);
logger.info(`${message.author.username} removed ${user.username} from queue.`);
checkQueue(
message,
playerQueue,
playerVotes,
logger,
Discord,
pokemonName,
captainInGameName,
config
);
} else if (!playerQueue.includes(user)) {
message.channel.send(`${user.username} is not in the queue.`);
logger.info(
`${message.author.username} attempted to remove ${user.username} from queue but they weren't in it.`
);
}
} catch (error) {
logger.error(`Tried to remove user ${args[0]} from queue but got an error: ${error}`);
message.channel.send(`Failed to remove ${args[0]} from the queue.`);
}
}
}
function commandDeleteChannel(Discord, config, logger, message, command, args) {
logger.info(`${message.author.username} used command dc in ${message.channel.name}`);
// Determine if they can delete the channel (i.e. it's their temporary one) - compare the name
if (message.channel.name === getChannelName(message.author.username)) {
deleteChannel(message, message.channel.name, logger);
logger.info(`${message.author.username} deleted their adventure channel.`);
} else {
message.channel.send(`${message.author.username} is not allowed to delete this channel.`);
logger.info(
`${message.author.username} attempted to delete channel ${message.channel.name} but it's not their adventure channel.`
);
}
}
+6 -1
View File
@@ -2,6 +2,11 @@
"channeldeletetimeinminutes": 30, "channeldeletetimeinminutes": 30,
"deletecommandstoggle": false, "deletecommandstoggle": false,
"maxqueuesize": 4, "maxqueuesize": 4,
"elasticsearch" : {
"elasticsearch_address" : "http://YOUR-ELASTICSEARCH-ADDRESS-GOES-HERE:9200",
"elasticsearch_index_pattern" : "[logs-denbot-]YYYY.MM.DD"
},
"prefix": "!", "prefix": "!",
"token": "NzcwMDQyMDEzMjY5NDkxNzUy.X5Xzgg.kcr_51g95iUvEcYCfbQvG1Sx8ao" "token-dev": "YOUR-DEV-TOKEN-GOES-HERE",
"token": "YOUR-TOKEN-GOES-HERE"
} }
+217
View File
@@ -0,0 +1,217 @@
const { loggers } = require('winston');
module.exports = {
checkQueue: function (
message,
playerQueue,
playerVotes,
logger,
Discord,
pokemonName,
captainInGameName,
config
) {
if (
playerQueue.length === config.maxqueuesize ||
(playerQueue.length > 0 && playerQueue.length == playerVotes.length)
) {
// Queue is full
logger.info('Queue is full');
// Post a message to say the queue is complete
module.exports.sendEmbedMessage(
logger,
message,
Discord,
'Queue full',
`The adventure is beginning - players should check their DMs\n${playerQueue
.map((player, index) => `${index + 1} - ${player.username}`)
.join('\n')}`
);
const randomCode = generateRandomCode();
const baseMessage = `@${playerQueue[0].username} is making the lobby. Here is your link code ${randomCode}`;
const adventureMessage =
pokemonName === '' && captainInGameName === ''
? `${generateQueueTitle(playerQueue, config)}\n${baseMessage}`
: `${generateQueueTitle(
playerQueue,
config
)}\nPokemon: ${pokemonName}\nCaptain's IGN: ${captainInGameName}\n${baseMessage}`;
try {
// DM users
playerQueue.forEach((player) => {
message.client.users.cache.get(player.id).send(adventureMessage);
});
} catch (error) {
logger.error('Tried to DM users: ', error);
module.exports.sendEmbedMessage(
logger,
message,
Discord,
'An error occurred',
`There was an error, please let an admin know!\n${error}`,
'#DB4437'
);
}
makeTempChannel(message, adventureMessage, playerQueue, logger);
// Clear the queue and votes
playerQueue = [];
logger.info('Emptied queue');
playerVotes = [];
logger.info('Emptied votes');
pokemonName = '';
captainInGameName = '';
logger.info('Emptied pokemon name and captain name');
} else if (playerQueue.length === 0) {
module.exports.sendEmbedMessage(
logger,
message,
Discord,
'Queue empty',
'The queue is empty :('
);
pokemonName = '';
captainInGameName = '';
} else {
// Show queue after adding
let queueTitle = generateQueueTitle(playerQueue, config);
if (pokemonName === '' && captainInGameName === '') {
module.exports.sendEmbedMessage(
logger,
message,
Discord,
queueTitle,
`${playerQueue.map((player, index) => `${index + 1} - ${player.username}`).join('\n')}`
);
} else {
module.exports.sendEmbedMessage(
logger,
message,
Discord,
queueTitle,
`**Pokemon**: ${pokemonName}\n**Captain's IGN**: ${captainInGameName}\n${playerQueue
.map((player, index) => `${index + 1} - ${player.username}`)
.join('\n')}`
);
}
}
},
deleteChannel: function (message, channelName, logger) {
try {
// Get a Channel by Name
const fetchedChannel = message.guild.channels.cache.find(
(channel) => channel.name.toLowerCase() == channelName.toLowerCase()
);
logger.info(`Deleting temp channel ${channelName}`);
fetchedChannel.delete();
} catch (error) {
logger.error(`Tried to delete channel ${channelName}: `, error);
let names = [];
message.guild.channels.cache.forEach((channel) => {
names.push(channel.name);
});
logger.info(`Channel names in cache: ${names}`);
}
},
getChannelName: function (username) {
return `${username.toLowerCase().replace(' ', '-')}s-adventurers`;
},
getUserFromMention: function (mention, logger, message) {
try {
if (!mention) return;
if (mention.startsWith('<@') && mention.endsWith('>')) {
mention = mention.slice(2, -1);
if (mention.startsWith('!')) {
mention = mention.slice(1);
}
return message.client.users.cache.get(mention);
}
} catch (error) {
logger.error('Tried to get users from mention: ', error);
}
},
sendEmbedMessage: function (logger, message, Discord, title, description, colour = '#F4B400') {
// For documentation see https://github.com/AnIdiotsGuide/discordjs-bot-guide/blob/master/first-bot/using-embeds-in-messages.md
try {
const embed = new Discord.MessageEmbed()
.setTitle(title)
.setColor(colour)
.setDescription(description)
.setFooter('This is a custom bot - if it goes down, tag Scruffy')
.setThumbnail(
'https://cdn.discordapp.com/icons/770039159506337802/a_510706276ebcf77e568900fa708e6c63.png?size=128'
)
.setTimestamp();
message.channel.send(embed);
} catch (error) {
logger.error('Failed to send embed message', error);
message.channel.send('There was a problem, sorry!');
}
},
};
function generateQueueTitle(playerQueue, config) {
return `\n===== ADVENTURE QUEUE (${playerQueue.length}/${config.maxqueuesize}) =====`;
}
function generateRandomCode() {
const num1 = Math.floor(Math.random() * 9999 + 1);
const num2 = Math.floor(Math.random() * 9999 + 1);
// Pad the start of the numbers so there's always four digits, including leading zeros
return `${num1.toString().padStart(4, '0')}-${num2.toString().padStart(4, '0')}`;
}
function makeTempChannel(message, adventureMessage, playerQueue, logger) {
const channelName = module.exports.getChannelName(playerQueue[0].username);
let createdChannelId = '';
let everyoneRole = message.guild.roles.cache.find((r) => r.name === '@everyone');
let permissionOverwrites = [
{
id: everyoneRole.id,
deny: ['VIEW_CHANNEL'],
},
];
playerQueue.forEach((player) => {
permissionOverwrites.push({
id: player.id,
allow: ['VIEW_CHANNEL'],
});
});
logger.info(`Making temp channel ${channelName}`);
message.guild.channels
.create(channelName, {
type: 'text',
permissionOverwrites: permissionOverwrites,
})
.then((createdChannel) => {
createdChannelId = createdChannel.id;
// Post in the channel
createdChannel.send(adventureMessage);
createdChannel.send(
`Please delete this channel when you're finished with it using the ${config.prefix}dc command`
);
})
.catch(logger.error);
// try {
// // Delete the channel after the configured amount of minutes
// setTimeout(function(){ deleteChannel(message, channelName); }, (config.channeldeletetimeinminutes * 1000 * 60) );
// } catch (error) {
// logger.error(`Tried to delete channel ${channelName} after timeout: `, error);
// }
}
+8 -305
View File
@@ -1,165 +1,16 @@
const Discord = require('discord.js'); const Discord = require('discord.js');
const winston = require('winston'); const winston = require('winston');
const Elasticsearch = require('winston-elasticsearch');
const config = require('./config.json'); const config = require('./config.json');
const { initialiseLogger } = require('./logger.js');
const { matchCommand } = require('./commands.js');
const client = new Discord.Client(); const client = new Discord.Client();
const logger = initialiseLogger(config, winston, Elasticsearch);
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
defaultMeta: { service: 'user-service' },
transports: [
//
// - Write all logs with level `error` and below to `error.log`
// - Write all logs with level `info` and below to `combined.log`
//
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' }),
],
});
//
// If we're not in production then log to the `console` with the format:
// `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
//
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple(),
}));
}
let playerQueue = [];
let pokemonName = '';
let captainInGameName = '';
client.login(config.token); client.login(config.token);
client.once('ready', () => { client.once('ready', () => {
logger.log('info', 'denBot logged in successfully!'); logger.info('denBot logged in successfully!');
})
/*
* Commands:
* q - Join the queue
* q pokemon ign - Join the queue (first player can set the pokemon and their in-game name)
* lq - Leave the queue
* sq - Show the queue
* cq - Clear the queue
* ru - Remove a tagged user from the queue
* dc - Delete an adventure channel (only the captain can use this)
*/
function generateQueueTitle() {
return `\n===== ADVENTURE QUEUE (${playerQueue.length}/${config.maxqueuesize}) =====`;
}
function generateRandomCode() {
const num1 = Math.floor((Math.random() * 9999) + 1);
const num2 = Math.floor((Math.random() * 9999) + 1);
// Pad the start of the numbers so there's always four digits, including leading zeros
return `${num1.toString().padStart(4, '0')}-${num2.toString().padStart(4, '0')}`;
}
function checkQueue(message) {
if (playerQueue.length === config.maxqueuesize) {
// Queue is full
logger.log('info', 'Queue is full');
// Post a message to say the queue is complete
message.channel.send(`The adventure is beginning - players should check their DMs\n${playerQueue.map((player, index) => `${index + 1} - ${player.username}`).join('\n')}`);
const randomCode = generateRandomCode();
const adventureMessage = (pokemonName === '' && captainInGameName === '')
? `${generateQueueTitle()}\nHere is your link code ${randomCode} - @${playerQueue[0].username} is making the lobby, have fun!`
: `${generateQueueTitle()}\nPokemon: ${pokemonName}\nCaptain's IGN: ${captainInGameName}\nHere is your link code ${randomCode} - @${playerQueue[0].username} is making the lobby, have fun!`;
try {
// DM users
playerQueue.forEach(player => {
client.users.cache.get(player.id).send(adventureMessage);
}); });
} catch (error) {
logger.log('error', error);
message.channel.send(`There was an error, please let an admin know!\n${error}`);
}
makeTempChannel(message, adventureMessage);
// Clear the queue
playerQueue = [];
logger.log('info', 'Emptied queue');
}
else if (playerQueue.length === 0) {
message.channel.send('The queue is empty :(');
pokemonName = '';
captainInGameName = '';
}
else {
// Show queue after adding
if (pokemonName === '' && captainInGameName === '') {
message.channel.send(`${generateQueueTitle()}\n${playerQueue.map((player, index) => `${index + 1} - ${player.username}`).join('\n')}`);
}
else {
message.channel.send(`${generateQueueTitle()}\nPokemon: ${pokemonName}\nCaptain's IGN: ${captainInGameName}\n${playerQueue.map((player, index) => `${index + 1} - ${player.username}`).join('\n')}`);
}
}
}
function getUserFromMention(mention) {
if (!mention) return;
if (mention.startsWith('<@') && mention.endsWith('>')) {
mention = mention.slice(2, -1);
if (mention.startsWith('!')) {
mention = mention.slice(1);
}
return client.users.cache.get(mention);
}
}
function getChannelName(username) {
return `${username}s-adventurers`;
}
function makeTempChannel(message, adventureMessage) {
const channelName = getChannelName(playerQueue[0].username);
let createdChannelId = '';
let everyoneRole = message.guild.roles.cache.find(r => r.name === '@everyone');
let permissionOverwrites = [{
id: everyoneRole.id,
deny: ['VIEW_CHANNEL'],
},
];
playerQueue.forEach(player => {
permissionOverwrites.push({
id: player.id,
allow: ['VIEW_CHANNEL'],
});
});
message.guild.channels.create(channelName, {
type: 'text',
permissionOverwrites: permissionOverwrites,
}).then(createdChannel => {
createdChannelId = createdChannel.id;
// Post in the channel
createdChannel.send(adventureMessage);
})
.catch(logger.error);
// Delete the channel after the configured amount of minutes
setTimeout(function(){ deleteChannel(message, createdChannelId); }, (config.channeldeletetimeinminutes * 1000 * 60) );
}
function deleteChannel(message, channelId) {
// For some reason this errors without the console.log loading the cache first - not sure why
console.log(message.guild.channels.cache);
const fetchedChannel = message.guild.channels.cache.get(channelId);
fetchedChannel.delete();
}
client.on('message', function (message) { client.on('message', function (message) {
// Ignore messages from the bot and that don't begin with the prefix, and do not run in DMs to bot // Ignore messages from the bot and that don't begin with the prefix, and do not run in DMs to bot
@@ -167,159 +18,11 @@ client.on('message', function (message) {
if (!message.content.startsWith(config.prefix)) return; if (!message.content.startsWith(config.prefix)) return;
if (message.channel instanceof Discord.DMChannel) return; if (message.channel instanceof Discord.DMChannel) return;
// Split out the fields we need from the message
const commandBody = message.content.slice(config.prefix.length); const commandBody = message.content.slice(config.prefix.length);
const args = commandBody.split(' '); const args = commandBody.split(' ');
const command = args.shift().toLowerCase(); const command = args.shift().toLowerCase();
// Join the queue // Run the matching command
if (command === 'q' && !(playerQueue.includes(message.author))) { matchCommand(Discord, config, logger, message, command, args);
// Delete the message with the bot command
if (config.deletecommandstoggle) {
message.delete();
}
logger.log('info', `${message.author.username} joined the queue.`);
message.channel.send(`=====\n${message.author.username} joined the queue.`);
playerQueue.push(message.author);
if (playerQueue.length === 1 && args[0] && args[1]) {
// First player can submit pokemon and in-game name
pokemonName = args[0];
captainInGameName = args[1];
}
checkQueue(message);
}
else if (command === 'q' && (playerQueue.includes(message.author))) {
// Delete the message with the bot command
if (config.deletecommandstoggle) {
message.delete();
}
message.channel.send(`${message.author.username} is too keen, you're already in the queue mate!`);
}
// Leave the queue
if(command === 'lq' && (playerQueue.includes(message.author))){
// Delete the message with the bot command
if (config.deletecommandstoggle) {
message.delete();
}
logger.log('info', `${message.author.username} left the queue.`);
message.channel.send(`${message.author.username} left the queue.`);
for( var i = 0; i < playerQueue.length; i++){
if ( playerQueue[i] === message.author) {
playerQueue.splice(i, 1);
i--;
}
}
checkQueue(message);
}
else if (command === 'lq' && !(playerQueue.includes(message.author))) {
// Delete the message with the bot command
if (config.deletecommandstoggle) {
message.delete();
}
message.channel.send(`${message.author.username} you can't leave the queue before you've joined it!`);
}
// Show the queue
if(command === 'sq') {
// Delete the message with the bot command
if (config.deletecommandstoggle) {
message.delete();
}
checkQueue(message);
}
// Clear the queue
if(command === 'cq') {
// Delete the message with the bot command
if (config.deletecommandstoggle) {
message.delete();
}
if (playerQueue.length === 0) {
message.channel.send(`The queue is already empty...`);
logger.log('info', `${message.author.username} attempted to clear the queue but it was already empty.`);
}
else {
playerQueue = [];
message.channel.send(`The queue has been cleared!`);
logger.log('info', `${message.author.username} cleared the queue.`);
}
}
// Remove a tagged user from the queue
if (command === 'ru' && args[0]) {
const user = getUserFromMention(args[0]);
// Delete the message with the bot command
if (config.deletecommandstoggle) {
message.delete();
}
if ((playerQueue.includes(user))) {
// Search for the user
for(var i = 0; i < playerQueue.length; i++){
if ((playerQueue[i].username === user.username)){
playerQueue.splice(i, 1);
i--;
}
}
message.channel.send(`${user.username} removed from queue.`);
logger.log('info', `${message.author.username} removed ${user.username} from queue.`);
checkQueue(message);
}
else if (!(playerQueue.includes(user))) {
message.channel.send(`${user.username} is not in the queue.`);
logger.log('info', `${message.author.username} attempted to remove ${user.username} from queue but they weren't in it.`);
}
}
if (command === 'dc') {
// todo - add logic here to determine if they can delete the channel (i.e. it's their temporary one) - compare the name
if (message.channel.name === getChannelName(message.author.username)) {
deleteChannel(message, message.channel.id)
logger.log('info', `${message.author.username} deleted their adventure channel.`);
}
else {
message.channel.send(`${message.author.username} is not allowed to delete this channel.`);
logger.log('info', `${message.author.username} attempted to delete channel ${message.channel.id} but it's not their adventure channel.`);
}
}
if (command === 'help') {
let msg = `
The commands available to you are:
- !q - Join the queue
- !q <pokemon> <ign> - Join the queue (first player can set the pokemon and their in-game name)
- !lq - Leave the queue
- !sq - Show the queue
- !dc - Delete an adventure channel (only the captain can use this)
`;
message.channel.send(msg);
}
if (command === 'adminhelp') {
let msg = `
The commands available to you are:
- !q - Join the queue
- !q <pokemon> <ign> - Join the queue (first player can set the pokemon and their in-game name)
- !lq - Leave the queue
- !sq - Show the queue
- !cq - Clear the queue (admin-only, not to be shared with normal users)
- !ru - Remove a tagged user from the queue (admin-only, not to be shared with normal users)
- !dc - Delete an adventure channel (only the captain can use this)
`;
message.channel.send(msg);
}
}); });
+78
View File
@@ -0,0 +1,78 @@
module.exports = {
initialiseLogger: function (config, winston, Elasticsearch) {
const { format } = winston;
const { combine, timestamp } = format;
let env = 'dev';
if (process.env.NODE_ENV === 'production') {
env = 'prod';
}
// Custom format function that will look for an error object and log out the stack and if
// its not production, the error itself
const myFormat = format.printf((info) => {
const { timestamp: tmsmp, level, message, error, ...rest } = info;
let log = `${tmsmp} - ${level}:\t${message}`;
// Only if there is an error
if ( error ) {
if ( error.stack) log = `${log}\n${error.stack}`;
if (process.env.NODE_ENV !== 'production') log = `${log}\n${JSON.stringify(error, null, 2)}`;
}
// Check if rest is object
if ( !( Object.keys(rest).length === 0 && rest.constructor === Object ) ) {
log = `${log}\n${JSON.stringify(rest, null, 2)}`;
}
return log;
});
const esTransportOpts = {
level: 'info',
indexPrefix: 'logs_denbot',
clientOpts: {
host: config.elasticsearch.elasticsearch_address,
log: 'info'
},
transformer: logData => {
return {
timestamp: new Date().toISOString(),
severity: logData.level,
message: logData.message,
meta: {
app: 'denbot',
env: env,
stack: logData.meta.stack
}
}
}
};
const logger = winston.createLogger({
level: 'info',
format: combine(
winston.format.errors({ stack: true }), // <-- use errors format
winston.format.timestamp(),
winston.format.prettyPrint(),
myFormat,
winston.format.json()
),
defaultMeta: { service: 'user-service' },
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' }),
new Elasticsearch(esTransportOpts)
],
});
//
// If we're not in production then log to the `console` with the format:
// `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
//
//if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple(),
}));
//}
return logger;
},
};
+164
View File
@@ -37,6 +37,29 @@
"event-target-shim": "^5.0.0" "event-target-shim": "^5.0.0"
} }
}, },
"agentkeepalive": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz",
"integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==",
"requires": {
"humanize-ms": "^1.2.1"
}
},
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
},
"asap": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
},
"async": { "async": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz",
@@ -47,6 +70,18 @@
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
}, },
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"requires": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
}
},
"color": { "color": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz",
@@ -105,6 +140,14 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
}, },
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"delayed-stream": { "delayed-stream": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -125,11 +168,26 @@
"ws": "^7.3.1" "ws": "^7.3.1"
} }
}, },
"elasticsearch": {
"version": "16.7.1",
"resolved": "https://registry.npmjs.org/elasticsearch/-/elasticsearch-16.7.1.tgz",
"integrity": "sha512-PL/BxB03VGbbghJwISYvVcrR9KbSSkuQ7OM//jHJg/End/uC2fvXg4QI7RXLvCGbhBuNQ8dPue7DOOPra73PCw==",
"requires": {
"agentkeepalive": "^3.4.1",
"chalk": "^1.0.0",
"lodash": "^4.17.10"
}
},
"enabled": { "enabled": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
}, },
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"event-target-shim": { "event-target-shim": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
@@ -150,6 +208,22 @@
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
}, },
"has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
"requires": {
"ansi-regex": "^2.0.0"
}
},
"humanize-ms": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
"integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=",
"requires": {
"ms": "^2.0.0"
}
},
"inherits": { "inherits": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@@ -175,6 +249,11 @@
"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
}, },
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
},
"logform": { "logform": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz",
@@ -200,6 +279,11 @@
"mime-db": "1.44.0" "mime-db": "1.44.0"
} }
}, },
"moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
},
"ms": { "ms": {
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -228,6 +312,14 @@
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
}, },
"promise": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz",
"integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==",
"requires": {
"asap": "~2.0.6"
}
},
"readable-stream": { "readable-stream": {
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
@@ -238,6 +330,11 @@
"util-deprecate": "^1.0.1" "util-deprecate": "^1.0.1"
} }
}, },
"retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs="
},
"safe-buffer": { "safe-buffer": {
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -269,6 +366,19 @@
"safe-buffer": "~5.2.0" "safe-buffer": "~5.2.0"
} }
}, },
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
}
},
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
},
"text-hex": { "text-hex": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
@@ -305,6 +415,60 @@
"winston-transport": "^4.4.0" "winston-transport": "^4.4.0"
} }
}, },
"winston-elasticsearch": {
"version": "0.7.12",
"resolved": "https://registry.npmjs.org/winston-elasticsearch/-/winston-elasticsearch-0.7.12.tgz",
"integrity": "sha512-nH35GlCD/vIFKNFUQSTd16esNKqLgaQgMvCoz3amXNNf8LWq4lYejTWhDQQpKQr+8EA2NOWfSGa2hYECQm/bGA==",
"requires": {
"debug": "4.1.1",
"elasticsearch": "^16.0.0",
"lodash": "^4.17.11",
"moment": "^2.24.0",
"promise": "^8.0.3",
"retry": "^0.12.0",
"triple-beam": "^1.3.0",
"winston": "^3.2.1",
"winston-transport": "4.3.0"
},
"dependencies": {
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
},
"winston-transport": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz",
"integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==",
"requires": {
"readable-stream": "^2.3.6",
"triple-beam": "^1.2.0"
}
}
}
},
"winston-transport": { "winston-transport": {
"version": "4.4.0", "version": "4.4.0",
"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz",
+2 -1
View File
@@ -10,6 +10,7 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"discord.js": "12.4.1", "discord.js": "12.4.1",
"winston": "3.3.3" "winston": "3.3.3",
"winston-elasticsearch": "^0.7.12"
} }
} }