var cfg = require('../../cfg.json'); var fs = require('fs-extra'); var http = require('http'); var https = require('https'); var mysql = require('mysql'); var bot = require('coffea')(); var uuid = require('uuid'); var crypto = require('crypto'); var path = require('path'); var exec = require('child_process').exec; var probe = require('node-ffprobe'); var swig = require('swig'); var templates = {}; var debug = false; var upload = true; var admins = []; var sql; var haDC = () => { sql = mysql.createConnection(cfg.mysql); sql.connect((err) => { if(err) setTimeout(haDC,2000); }); sql.on('error', (err) => { if(err.code === 'PROTOCOL_CONNECTION_LOST') haDC(); }); }; haDC(); cfg.server.forEach((e,i,a) => { bot.add({ "name": e.name, "host": e.host, "port": e.port, "ssl": e.ssl, "ssl_allow_invalid": e.ssl_allow_invalid, "pass": e.pass, "nick": e.nick, "username": e.username, "realname": e.realname }); console.log("Server "+e.name+" wurde geladen"); }); var log = (msg) => { if(debug) bot.send("#f0ck", msg, 'n0xy'); }; bot.on('motd', (e) => { console.log("motd von "+e.network+" erhalten"); bot.write('MODE f0ck +B', e.network, (c)=>{}); // Botflag }); bot.on('message', (e) => { var orig = e.message; if(orig.match(/https?:\/\/[\w-]+(\.[\w-]+)+\.?(:\d+)?(\/\S*)?/gi) && e.channel.getName() == "#f0ck" && upload) { // shitpostcatcher if(!orig.match(/\!ignore$/)) { var tmp = orig.match(/https?:\/\/[\w-]+(\.[\w-]+)+\.?(:\d+)?(\/\S*)?/gi); // get links tmp.forEach((entry,i,a) => { var tmpdest = uuid.v1().split('-')[0]; dl(entry, "./b/"+tmpdest, (cb) => { // download item if(cb.status === true) { var tmpuser = getUser(e.user.getNick(), e.network); getCheckSum("./b/"+tmpdest+"."+cb.infos.ext, (cbcs) => { checkRepostCheckSum(cbcs, (cbcrcs) => { if(cbcrcs) { sql.query("insert into `f0ck`.`items` (`src`,`dest`,`mime`,`size`,`checksum`,`username`,`userchannel`,`usernetwork`,`stamp`,`active`) values (?,?,?,?,?,?,?,?,?,?)", [ entry, "./b/"+tmpdest+"."+cb.infos.ext, cb.infos.mime, cb.infos.size, cbcs, tmpuser['nick'], e.channel.getName(), e.network, Math.floor(new Date() / 1000), 0 ]).on('result', (result) => { generateThumbs(); e.reply("https://f0ck.me/"+result.insertId+" - "+path.parse(entry).base+" ("+cb.infos.mime+", ~"+formatSize(cb.infos.size)+") from "+tmpuser['nick']+" ("+tmpuser['username']+"@"+tmpuser['hostname']+")"); }).on('error', (msg) => { e.reply(msg); }); } else { fs.unlink("./b/"+tmpdest+"."+cb.infos.ext); // delete repost e.reply("repost motherf0cker"); } }); }); } else if(cb.type == 1) e.reply(cb.msg); }); }); } } else if(orig.match(/^\.user/)) { // (debug) get userinfos var tmp = getUser(e.user.getNick(), e.network); setTimeout(()=>{ e.reply(tmp); }, 500); } else if(orig.match(/^\!reload tpl$/)) { getTpls(); e.reply("templates reloaded"); } else if(orig.match(/^\!toggle debug$/)) { if(debug) { debug = false; e.reply("debugmessages deactivated"); } else { debug = true; e.reply("debugmessages activated"); } } else if(orig.match(/^\!toggle catcher$/)) { if(upload) { upload = false; e.reply("catcher deactivated"); } else { upload = true; e.reply("catcher activated"); } } else if(orig.match(/^\!load user$/)) { loadUser((cb) => { //e.reply(admins); }); } else if(orig.match(/^\!level$/)) { var tmp_channel = bot.getChannel(e.channel.getName(), e.network).names; var level = getUserlevel(e, e.user.getNick(), tmp_channel, (cb) => { e.reply("level from "+e.user.getNick()+": "+cb.level+" (Channel: "+cb.channel+" DB: "+cb.db+")"); }); } else if(orig.match(/^\!del (\d+)$/i)) { var tmp_channel = bot.getChannel(e.channel.getName(), e.network).names; getUserlevel(e, e.user.getNick(), tmp_channel, (cb) => { if(cb.level >= 100) { var id = orig.split(' ')[1]; sql.query("delete from `f0ck`.`items` where `id` = ?", id).on('end', () => { e.reply("f0ck "+id+" deleted"); }); } else e.reply("no permission"); }); } else if(orig.match(/^\!thumbs$/i)) { var tmp_channel = bot.getChannel(e.channel.getName(), e.network).names; getUserlevel(e, e.user.getNick(), tmp_channel, (cb) => { if(cb.level >= 100) { //exec('rm ./t/*.png', (error) => { e.reply("generating Thumbnails..."); generateThumbs(); //}); } else e.reply("no permission"); }); } }); var getUser = (u, n) => { bot.whois(u, n, (fn)=>{ }); // send whois var start = Date.now(); while(Date.now() < start + 500) {} // block script for catch whois return bot.getUser(u, n); }; var getUserlevel = (e, name, tmp_channel, cb) => { var lvl_channel = (name in tmp_channel)? cfg.level[ tmp_channel[name] ] : 0; var lvl_db = 0; if(name.toLowerCase() in admins) lvl_db = (admins[name.toLowerCase()].server == e.network)? admins[name.toLowerCase()].level : 0; cb({ 'channel': lvl_channel, 'db': lvl_db, 'level': Math.max(lvl_channel, lvl_db) }); }; var loadUser = (cb) => { admins = []; sql.query("select * from `f0ck`.`user`", (err, rows, fields) => { rows.forEach((e,i,a) => { admins.push(e.nick); admins[e.nick] = { 'id': e.id, 'nick': e.nick, 'vhost': e.vhost, 'level': e.level, 'server': e.server }; }); cb(true); }).on('error', () => { cb(false); });; }; var dl = (url, dest, cb) => { var request = (url.match(/^https/)?https:http).get(url, (response) => { // type:1=post,type:2=stfu console.log(response.headers['content-type']); if(cfg.allowedMimes.hasOwnProperty(response.headers['content-type'])) { if(response.headers['content-length'] <= cfg.maxFileSize) { checkRepost(url, (cbcr) => { if(cbcr) { var file = fs.createWriteStream(dest+"."+cfg.allowedMimes[response.headers['content-type']]); response.pipe(file); file.on('finish', () => { file.close(); probe(dest+"."+cfg.allowedMimes[response.headers['content-type']], (err, probeData) => { if(probeData.streams[0].height !== undefined || probeData.streams[0].width !== undefined) { if(probeData.streams[0].height <= cfg.minRes || probeData.streams[0].width <= cfg.minRes) cb({'status':false, 'msg':'f0ck! your shitpost is too small ('+probeData.streams[0].width+' x '+probeData.streams[0].height+'), min '+cfg.minRes+' x '+cfg.minRes+' required', 'type':1}); else cb({'status':true, 'msg':'downloaded '+dest, 'type':1, 'infos':{'mime':response.headers['content-type'], 'size':response.headers['content-length'], 'ext':cfg.allowedMimes[response.headers['content-type']]}}); } else cb({'status':true, 'msg':'downloaded '+dest, 'type':1, 'infos':{'mime':response.headers['content-type'], 'size':response.headers['content-length'], 'ext':cfg.allowedMimes[response.headers['content-type']]}}); }); }); file.on('error', (err) => { fs.unlink(dest+"."+cfg.allowedMimes[response.headers['content-type']]); file.close(); cb({'status':false, 'msg':err.message, 'type':1}); }); } else cb({'status':false, 'msg':'repost motherf0cker', 'type':1}); }); } else cb({'status':false, 'msg':'f0ck! your file is too big (~'+formatSize(response.headers['content-length'])+'), max '+formatSize(cfg.maxFileSize)+' allowed', 'type':1}); } else cb({'status':false, 'msg':'f0ck you', 'type':2}); }).on('error', (msg) => { cb({'status':false, 'msg':msg, 'type':2}); }); }; var checkRepost = (url, cbcr) => { sql.query("select count(*) as count from `f0ck`.`items` where `src` = ?", url, (err, rows, fields) => { cbcr((rows[0].count == 0)?true:false); }); }; var checkRepostCheckSum = (cs, cbcrcs) => { sql.query("select count(*) as count from `f0ck`.`items` where `checksum` = ?", cs, (err, rows, fields) => { cbcrcs((rows[0].count == 0)?true:false); }); }; var formatSize = (size) => { var i = Math.floor(Math.log(size) / Math.log(1024)); return (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i]; }; var getCheckSum = (file, cbcs) => { var sha256sum = crypto.createHash('sha256'); var s = fs.ReadStream(file); s.on('data', (d) => { sha256sum.update(d); }); s.on('end', () => { cbcs(sha256sum.digest('hex')); }); }; // Webserver http.createServer((req, res) => { if(cfg.wlip.hasOwnProperty(req.connection.remoteAddress)) { var filePath = '.' + req.url; var url = req.url.split("/")[1]; if(filePath == './') filePath = './index.html'; var extname = String(path.extname(filePath)).toLowerCase(); var contentType = 'text/html'; var mimeTypes = { '.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css', '.png': 'image/png', '.jpg': 'image/jpg', '.gif': 'image/gif', '.mp3': 'audio/mpeg', '.mp4': 'video/mp4', '.webm': 'video/webm', '.css': 'text/css', '.ogg': 'audio/ogg' }; if(filePath == "./index.html") { // mainpage var tpl = swig.compile(templates.index); var data = { items: [] }; sql.query("select `id`,`mime` from `f0ck`.`items` order by `id` desc", (err, rows, fields) => { rows.forEach((e,i,a) => { data.items.push({ "id": e.id, "mime": e.mime }); }); res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(tpl(data), 'utf-8'); }); } else if(Number.isInteger(parseInt(url))) { // itempage var query = "select * from `f0ck`.`items` where `id` = ? limit 1; " // get item + "select `id` from `f0ck`.`items` where `id` = (select min(`id`) from `f0ck`.`items` where `id` > ?); " // get previous item + "select `id` from `f0ck`.`items` where `id` = (select max(`id`) from `f0ck`.`items` where `id` < ?)"; // get next item sql.query(query, [url, url, url], (err, rows, fields) => { var tpl = swig.compile(templates.item); var data = { id: '', username: '', item: '', src: '', dest: '', mime: '', size: '', userchannel: '', usernetwork: '', next: null, prev: null }; if(rows[0].length) { var e = rows[0][0]; switch(e.mime) { case "image/png": case "image/jpeg": case "image/gif": data.item = 'image'; break; case "video/webm": case "video/mp4": data.item = 'video'; break; case "audio/mpeg": case "audio/ogg": data.item = 'audio'; break; } data.id = e.id; data.username = e.username; data.src = e.src; data.dest = e.dest; data.mime = e.mime; data.size = formatSize(e.size); data.userchannel = e.userchannel; data.usernetwork = e.usernetwork; if(rows[1].length) data.next = rows[1][0].id; if(rows[2].length) data.prev = rows[2][0].id; } res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(tpl(data), 'utf-8'); }); } else if(filePath == "./random") { sql.query("select `id` from `f0ck`.`items` order by rand() limit 1", (err, rows, fields) => { res.writeHead(301, { 'Cache-Control': 'no-cache, public', 'Location': '/' + rows[0].id }); res.end(); }); } else if(filePath == "./how") { var tpl = swig.compile(templates.how); res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(tpl(), 'utf-8'); } else if(filePath == "./contact") { var tpl = swig.compile(templates.contact); res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(tpl(), 'utf-8'); } else if(filePath == "./scripts") { var tpl = swig.compile(templates.scripts); res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(tpl(), 'utf-8'); } else if(filePath.match(/^\.\/(b|s|t)\/.*/)) { // file contentType = mimeTypes[extname]; switch(contentType) { case "video/webm": case "video/mp4": case "audio/mpeg": case "audio/ogg": var start = 0; var end = 0; var range = req.headers['range']; var stat = fs.statSync(filePath); if(range != null) { start = parseInt(range.slice(range.indexOf('bytes=')+6, range.indexOf('-'))); end = parseInt(range.slice(range.indexOf('-')+1, range.length)); } if(isNaN(end) || end == 0) end = stat.size-1; if(start > end) return; res.writeHead(206, { 'Connection':'close', 'Content-Type':contentType, 'Content-Length':end - start, 'Content-Range':'bytes '+start+'-'+end+'/'+stat.size, 'Transfer-Encoding':'chunked' }); var stream = fs.createReadStream(filePath, { flags: 'r', start: start, end: end}); stream.pipe(res); break; default: fs.readFile(filePath, (error, content) => { if(error) { if(error.code == 'ENOENT') { res.writeHead(200, { 'Content-Type': contentType }); res.end('404 - f0ck you', 'utf-8'); } else { res.writeHead(500); res.end('Sorry, check with the site admin for error: '+error.code+' ..\n'); res.end(); } } else { res.writeHead(200, { 'Content-Type': contentType, 'Content-Length': content.length, 'Cache-Control': 'max-age=2592000, public' }); res.end(content, 'utf-8'); } }); break; } fs.readFile(filePath, (error, content) => { if(error) { if(error.code == 'ENOENT') { res.writeHead(200, { 'Content-Type': contentType }); res.end('404 - f0ck you', 'utf-8'); } else { res.writeHead(500); res.end('Sorry, check with the site admin for error: '+error.code+' ..\n'); res.end(); } } else { } }); } else { // errorpage res.writeHead(404); res.end('404 - f0ck you', 'utf-8'); } } else { res.writeHead(403); res.end('403 - forbidden'); } }).listen(cfg.webserver.port); var getTpls = () => { templates = { "index": fs.readFileSync("./s/index.tpl.html", "utf-8"), "item": fs.readFileSync("./s/item.tpl.html", "utf-8"), "how": fs.readFileSync("./s/how.tpl.html", "utf-8"), "contact": fs.readFileSync("./s/contact.tpl.html", "utf-8"), "scripts": fs.readFileSync("./s/scripts.tpl.html", "utf-8") }; }; var generateThumbs = () => { var outdir = './t/'; sql.query("select * from `f0ck`.`items`", (err, rows, fields) => { rows.forEach((e,i,a) => { var thumbnail = outdir+e.id+'.png'; if(!fs.existsSync(thumbnail)) { exec('ffmpegthumbnailer -i'+e.dest+' -s256 -o'+thumbnail, (error) => { if(error) { log('failed thumbnail for '+e.id+' ('+e.mime+') 1'); //fs.unlink(thumbnail); fs.copySync('./s/mp3.png', thumbnail); // copy standardthumbnail } else { exec('convert '+thumbnail+' -resize "128x128^" -gravity center -crop 128x128+0+0 +repage '+thumbnail, (error) => { if(error) { log('failed thumbnail for '+e.id+' ('+e.mime+') 2'); //fs.unlink(thumbnail); } else { log("generated thumbnail for "+e.id+" ("+e.mime+")"); } }); } }); } }); }); }; loadUser((cb)=>{ console.log((cb)?"Admins wurden geladen":"Admins konnten nicht geladen werden"); }); getTpls();