Add log saving and serving

master
Miikka Virtanen 2016-12-06 02:29:55 +02:00
parent 691229068f
commit 145a3bee15
4 changed files with 186 additions and 8 deletions

155
index.js
View File

@ -1,9 +1,15 @@
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 moment = require('moment');
const Queue = require('./queue');
const config = require('./config');
const logServerPort = config.port || 8890;
const bot = new Eris.CommandClient(config.token, {}, {
prefix: config.prefix || '!',
ignoreSelf: true,
@ -18,6 +24,9 @@ const messageQueue = new Queue();
const blockFile = `${__dirname}/blocked.json`;
let blocked = [];
const logDir = `${__dirname}/logs`;
const logFileFormatRegex = /^([0-9\-]+?)__([0-9]+?)__([0-9a-f]+?)\.txt$/;
try {
const blockedJSON = fs.readFileSync(blockFile, {encoding: 'utf8'});
blocked = JSON.parse(blockedJSON);
@ -29,6 +38,72 @@ function saveBlocked() {
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', () => {
modMailGuild = bot.guilds.find(g => g.id === config.mailGuildId);
@ -146,15 +221,18 @@ bot.registerCommand('close', (msg, args) => {
return `[${date}] ${message.author.username}#${message.author.discriminator}: ${message.content}`;
}).join('\n') + '\n';
bot.createMessage(modMailGuild.id, `Log of modmail thread with ${channelInfo.name}:`, {
file: new Buffer(log),
name: 'log.txt',
});
getRandomLogFile(channelInfo.userId).then(logfile => {
fs.writeFile(getLogFilePath(logfile), log, {encoding: 'utf8'}, err => {
getLogFileUrl(logfile).then(logurl => {
bot.createMessage(modMailGuild.id, `Log of modmail thread with ${channelInfo.name}:\n<${logurl}>`);
delete modMailChannels[channelInfo.userId];
msg.channel.delete();
});
});
})
});
});
bot.registerCommand('block', (msg, args) => {
if (msg.channel.guild.id !== modMailGuild.id) return;
@ -196,4 +274,73 @@ bot.registerCommand('unblock', (msg, args) => {
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();
// 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);

2
logs/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!/.gitignore

View File

@ -10,7 +10,8 @@
"license": "ISC",
"dependencies": {
"eris": "github:abalabahaha/eris#dev",
"moment": "^2.17.1"
"moment": "^2.17.1",
"public-ip": "^2.0.1"
},
"devDependencies": {
"eslint": "^3.9.1"

View File

@ -162,6 +162,19 @@ del@^2.0.2:
pinkie-promise "^2.0.0"
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:
version "1.5.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
@ -441,6 +454,10 @@ interpret@^1.0.0:
version "1.0.1"
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:
version "1.0.0"
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"
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"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@ -637,6 +654,13 @@ progress@^1.1.8:
version "1.1.8"
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:
version "2.0.6"
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"
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:
version "0.7.5"
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.5.tgz#2eef7a50a21e1ccf37da00df767ec69e30ad0675"