Manual:IRC RC Bot

From Linux Web Expert

To create an IRC bot to display recent changes to your Wiki:

LocalSettings.php

Add the following to your local settings file (see $wgRCFeeds ):

// IRC # port: 1338
$wgRCFeeds['ircfeed'] = array(
    'formatter' => 'IRCColourfulRCFeedFormatter',
    'uri' => 'udp://127.0.0.1:1338',
    'add_interwiki_prefix' => false,
    'omit_bots' => true,
);

SOCAT and ircII (or similar cli irc client)

Use socat instead of netcat. Some versions of netcat (on Debian at least) stop listening after receiving the first RC entry, because the wiki changes the port used to send the UDP packet.

socat -T5 - udp4-listen:1338,reuseaddr,fork | ircII -d -c \#<channel> <nick> <irc_server>

Use -T5 to specify a timeout (5 seconds here) after which each UDP connection will be closed for inactivity. Otherwise, the fork option will create more and more processes for each connection, potentially reaching the open file description limit or exhaust your server resources.

You would normally set up it in a script that runs in background, or launch it inside a screen command like GNU Screen or tmux that you can detach from but keeps running in the background.

If you see Recent Changes entries are printed out on the irc console but not on the channel, you may need to set up an ~/.ircrc file with commands to actually switch the input to the channel view. For example:

WAIT -CMD JOIN #<channel>
WAIT -CMD WINDOW CHANNEL #<channel>

Perl and ircII

Requirements

Requires ircII which can be downloaded from eterna.com.au. It can be found in almost all Linux distributions packaged and ready to install.

This Perl script also requires POE, which you may find was not packaged with your system.

POE may be installed through the CPAN shell which you should find on your system.

  % perl -MCPAN -e shell
  cpan> install POE

When CPAN is first run, it will ask you a series of questions, however the defaults seemed to work fine for me. However I did have a problem with some FTP sites not having the POE file, so I went for ones in the US and they had it.

You may find that POE fails to install with a message that says cannot install unless force is used as a number of tests have failed. You might want to check the seriousness of these errors, however its possible to force via the following syntax.

 cpan> force install POE

Usage

Run this command on the receiving computer:

./UDPserver.pl <port> | ircII -d -c \#<channel> <nick> <irc_server>

In the above entries used in your LocalSettings.php file ‎<port> would be 1338.

UDPserver.pl

#!/usr/bin/perl

use warnings;
use strict;

use POE;
use IO::Socket::INET;

use constant DATAGRAM_MAXLEN => 1024;

if (!$ARGV[0]) {
  print "Usage: $0 <port>\n";
  exit;
}

select((select(STDOUT), $|=1)[0]);

POE::Session->create(
    inline_states => {
        _start       => \&server_start,
        get_datagram => \&server_read,
      }
);

POE::Kernel->run();
exit;

sub server_start {
    my $kernel = $_[KERNEL];

    my $socket = IO::Socket::INET->new(
        Proto     => 'udp',
        LocalPort => $ARGV[0],
    );

    die "Couldn't create server socket: $!" unless $socket;
    $kernel->select_read( $socket, "get_datagram" );
}

sub server_read {
    my ( $kernel, $socket ) = @_[ KERNEL, ARG0 ];
    recv( $socket, my $message = "", DATAGRAM_MAXLEN, 0 );
    print STDOUT "$message";
}

Python

Requirements

  • Twisted

The script is fully contained and thus does not require ircII.

Running

After configuring it you can run it using python udpserver.py.

udpserver.py

from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
from twisted.words.protocols import irc
from twisted.internet import protocol
import sys

recver = None

class RCBot(irc.IRCClient):
    nickname = "rc"
    channel = "#wikirc"
    def signedOn(self):
        global recver
        self.join(self.channel)
        print "Signed on as %s." % (self.nickname,)
        recver = self

    def joined(self, channel):
        print "Joined %s." % (channel,)

    def gotUDP(self, broadcast):
        self.msg(self.channel, broadcast)

class RCFactory(protocol.ClientFactory):
    protocol = RCBot

    def clientConnectionLost(self, connector, reason):
        print "Lost connection (%s), reconnecting." % (reason,)
        connector.connect()

    def clientConnectionFailed(self, connector, reason):
        print "Could not connect: %s" % (reason,)

class Echo(DatagramProtocol):

    def datagramReceived(self, data, (host, port)):
        global recver
        recver.gotUDP(data)

reactor.listenUDP(33333, Echo())
reactor.connectTCP("localhost", 6667, RCFactory())
reactor.run()

Configuration

  • reactor.listenUDP(33333, Echo()): Change the 33333 to your UDP port in LocalSettings.php
  • reactor.connectTCP("localhost", 6667, RCFactory()): Change the IRC server here
  • You can change the nickname and channel in the RCBot class

Caveats

Currently, anyone can "hack" your bot by sending packets to the port you specify. This can be fixed with iptables.

However, in the long run, a more complex bot should be written to allow for restrictions and perhaps some more stuff.

See also