Skip to content

Commit 145a3be

Browse files
committed
Add log saving and serving
1 parent 6912290 commit 145a3be

File tree

4 files changed

+186
-8
lines changed

4 files changed

+186
-8
lines changed

index.js

+153-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
const fs = require('fs');
2+
const http = require('http');
3+
const url = require('url');
4+
const crypto = require('crypto');
5+
const publicIp = require('public-ip');
26
const Eris = require('eris');
37
const moment = require('moment');
48
const Queue = require('./queue');
59
const config = require('./config');
610

11+
const logServerPort = config.port || 8890;
12+
713
const bot = new Eris.CommandClient(config.token, {}, {
814
prefix: config.prefix || '!',
915
ignoreSelf: true,
@@ -18,6 +24,9 @@ const messageQueue = new Queue();
1824
const blockFile = `${__dirname}/blocked.json`;
1925
let blocked = [];
2026

27+
const logDir = `${__dirname}/logs`;
28+
const logFileFormatRegex = /^([0-9\-]+?)__([0-9]+?)__([0-9a-f]+?)\.txt$/;
29+
2130
try {
2231
const blockedJSON = fs.readFileSync(blockFile, {encoding: 'utf8'});
2332
blocked = JSON.parse(blockedJSON);
@@ -29,6 +38,72 @@ function saveBlocked() {
2938
fs.writeFileSync(blockFile, JSON.stringify(blocked, null, 4));
3039
}
3140

41+
function getLogFileInfo(logfile) {
42+
const match = logfile.match(logFileFormatRegex);
43+
if (! match) return null;
44+
45+
return {
46+
date: match[1],
47+
userId: match[2],
48+
token: match[3],
49+
};
50+
}
51+
52+
function getLogFilePath(logfile) {
53+
return `${logDir}/${logfile}`;
54+
}
55+
56+
function getLogFileUrl(logfile) {
57+
const info = getLogFileInfo(logfile);
58+
59+
return publicIp.v4().then(ip => {
60+
return `http://${ip}:${logServerPort}/logs/${info.token}`;
61+
});
62+
}
63+
64+
function getRandomLogFile(userId) {
65+
return new Promise(resolve => {
66+
crypto.randomBytes(16, (err, buf) => {
67+
const token = buf.toString('hex');
68+
const date = moment.utc().format('YYYY-MM-DD-HH-mm-ss');
69+
70+
resolve(`${date}__${userId}__${token}.txt`);
71+
});
72+
});
73+
}
74+
75+
function findLogFile(token) {
76+
return new Promise(resolve => {
77+
fs.readdir(logDir, (err, files) => {
78+
for (const file of files) {
79+
if (file.endsWith(`__${token}.txt`)) {
80+
resolve(file);
81+
return;
82+
}
83+
}
84+
85+
resolve(null);
86+
});
87+
});
88+
}
89+
90+
function findLogFilesByUserId(userId) {
91+
return new Promise(resolve => {
92+
fs.readdir(logDir, (err, files) => {
93+
const logfiles = files.filter(file => {
94+
const info = getLogFileInfo(file);
95+
console.log('info:', info);
96+
console.log('userId:', userId, typeof userId);
97+
if (! info) return false;
98+
99+
return info.userId === userId;
100+
});
101+
102+
resolve(logfiles);
103+
});
104+
});
105+
}
106+
32107
bot.on('ready', () => {
33108
modMailGuild = bot.guilds.find(g => g.id === config.mailGuildId);
34109

@@ -146,13 +221,16 @@ bot.registerCommand('close', (msg, args) => {
146221
return `[${date}] ${message.author.username}#${message.author.discriminator}: ${message.content}`;
147222
}).join('\n') + '\n';
148223

149-
bot.createMessage(modMailGuild.id, `Log of modmail thread with ${channelInfo.name}:`, {
150-
file: new Buffer(log),
151-
name: 'log.txt',
152-
});
224+
getRandomLogFile(channelInfo.userId).then(logfile => {
225+
fs.writeFile(getLogFilePath(logfile), log, {encoding: 'utf8'}, err => {
226+
getLogFileUrl(logfile).then(logurl => {
227+
bot.createMessage(modMailGuild.id, `Log of modmail thread with ${channelInfo.name}:\n<${logurl}>`);
153228

154-
delete modMailChannels[channelInfo.userId];
155-
msg.channel.delete();
229+
delete modMailChannels[channelInfo.userId];
230+
msg.channel.delete();
231+
});
232+
});
233+
})
156234
});
157235
});
158236

@@ -196,4 +274,73 @@ bot.registerCommand('unblock', (msg, args) => {
196274
msg.channel.createMessage(`Unblocked <@${userId}> (id ${userId}) from modmail`);
197275
});
198276

277+
bot.registerCommand('logs', (msg, args) => {
278+
if (msg.channel.guild.id !== modMailGuild.id) return;
279+
if (! msg.member.permission.has('manageRoles')) return;
280+
if (args.length !== 1) return;
281+
282+
let userId;
283+
if (args[0].match(/^[0-9]+$/)) {
284+
userId = args[0];
285+
} else {
286+
let mentionMatch = args[0].match(/^<@([0-9]+?)>$/);
287+
if (mentionMatch) userId = mentionMatch[1];
288+
}
289+
290+
if (! userId) return;
291+
292+
findLogFilesByUserId(userId).then(logfiles => {
293+
let message = `**Log files for <@${userId}>:**\n`;
294+
295+
const urlPromises = logfiles.map(logfile => {
296+
const info = getLogFileInfo(logfile);
297+
return getLogFileUrl(logfile).then(url => {
298+
info.url = url;
299+
return info;
300+
});
301+
});
302+
303+
Promise.all(urlPromises).then(infos => {
304+
infos.sort((a, b) => {
305+
if (a.date > b.date) return 1;
306+
if (a.date < b.date) return -1;
307+
return 0;
308+
});
309+
310+
message += infos.map(info => {
311+
const formattedDate = moment.utc(info.date, 'YYYY-MM-DD-HH-mm-ss').format('MMM Mo [at] HH:mm [UTC]');
312+
return `${formattedDate}: <${info.url}>`;
313+
}).join('\n');
314+
315+
msg.channel.createMessage(message);
316+
});
317+
});
318+
});
319+
199320
bot.connect();
321+
322+
// Server for serving logs
323+
const server = http.createServer((req, res) => {
324+
const parsedUrl = url.parse(`http://${req.url}`);
325+
326+
if (! parsedUrl.path.startsWith('/logs/')) return;
327+
328+
const pathParts = parsedUrl.path.split('/').filter(v => v !== '');
329+
const token = pathParts[pathParts.length - 1];
330+
331+
console.log('token:', token);
332+
333+
if (token.match(/^[0-9a-f]+$/) === null) return res.end();
334+
335+
findLogFile(token).then(logfile => {
336+
console.log('logfile:', logfile);
337+
if (logfile === null) return res.end();
338+
339+
fs.readFile(getLogFilePath(logfile), {encoding: 'utf8'}, (err, data) => {
340+
res.setHeader('Content-Type', 'text/plain');
341+
res.end(data);
342+
});
343+
});
344+
});
345+
346+
server.listen(logServerPort);

logs/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*
2+
!/.gitignore

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"license": "ISC",
1111
"dependencies": {
1212
"eris": "github:abalabahaha/eris#dev",
13-
"moment": "^2.17.1"
13+
"moment": "^2.17.1",
14+
"public-ip": "^2.0.1"
1415
},
1516
"devDependencies": {
1617
"eslint": "^3.9.1"

yarn.lock

+29-1
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,19 @@ del@^2.0.2:
162162
pinkie-promise "^2.0.0"
163163
rimraf "^2.2.8"
164164

165+
dns-packet@^1.1.0:
166+
version "1.1.1"
167+
resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.1.1.tgz#2369d45038af045f3898e6fa56862aed3f40296c"
168+
dependencies:
169+
ip "^1.1.0"
170+
safe-buffer "^5.0.1"
171+
172+
dns-socket@^1.0.0:
173+
version "1.4.2"
174+
resolved "https://registry.yarnpkg.com/dns-socket/-/dns-socket-1.4.2.tgz#8e09322c1566e2cb402c322f086dfe69fd0898e5"
175+
dependencies:
176+
dns-packet "^1.1.0"
177+
165178
doctrine@^1.2.2:
166179
version "1.5.0"
167180
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
@@ -441,6 +454,10 @@ interpret@^1.0.0:
441454
version "1.0.1"
442455
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c"
443456

457+
ip@^1.1.0:
458+
version "1.1.4"
459+
resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.4.tgz#de8247ffef940451832550fba284945e6e039bfb"
460+
444461
is-fullwidth-code-point@^1.0.0:
445462
version "1.0.0"
446463
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:
607624
version "1.0.2"
608625
resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53"
609626

610-
pify@^2.0.0:
627+
pify@^2.0.0, pify@^2.3.0:
611628
version "2.3.0"
612629
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
613630

@@ -637,6 +654,13 @@ progress@^1.1.8:
637654
version "1.1.8"
638655
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
639656

657+
public-ip:
658+
version "2.0.1"
659+
resolved "https://registry.yarnpkg.com/public-ip/-/public-ip-2.0.1.tgz#1648026a5a11fb88bee52bd4ecf4a2e6af3747f7"
660+
dependencies:
661+
dns-socket "^1.0.0"
662+
pify "^2.3.0"
663+
640664
readable-stream@~2.0.0:
641665
version "2.0.6"
642666
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e"
@@ -700,6 +724,10 @@ rx-lite@^3.1.2:
700724
version "3.1.2"
701725
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
702726

727+
safe-buffer@^5.0.1:
728+
version "5.0.1"
729+
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
730+
703731
shelljs@^0.7.5:
704732
version "0.7.5"
705733
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.5.tgz#2eef7a50a21e1ccf37da00df767ec69e30ad0675"

0 commit comments

Comments
 (0)