1
1
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' ) ;
2
6
const Eris = require ( 'eris' ) ;
3
7
const moment = require ( 'moment' ) ;
4
8
const Queue = require ( './queue' ) ;
5
9
const config = require ( './config' ) ;
6
10
11
+ const logServerPort = config . port || 8890 ;
12
+
7
13
const bot = new Eris . CommandClient ( config . token , { } , {
8
14
prefix : config . prefix || '!' ,
9
15
ignoreSelf : true ,
@@ -18,6 +24,9 @@ const messageQueue = new Queue();
18
24
const blockFile = `${ __dirname } /blocked.json` ;
19
25
let blocked = [ ] ;
20
26
27
+ const logDir = `${ __dirname } /logs` ;
28
+ const logFileFormatRegex = / ^ ( [ 0 - 9 \- ] + ?) _ _ ( [ 0 - 9 ] + ?) _ _ ( [ 0 - 9 a - f ] + ?) \. t x t $ / ;
29
+
21
30
try {
22
31
const blockedJSON = fs . readFileSync ( blockFile , { encoding : 'utf8' } ) ;
23
32
blocked = JSON . parse ( blockedJSON ) ;
@@ -29,6 +38,72 @@ function saveBlocked() {
29
38
fs . writeFileSync ( blockFile , JSON . stringify ( blocked , null , 4 ) ) ;
30
39
}
31
40
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
+
32
107
bot . on ( 'ready' , ( ) => {
33
108
modMailGuild = bot . guilds . find ( g => g . id === config . mailGuildId ) ;
34
109
@@ -146,13 +221,16 @@ bot.registerCommand('close', (msg, args) => {
146
221
return `[${ date } ] ${ message . author . username } #${ message . author . discriminator } : ${ message . content } ` ;
147
222
} ) . join ( '\n' ) + '\n' ;
148
223
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 } >` ) ;
153
228
154
- delete modMailChannels [ channelInfo . userId ] ;
155
- msg . channel . delete ( ) ;
229
+ delete modMailChannels [ channelInfo . userId ] ;
230
+ msg . channel . delete ( ) ;
231
+ } ) ;
232
+ } ) ;
233
+ } )
156
234
} ) ;
157
235
} ) ;
158
236
@@ -196,4 +274,73 @@ bot.registerCommand('unblock', (msg, args) => {
196
274
msg . channel . createMessage ( `Unblocked <@${ userId } > (id ${ userId } ) from modmail` ) ;
197
275
} ) ;
198
276
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
+
199
320
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 - 9 a - 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 ) ;
0 commit comments