Add log saving and serving
parent
691229068f
commit
145a3bee15
155
index.js
155
index.js
|
@ -1,9 +1,15 @@
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const http = require('http');
|
||||||
|
const url = require('url');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const publicIp = require('public-ip');
|
||||||
const Eris = require('eris');
|
const Eris = require('eris');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const Queue = require('./queue');
|
const Queue = require('./queue');
|
||||||
const config = require('./config');
|
const config = require('./config');
|
||||||
|
|
||||||
|
const logServerPort = config.port || 8890;
|
||||||
|
|
||||||
const bot = new Eris.CommandClient(config.token, {}, {
|
const bot = new Eris.CommandClient(config.token, {}, {
|
||||||
prefix: config.prefix || '!',
|
prefix: config.prefix || '!',
|
||||||
ignoreSelf: true,
|
ignoreSelf: true,
|
||||||
|
@ -18,6 +24,9 @@ const messageQueue = new Queue();
|
||||||
const blockFile = `${__dirname}/blocked.json`;
|
const blockFile = `${__dirname}/blocked.json`;
|
||||||
let blocked = [];
|
let blocked = [];
|
||||||
|
|
||||||
|
const logDir = `${__dirname}/logs`;
|
||||||
|
const logFileFormatRegex = /^([0-9\-]+?)__([0-9]+?)__([0-9a-f]+?)\.txt$/;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const blockedJSON = fs.readFileSync(blockFile, {encoding: 'utf8'});
|
const blockedJSON = fs.readFileSync(blockFile, {encoding: 'utf8'});
|
||||||
blocked = JSON.parse(blockedJSON);
|
blocked = JSON.parse(blockedJSON);
|
||||||
|
@ -29,6 +38,72 @@ function saveBlocked() {
|
||||||
fs.writeFileSync(blockFile, JSON.stringify(blocked, null, 4));
|
fs.writeFileSync(blockFile, JSON.stringify(blocked, null, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLogFileInfo(logfile) {
|
||||||
|
const match = logfile.match(logFileFormatRegex);
|
||||||
|
if (! match) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
date: match[1],
|
||||||
|
userId: match[2],
|
||||||
|
token: match[3],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLogFilePath(logfile) {
|
||||||
|
return `${logDir}/${logfile}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLogFileUrl(logfile) {
|
||||||
|
const info = getLogFileInfo(logfile);
|
||||||
|
|
||||||
|
return publicIp.v4().then(ip => {
|
||||||
|
return `http://${ip}:${logServerPort}/logs/${info.token}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomLogFile(userId) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
crypto.randomBytes(16, (err, buf) => {
|
||||||
|
const token = buf.toString('hex');
|
||||||
|
const date = moment.utc().format('YYYY-MM-DD-HH-mm-ss');
|
||||||
|
|
||||||
|
resolve(`${date}__${userId}__${token}.txt`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function findLogFile(token) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
fs.readdir(logDir, (err, files) => {
|
||||||
|
for (const file of files) {
|
||||||
|
if (file.endsWith(`__${token}.txt`)) {
|
||||||
|
resolve(file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function findLogFilesByUserId(userId) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
fs.readdir(logDir, (err, files) => {
|
||||||
|
const logfiles = files.filter(file => {
|
||||||
|
const info = getLogFileInfo(file);
|
||||||
|
console.log('info:', info);
|
||||||
|
console.log('userId:', userId, typeof userId);
|
||||||
|
if (! info) return false;
|
||||||
|
|
||||||
|
return info.userId === userId;
|
||||||
|
});
|
||||||
|
|
||||||
|
resolve(logfiles);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
bot.on('ready', () => {
|
bot.on('ready', () => {
|
||||||
modMailGuild = bot.guilds.find(g => g.id === config.mailGuildId);
|
modMailGuild = bot.guilds.find(g => g.id === config.mailGuildId);
|
||||||
|
|
||||||
|
@ -146,14 +221,17 @@ bot.registerCommand('close', (msg, args) => {
|
||||||
return `[${date}] ${message.author.username}#${message.author.discriminator}: ${message.content}`;
|
return `[${date}] ${message.author.username}#${message.author.discriminator}: ${message.content}`;
|
||||||
}).join('\n') + '\n';
|
}).join('\n') + '\n';
|
||||||
|
|
||||||
bot.createMessage(modMailGuild.id, `Log of modmail thread with ${channelInfo.name}:`, {
|
getRandomLogFile(channelInfo.userId).then(logfile => {
|
||||||
file: new Buffer(log),
|
fs.writeFile(getLogFilePath(logfile), log, {encoding: 'utf8'}, err => {
|
||||||
name: 'log.txt',
|
getLogFileUrl(logfile).then(logurl => {
|
||||||
});
|
bot.createMessage(modMailGuild.id, `Log of modmail thread with ${channelInfo.name}:\n<${logurl}>`);
|
||||||
|
|
||||||
delete modMailChannels[channelInfo.userId];
|
delete modMailChannels[channelInfo.userId];
|
||||||
msg.channel.delete();
|
msg.channel.delete();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
bot.registerCommand('block', (msg, args) => {
|
bot.registerCommand('block', (msg, args) => {
|
||||||
|
@ -196,4 +274,73 @@ bot.registerCommand('unblock', (msg, args) => {
|
||||||
msg.channel.createMessage(`Unblocked <@${userId}> (id ${userId}) from modmail`);
|
msg.channel.createMessage(`Unblocked <@${userId}> (id ${userId}) from modmail`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bot.registerCommand('logs', (msg, args) => {
|
||||||
|
if (msg.channel.guild.id !== modMailGuild.id) return;
|
||||||
|
if (! msg.member.permission.has('manageRoles')) return;
|
||||||
|
if (args.length !== 1) return;
|
||||||
|
|
||||||
|
let userId;
|
||||||
|
if (args[0].match(/^[0-9]+$/)) {
|
||||||
|
userId = args[0];
|
||||||
|
} else {
|
||||||
|
let mentionMatch = args[0].match(/^<@([0-9]+?)>$/);
|
||||||
|
if (mentionMatch) userId = mentionMatch[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! userId) return;
|
||||||
|
|
||||||
|
findLogFilesByUserId(userId).then(logfiles => {
|
||||||
|
let message = `**Log files for <@${userId}>:**\n`;
|
||||||
|
|
||||||
|
const urlPromises = logfiles.map(logfile => {
|
||||||
|
const info = getLogFileInfo(logfile);
|
||||||
|
return getLogFileUrl(logfile).then(url => {
|
||||||
|
info.url = url;
|
||||||
|
return info;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Promise.all(urlPromises).then(infos => {
|
||||||
|
infos.sort((a, b) => {
|
||||||
|
if (a.date > b.date) return 1;
|
||||||
|
if (a.date < b.date) return -1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
message += infos.map(info => {
|
||||||
|
const formattedDate = moment.utc(info.date, 'YYYY-MM-DD-HH-mm-ss').format('MMM Mo [at] HH:mm [UTC]');
|
||||||
|
return `${formattedDate}: <${info.url}>`;
|
||||||
|
}).join('\n');
|
||||||
|
|
||||||
|
msg.channel.createMessage(message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
bot.connect();
|
bot.connect();
|
||||||
|
|
||||||
|
// Server for serving logs
|
||||||
|
const server = http.createServer((req, res) => {
|
||||||
|
const parsedUrl = url.parse(`http://${req.url}`);
|
||||||
|
|
||||||
|
if (! parsedUrl.path.startsWith('/logs/')) return;
|
||||||
|
|
||||||
|
const pathParts = parsedUrl.path.split('/').filter(v => v !== '');
|
||||||
|
const token = pathParts[pathParts.length - 1];
|
||||||
|
|
||||||
|
console.log('token:', token);
|
||||||
|
|
||||||
|
if (token.match(/^[0-9a-f]+$/) === null) return res.end();
|
||||||
|
|
||||||
|
findLogFile(token).then(logfile => {
|
||||||
|
console.log('logfile:', logfile);
|
||||||
|
if (logfile === null) return res.end();
|
||||||
|
|
||||||
|
fs.readFile(getLogFilePath(logfile), {encoding: 'utf8'}, (err, data) => {
|
||||||
|
res.setHeader('Content-Type', 'text/plain');
|
||||||
|
res.end(data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(logServerPort);
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!/.gitignore
|
|
@ -10,7 +10,8 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"eris": "github:abalabahaha/eris#dev",
|
"eris": "github:abalabahaha/eris#dev",
|
||||||
"moment": "^2.17.1"
|
"moment": "^2.17.1",
|
||||||
|
"public-ip": "^2.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^3.9.1"
|
"eslint": "^3.9.1"
|
||||||
|
|
30
yarn.lock
30
yarn.lock
|
@ -162,6 +162,19 @@ del@^2.0.2:
|
||||||
pinkie-promise "^2.0.0"
|
pinkie-promise "^2.0.0"
|
||||||
rimraf "^2.2.8"
|
rimraf "^2.2.8"
|
||||||
|
|
||||||
|
dns-packet@^1.1.0:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.1.1.tgz#2369d45038af045f3898e6fa56862aed3f40296c"
|
||||||
|
dependencies:
|
||||||
|
ip "^1.1.0"
|
||||||
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
|
dns-socket@^1.0.0:
|
||||||
|
version "1.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/dns-socket/-/dns-socket-1.4.2.tgz#8e09322c1566e2cb402c322f086dfe69fd0898e5"
|
||||||
|
dependencies:
|
||||||
|
dns-packet "^1.1.0"
|
||||||
|
|
||||||
doctrine@^1.2.2:
|
doctrine@^1.2.2:
|
||||||
version "1.5.0"
|
version "1.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
|
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
|
||||||
|
@ -441,6 +454,10 @@ interpret@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c"
|
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c"
|
||||||
|
|
||||||
|
ip@^1.1.0:
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.4.tgz#de8247ffef940451832550fba284945e6e039bfb"
|
||||||
|
|
||||||
is-fullwidth-code-point@^1.0.0:
|
is-fullwidth-code-point@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
|
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
|
||||||
|
@ -607,7 +624,7 @@ path-is-inside@^1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
|
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
|
||||||
|
|
||||||
pify@^2.0.0:
|
pify@^2.0.0, pify@^2.3.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
|
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
|
||||||
|
|
||||||
|
@ -637,6 +654,13 @@ progress@^1.1.8:
|
||||||
version "1.1.8"
|
version "1.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
|
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
|
||||||
|
|
||||||
|
public-ip:
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/public-ip/-/public-ip-2.0.1.tgz#1648026a5a11fb88bee52bd4ecf4a2e6af3747f7"
|
||||||
|
dependencies:
|
||||||
|
dns-socket "^1.0.0"
|
||||||
|
pify "^2.3.0"
|
||||||
|
|
||||||
readable-stream@~2.0.0:
|
readable-stream@~2.0.0:
|
||||||
version "2.0.6"
|
version "2.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e"
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e"
|
||||||
|
@ -700,6 +724,10 @@ rx-lite@^3.1.2:
|
||||||
version "3.1.2"
|
version "3.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
|
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
|
||||||
|
|
||||||
|
safe-buffer@^5.0.1:
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
|
||||||
|
|
||||||
shelljs@^0.7.5:
|
shelljs@^0.7.5:
|
||||||
version "0.7.5"
|
version "0.7.5"
|
||||||
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.5.tgz#2eef7a50a21e1ccf37da00df767ec69e30ad0675"
|
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.5.tgz#2eef7a50a21e1ccf37da00df767ec69e30ad0675"
|
||||||
|
|
Loading…
Reference in New Issue