it's a tool for adding things to crab rave
also available as a Telegram bot
brought to you by boringcactus
var Libhoney = require("libhoney"); var hny = new Libhoney({ writeKey: process.env.HONEYCOMB_KEY, dataset: "crabravebot" }) const { Composer } = require('micro-bot'); const child_process = require('child_process'); const fs = require('fs'); const url = require('url'); const { JSDOM } = require("jsdom"); // the bot const bot = new Composer(); // start command bot.command('/start', async ({ from, replyWithMarkdown, botInfo }) => replyWithMarkdown(`Hi *${from.first_name || from.username}*! To shitpost, type @${botInfo.username} and type the text you want to overlay over crab rave. This was originally made by @boringcactus in one afternoon when she was bored. This bot isn't super reliable but the source is at https://glitch.com/edit/#!/${process.env.PROJECT_DOMAIN}, and you can use this bot from the Web at https://${process.env.PROJECT_DOMAIN}.glitch.me/`)); // styles const STYLES = { 'classic': { 'video': 'https://cdn.glitch.com/70ea37b5-d264-46e5-a1db-29c786c86515%2FCrabRaveQuieter.mp4?v=1548223581701', 'thumb': 'https://cdn.glitch.com/70ea37b5-d264-46e5-a1db-29c786c86515%2FCrabRaveBackground.png?v=1566350372048', }, 'otamatone': { 'video': 'https://cdn.glitch.com/70ea37b5-d264-46e5-a1db-29c786c86515%2FCrabRaveOtamatone.mp4?v=1566354239409', 'thumb': 'https://cdn.glitch.com/70ea37b5-d264-46e5-a1db-29c786c86515%2FCrabRaveOtamatoneBackground.png?v=1566354702285', }, 'garfield': { 'video': 'https://cdn.glitch.com/70ea37b5-d264-46e5-a1db-29c786c86515%2FCrabRaveGarfield.mp4?v=1575143374116', 'thumb': 'https://cdn.glitch.com/70ea37b5-d264-46e5-a1db-29c786c86515%2FCrabRaveGarfieldBackground.png?v=1575143434563', }, } // inline query bot.on('inline_query', async ({ inlineQuery, answerInlineQuery }) => { const query = inlineQuery.query || ''; console.log('Got query', query); if (query.length > 1) { const result = [ { type: "video", id: "classic", video_url: "https://" + process.env.PROJECT_DOMAIN + ".glitch.me/video/" + encodeURIComponent(query) + ".mp4?v1", mime_type: "video/mp4", thumb_url: "https://" + process.env.PROJECT_DOMAIN + ".glitch.me/video/" + encodeURIComponent(query) + ".png?v1", title: 'Classic' }, { type: "video", id: "otamatone", video_url: "https://" + process.env.PROJECT_DOMAIN + ".glitch.me/video/" + encodeURIComponent(query) + ".mp4?v1&style=otamatone", mime_type: "video/mp4", thumb_url: "https://" + process.env.PROJECT_DOMAIN + ".glitch.me/video/" + encodeURIComponent(query) + ".png?v1&style=otamatone", title: 'Otamatone (original by TheRealSullyG)' }, { type: "video", id: "garfield", video_url: "https://" + process.env.PROJECT_DOMAIN + ".glitch.me/video/" + encodeURIComponent(query) + ".mp4?v1&style=garfield", mime_type: "video/mp4", thumb_url: "https://" + process.env.PROJECT_DOMAIN + ".glitch.me/video/" + encodeURIComponent(query) + ".png?v1&style=garfield", title: 'Garfield (original by @jondancesto)' }, ]; return answerInlineQuery(result); } }); function fixPaths() { for (let style of Object.keys(STYLES)) { try { let path = require('path').join('/tmp', style, 'video'); fs.mkdirSync(path, {recursive: true}); } catch (e) { console.log(e); } } } module.exports = { bot, server(req, res) { if (req.url === '/') { res.end(`
also available as a Telegram bot
brought to you by boringcactus
 `);
    } else if (req.url.startsWith('/add-text')) {
      const parsed = url.parse(req.url, true);
      const data = parsed.query.text;
      const style = parsed.query.style;
      res.writeHead(303, {
        'Location': "https://" + process.env.PROJECT_DOMAIN + ".glitch.me/video/" + encodeURIComponent(data) + ".mp4?style=" + style
      });
      res.end();
    } else if (req.url.startsWith('/video/')) {
      fixPaths();
      if (!fs.existsSync('/tmp/classic/video')) {
        console.log('what???');
        child_process.spawnSync('ls', ['/tmp', '/tmp/classic', '/tmp/classic/video']);
      }
      const parsed = url.parse(req.url, true);
      const match = /^\/video\/(.*)\.(mp4|png)/.exec(parsed.pathname);
      if (match === null) {
        console.error('Bad URL: ' + match);
        res.writeHead(400);
        res.end();
        return;
      }
      const text = decodeURIComponent(match[1]);
      console.log('Match:', text);
      const type = match[2];
      if (type === 'mp4') {
        const ev = hny.newEvent();
        ev.addField('type', 'bake');
        ev.addField('text', text);
        ev.send();
      }
      const style = parsed.query.style || 'classic';
      if (!Object.keys(STYLES).includes(style)) {
        console.error("Bad style");
        res.writeHead(400);
        res.end();
        return;
      }
      const path = require('path').join('/tmp', style, encodeURIComponent(match[0]));
      if (type === 'mp4') {
        res.setHeader('Content-Type', 'video/mp4');
      } else {
        res.setHeader('Content-Type', 'image/png');
      }
      console.log('Name:', path);
      // TODO don't do this dumb thing
      if (!fs.existsSync(path)) {
        if (!fs.existsSync(path + '.png')) {
          const dom = new JSDOM(`
`, {contentType: 'image/svg+xml'});
          const document = dom.window.document;
          const lines = text.split('\n');
          let y = 300 - (75 * lines.length) / 2;
          for (let line of lines) {
            const lineNode = document.createElement('text');
            lineNode.setAttribute('x', 424);
            lineNode.setAttribute('y', y);
            y += 75;
            lineNode.setAttribute('style', "text-anchor:middle;alignment-baseline:middle;font-family:'DejaVu Sans',sans-serif;font-weight:bold;font-size:48pt;fill:white;stroke:black;stroke-width:1px;");
            lineNode.textContent = line;
            document.querySelector('svg').append(lineNode);
          }
          const svg = '\n' + dom.serialize();
          fs.writeFileSync(path + '.svg', svg);
          child_process.spawnSync('convert', ['-background', 'none', path + '.svg', path + '.png'], {stdio: ['inherit', 'inherit', 'inherit']});
        }
        if (type === 'mp4') {
          let background = STYLES[style].video;
          let args = [
            '-hide_banner',
            '-i', background,
            '-i', path + '.png',
            '-filter_complex', 'overlay=x=0:y=0',
            '-c:v', 'libx264', '-preset', 'superfast', '-crf', '27', '-f', 'mp4', '-c:a', 'copy',
            '-y', path
          ];
          console.log('ffmpeg', ...args);
          let child = child_process.spawnSync(
            'ffmpeg',
            args,
            {
              stdio: ['inherit', 'inherit', 'inherit']
            }
          );
        } else {
          let background_url = STYLES[style].thumb;
          let background = '/tmp/' + style + '.png';
          if (!fs.existsSync(background)) {
            child_process.spawnSync('curl', ['-o', background, background_url]);
          }
          child_process.spawnSync('composite', [path + '.png', background, path]);
        }
      }
      if (fs.existsSync(path)) {
        fs.createReadStream(path).pipe(res);
      } else {
        console.log('what the fuck', path);
      }
    }
  },
};
        
        
        
      `);
    } else if (req.url.startsWith('/add-text')) {
      const parsed = url.parse(req.url, true);
      const data = parsed.query.text;
      const style = parsed.query.style;
      res.writeHead(303, {
        'Location': "https://" + process.env.PROJECT_DOMAIN + ".glitch.me/video/" + encodeURIComponent(data) + ".mp4?style=" + style
      });
      res.end();
    } else if (req.url.startsWith('/video/')) {
      fixPaths();
      if (!fs.existsSync('/tmp/classic/video')) {
        console.log('what???');
        child_process.spawnSync('ls', ['/tmp', '/tmp/classic', '/tmp/classic/video']);
      }
      const parsed = url.parse(req.url, true);
      const match = /^\/video\/(.*)\.(mp4|png)/.exec(parsed.pathname);
      if (match === null) {
        console.error('Bad URL: ' + match);
        res.writeHead(400);
        res.end();
        return;
      }
      const text = decodeURIComponent(match[1]);
      console.log('Match:', text);
      const type = match[2];
      if (type === 'mp4') {
        const ev = hny.newEvent();
        ev.addField('type', 'bake');
        ev.addField('text', text);
        ev.send();
      }
      const style = parsed.query.style || 'classic';
      if (!Object.keys(STYLES).includes(style)) {
        console.error("Bad style");
        res.writeHead(400);
        res.end();
        return;
      }
      const path = require('path').join('/tmp', style, encodeURIComponent(match[0]));
      if (type === 'mp4') {
        res.setHeader('Content-Type', 'video/mp4');
      } else {
        res.setHeader('Content-Type', 'image/png');
      }
      console.log('Name:', path);
      // TODO don't do this dumb thing
      if (!fs.existsSync(path)) {
        if (!fs.existsSync(path + '.png')) {
          const dom = new JSDOM(`
`, {contentType: 'image/svg+xml'});
          const document = dom.window.document;
          const lines = text.split('\n');
          let y = 300 - (75 * lines.length) / 2;
          for (let line of lines) {
            const lineNode = document.createElement('text');
            lineNode.setAttribute('x', 424);
            lineNode.setAttribute('y', y);
            y += 75;
            lineNode.setAttribute('style', "text-anchor:middle;alignment-baseline:middle;font-family:'DejaVu Sans',sans-serif;font-weight:bold;font-size:48pt;fill:white;stroke:black;stroke-width:1px;");
            lineNode.textContent = line;
            document.querySelector('svg').append(lineNode);
          }
          const svg = '\n' + dom.serialize();
          fs.writeFileSync(path + '.svg', svg);
          child_process.spawnSync('convert', ['-background', 'none', path + '.svg', path + '.png'], {stdio: ['inherit', 'inherit', 'inherit']});
        }
        if (type === 'mp4') {
          let background = STYLES[style].video;
          let args = [
            '-hide_banner',
            '-i', background,
            '-i', path + '.png',
            '-filter_complex', 'overlay=x=0:y=0',
            '-c:v', 'libx264', '-preset', 'superfast', '-crf', '27', '-f', 'mp4', '-c:a', 'copy',
            '-y', path
          ];
          console.log('ffmpeg', ...args);
          let child = child_process.spawnSync(
            'ffmpeg',
            args,
            {
              stdio: ['inherit', 'inherit', 'inherit']
            }
          );
        } else {
          let background_url = STYLES[style].thumb;
          let background = '/tmp/' + style + '.png';
          if (!fs.existsSync(background)) {
            child_process.spawnSync('curl', ['-o', background, background_url]);
          }
          child_process.spawnSync('composite', [path + '.png', background, path]);
        }
      }
      if (fs.existsSync(path)) {
        fs.createReadStream(path).pipe(res);
      } else {
        console.log('what the fuck', path);
      }
    }
  },
};