Communi  3.4.0
A cross-platform IRC framework written with Qt
Bot example

The example presents a simple IRC bot that can handle half a dozen commands. This article focuses on handling messages, and parsing and sending commands. See the Minimal example for basics on setting up an IRC connection.

bot.png
The bot example in action

The bot uses IrcCommandParser for parsing commands from messages received from other clients. In order to take it in use, the desired commands must be first introduced.

parser.addCommand(IrcCommand::CtcpAction, "ACT [target] <message...>");
parser.addCommand(IrcCommand::Custom, "HELP (<command...>)");
parser.addCommand(IrcCommand::Nick, "NICK <nick>");
parser.addCommand(IrcCommand::Join, "JOIN <#channel> (<key>)");
parser.addCommand(IrcCommand::Part, "PART (<#channel>) (<message...>)");
parser.addCommand(IrcCommand::Quit, "QUIT (<message...>)");
parser.addCommand(IrcCommand::Message, "SAY [target] <message...>");

Some of the commands are context sensitive. Thus, the parser must know the list of channels the bot is on. This is easily achieved with help of IrcBufferModel.

connect(&bufferModel, SIGNAL(channelsChanged(QStringList)), &parser, SLOT(setChannels(QStringList)));

Furthermore, the current target of the parser is later updated whenever messages are received and parsed for commands.

In order for the bot to be able to process private and channel messages, it connects to the IrcConnection::privateMessageReceived() signal.

connect(this, SIGNAL(privateMessageReceived(IrcPrivateMessage*)), this, SLOT(processMessage(IrcPrivateMessage*)));

The current target of the parser, and command triggers are chosen depending on whether the received message is a channel or private message. The next snippet illustrates how the logic of the bot has been implemented using IrcCommandParser.

void IrcBot::processMessage(IrcPrivateMessage* message)
{
if (message->isPrivate()) {
// private message: reply to the message sender
// => triggers: "!<cmd> <params>" and "<cmd> <params>"
parser.setTarget(message->nick());
parser.setTriggers(QStringList() << "!" << "");
} else {
// channel message: reply to the target channel
// => triggers: "!<cmd> <params>" and "bot: <cmd> <params>"
parser.setTarget(message->target());
parser.setTriggers(QStringList() << "!" << nickName().append(":"));
}
IrcCommand* cmd = parser.parse(message->content());
if (cmd) {
if (cmd->type() == IrcCommand::Custom && cmd->parameters().value(0) == "HELP") {
help(cmd->parameters().mid(1));
} else {
sendCommand(cmd);
if (cmd->type() == IrcCommand::Quit) {
connect(this, SIGNAL(disconnected()), qApp, SLOT(quit()));
QTimer::singleShot(1000, qApp, SLOT(quit()));
}
}
}
}

Notice how the help command is implemented as a "custom" command. This is because it does not correspond to any actual IRC command, but we want the bot to reply with a help message instead of performing an actual command.

Finally, the last snippet shows the bot in action!

[17:42:44] ! CommuniBot172375 (~communi@12.34.56.78) joined #communi
[17:42:59] <jpnurmi> !say Communi rocks!
[17:42:59] <CommuniBot172375> Communi rocks!
[17:44:05] <jpnurmi> CommuniBot172375: nick CommuniRocks
[17:44:05] ! CommuniBot172375 changed nick to CommuniRocks
[17:44:18] <jpnurmi> CommuniRocks: quit
[17:44:19] ! CommuniRocks (~communi@12.34.56.78) has quit (Client Quit)

Files: