Web DevCenter
oreilly.comSafari Books Online.Conferences.
MySQL Conference and Expo April 14-17, 2008, Santa Clara, CA

Sponsored Developer Resources

Web Columns
Adobe GoLive
Essential JavaScript
Megnut

Web Topics
All Articles
Browsers
ColdFusion
CSS
Database
Flash
Graphics
HTML/XHTML/DHTML
Scripting Languages
Tools
Weblogs

Atom 1.0 Feed RSS 1.0 Feed RSS 2.0 Feed

Learning Lab






Hacking IRC, Part 2
Pages: 1, 2, 3

Author's note: Many people use IRC bots to act as interfaces to other systems or protocols. This article shows you how to write an IRC bot that constantly monitors a news server, waiting for new posts. When a new post turns up, it will alert everyone in the IRC channel.

Expert Hack #65

Announce Newsgroup Posts


Moderated newsgroups are updated only every so often. Have an IRC bot check for new posts instead of wasting your time.

Usenet discussion groups (or newsgroups, as some like to call them) form a worldwide bulletin board system that can be accessed through the Internet. A user can post a message to a newsgroup that will then be seen by anyone else who reads that newsgroup.

Some newsgroups have very infrequent postings or may even be moderated. Moderated newsgroups require a moderator to approve all postings before they end up on the newsgroup. In either case, the only way you can see whether there are new posts is to actually open up your newsreader and take a look. This is a waste of time if there aren't any new messages, so why not make an IRC bot to do it for you?

Connecting to a News Server

Newsreaders communicate with newsgroup servers with NNTP (Network News Transfer Protocol). This is a text-based protocol and is quite easy to understand, so it's not too difficult to make a bot that talks to a newsgroup server. The default port for NNTP is 119.

You can try using Telnet to connect to port 119 of a newsgroup server and issue the GROUPnewsgroup command. If the group exists, the server will reply with a 211 response, showing how many messages there are, followed by the range of the message IDs. You can then request information about all of these posts by entering XOVERlower-upper, where lower and upper define the range of message IDs to request. Figure 10-2 shows a request for the last three messages from the newsgroup alt.irc:


Figure 10-2. Connecting to a newsgroup server with Telnet

The Code

Now that you know how to request message details via NNTP, you can encapsulate this into a separate class that is responsible for getting these details. This class will use its count field to keep track of the most recent message on the newsgroup, so each time it receives a response to the XOVER command, it can tell if new messages have arrived. The getNewSubjects method will then be responsible for returning an array of these new messages.

Create the file NntpConnection.java:

import java.util.*;
import java.net.*;
import java.io.*;

public class NntpConnection {

    private BufferedReader reader;
    private BufferedWriter writer;
    private Socket socket;
    private int count = -1;
    
    public NntpConnection(String server) throws IOException {
        socket = new Socket(server, 119);
        reader = new BufferedReader(
                new InputStreamReader(socket.getInputStream( )));
        writer = new BufferedWriter(
                new OutputStreamWriter(socket.getOutputStream( )));
        reader.readLine( );
        writeLine("MODE READER");
        reader.readLine( );
    }
    
    public void writeLine(String line) throws IOException {
        writer.write(line + "\r\n");
        writer.flush( );
    }
    
    public String[] getNewSubjects(String group) throws IOException {
        String[] results = new String[0];
        
        writeLine("GROUP " + group);
        String[] replyParts = reader.readLine( ).split("\\s+");
        if (replyParts[0].equals("211")) {
            int newCount = Integer.parseInt(replyParts[3]);
            
            int oldCount = count;
            if (oldCount == -1) {
                oldCount = newCount;
                count = oldCount;
            }
            else if (oldCount < newCount) {
                writeLine("XOVER " + (oldCount + 1) + "-" + newCount);
                if (reader.readLine( ).startsWith("224")) {
                    LinkedList lines = new LinkedList( );
                    String line = null;
                    while (!(line = reader.readLine( )).equals(".")) {
                        lines.add(line);
                    }
                    results = (String[]) lines.toArray(results);
                    count = newCount;
                }
            }
        }
        return results;
    }
    
}

