Search code examples
bashdownloadbotsirc

How to download XDCC packs (from IRC bots) from the command-line?


Let's say we have an IRC #ChannelName at irc.server.com where people can freely download files from the bots, by commands such as /msg BotName xdcc send #123, and we want to download one such file into our /download/dir. How to do it in a simple, wget/curl-like, command?

It may be more than one command, or a script, but bear in mind that I want to encapsulate this into a script so I can just type something along the lines of

irc-download.sh irc.server.com ChannelName BotName 123 /download/dir

Then wait a while, and have the file, like it was a wget download.

Good things to have in a solution:

  • Is cross-platform (i.e., Windows binary or source code that can compile into it, or script).
  • Has some sort of progress indication.
  • Can download two files at the same time (i.e., has no problem connecting twice to same server).
  • Is secured against a bad bot sending other unrequested files in the same session.
  • Is mostly self-contained (i.e., any needed binaries can run by themselves).

Solution

  • I've looked high and low for a solution that is not super cumbersome (like installing Cygwin on Windows, come on, there are IRC clients coded in ~250 lines of C, you can even telnet this ****).

    And while nobody has simply made a program that does this basic task on a protocol older than your grandma's swimsuits, turns out we live in a world where NodeJS is a thing that exists.

    So yeah, this is simple.

    First, install these NPM packages (globally with -g if you want):

    npm install irc xdcc progress
    

    Then, put this code in irc-download.js:

    var irc = require('xdcc').irc, ProgressBar = require('progress'), progress, arg = process.argv;
    var user = 'user_' + Math.random().toString(36).substr(2), bar = 'Downloading... [:bar] :percent, :etas remaining';
    var client = new irc.Client(arg[2], user, { channels: [ '#' + arg[3] ], userName: user, realName: user });
    var last = 0, handle = received => { progress.tick(received - last); last = received; };
    
    client.on('join', (channel, nick) => nick == user && client.getXdcc(arg[4], 'xdcc send #' + arg[5], arg[6]));
    client.on('xdcc-connect', meta => progress = new ProgressBar(bar, {incomplete: ' ', total: meta.length, width: 40}));
    client.on('xdcc-data', handle).on('xdcc-end', r => { handle(r); process.exit(); } ).on('error', m => console.error(m));
    

    Then you can use basically the same command line I was going for in the question:

    node irc-download.js irc.server.com ChannelName BotName 123 /download/dir
    

    Javascript is like the Christopher Hitchens of programming or something.