The IRC bot will be written so that it spawns a new Thread to continually poll the newsgroup server. Performing this checking in a new Thread means that the bot is able to carry on doing essential tasks like responding to server pings. This new Thread is able to send messages to the IRC channel, as the sendMessage method in the PircBot class is thread-safe.

The bot will also store the time it last found new articles and made an announcement. If it has been less than 10 minutes since the last announcement, the bot will not bother saying anything. This is useful when lots of messages are arriving on a moderated newsgroup, as these tend to arrive in large clusters in a short time.

Create the bot in a file called NntpBot.java:

import org.jibble.pircbot.*;

public class NntpBot extends PircBot {

    private NntpConnection conn;
    private long updateInterval = 10000; // 10 seconds.
    private long lastTime = 0;
    
    public NntpBot(String ircServer, final String ircChannel, final String
            newsServer, final String newsGroup) throws Exception {
        setName("NntpBot");
        setVerbose(true);
        setMessageDelay(5000);
        
        conn = new NntpConnection(newsServer);
        
        connect(ircServer);
        joinChannel(ircChannel);
        
        new Thread( ) {
            public void run( ) {
                boolean running = true;
                while (running) {
                    try {
                        String[] lines = conn.getNewSubjects(newsGroup);
                        if (lines.length > 0) {
                            long now = System.currentTimeMillis( );
                            if (now - lastTime > 600000) {  // 10 minutes.
                                sendMessage(ircChannel, "New articles posted 
to " + newsGroup);
                            }
                            lastTime = now;
                        }
                        for (int i = 0; i < lines.length; i++) {
                            String line = lines[i];
                            String[] lineParts = line.split("\\t");
                            String count = lineParts[0];
                            String subject = lineParts[1];
                            String from = lineParts[2];
                            String date = lineParts[3];
                            String id = lineParts[4];
                            // Ignore the other fields.
                            sendMessage(ircChannel, Colors.BOLD +
                                    "[" + newsGroup + "] " + subject +
                                    Colors.NORMAL + " " + from + " " + id);
                        }
                        try {
                            Thread.sleep(updateInterval);
                        }
                        catch (InterruptedException ie) {
                            // Do nothing.
                        }
                    }
                    catch (Exception e) {
                        System.out.println("Disconnected from news server.");
                    }
                }
            }
        }.start( );
    }
    
}

Note that the Thread is started from the NntpBot constructor and no PircBot methods are overridden—there is no need for this bot to respond to user input, unless you want to modify it to do so.

The main method now just has to construct an instance of the bot, as the constructor also tells the bot to connect to the IRC server.

Create the main method in NntpBotMain.java:

public class NntpBotMain {
    
    public static void main(String[] args) throws Exception {
        NntpBot bot = new NntpBot("irc.freenode.net", "#irchacks",
                "news.kent.ac.uk", "ukc.misc");
    }
    
}

Note that the constructor arguments specify which IRC server to connect to, which channel to join, which newsgroup server to connect to, and which newsgroup to monitor. If you want, you could make the bot more flexible by using the command-line arguments (args) to specify the name of the server, channel, and so forth.

Running the Hack

Compile the bot like so:

C:\java\NntpBot> javac -classpath .;pircbot.jar *.java

You can then run the bot by entering:

C:\java\NntpBot> java -classpath .;pircbot.jar NntpBotMain

The Results

When you run the bot, it will connect to the IRC server and join the channel you specified. Each time a new post appears in the newsgroup, the bot will announce the post details, as shown in Figure 10-3. These details include title, author, email address, and message ID.


Figure 10-3. Running NntpBot on a local newsgroup server

Now you can keep an eye on your moderated newsgroups without having to keep your news client running in the background.

Pages: 1, 2, 3

Next Pagearrow