Creation/Dev/Graal IRC

From Graal Bible

IRC Information

Introduction

Graal 4 comes with a built-in form of IRC to allow people to chat between servers in rooms. Like IRC. But not, if you get my point. Now, there are ways in which you can use this IRC system to your advantage. Perhaps you want to script some kind of client for your players to use, or perhaps you want to script some kind of bot. Either way, the communication is simple.

Links

Logging In

To use the IRC, you just need to have basic knowledge of sendtext() and onReceiveText(). The fundamentals are that you have three parameters for each outgoing and incoming message. If you have used onReceiveText() before, you are likely to know it has a structure like this:

function onReceiveText(texttype, textoptions, textlines)

The texttype will always equal "irc" if it is an IRC command. This helps to filter out unwanted commands from, say, the scripted RC. The textoptions will always equal the command that you've sent, and the textlines will equal any parameters necessary for that command.

An important thing to note is that the IRC server will not throw any errors at you if you make a mistake with a command, so you should ensure throughly that your requests are correct.

You send requests like this:

sendtext("irc", "command", "params");

Like any other IRC server, you need to log in before you can use it. This is quite simply done. From your NPC, use the following command:

sendtext("irc", "login", "nickname");

NOTE: The nickname behaviour is different on the serverside. If you are logging in from the serverside, your nickname MUST begin with Graal, EventsBot or IRCBot. If you do not provide a valid nickname, then a suitable name (like IRCBot_2) will be automatically assigned and your nickname ignored. Now, with any luck, you will have been logged in successfully!
NOTE2: There is also the command requesttext(texttype,textoption), but it is not used for irc communication since most irc commands require additional data.

Joining a Channel

Now, you could probably get away with not joining a channel if you felt it necessary, but that would make communicating with you hard. So, why not join a channel? Joining and leaving channels is much like, again, in normal IRC. You sendtext("irc", "join", "#channame") to join a channel, and you sendtext("irc", "part", "#channame") to leave one.

You can be in multiple channels, however, if you are writing a bot, you should probably make sure that you are always replying to the correct channel. I suppose the same applies to if you are writing a client - make sure you are sending to the right channel!

Receiving Information

Information from the IRC server comes to you in the form of onReceiveText(),providing you have logged into the server first. So, you will want to make sure that your script is receiving the information, or you might as well talk to a nice stone. I am not sure that it will help you, however.

Now, earlier in the document, I said about the onReceiveText structure. Just for reference, it looks like this:

function onReceiveText(texttype, textoptions, textlines)

To ensure that you are only receiving information from the IRC server, you should put an if check to check that the texttype is always "irc":

if (texttype == "irc")
{
 // Let's do this if we get IRC data
} 
  else
{
  echo("I am receiving non-IRC data!");
}

NOTE: Commands like "privmsg" will be broadcasted to the whole channel, and will be sent back to you in the process. "join" and "part" are also sent to you to confirm that you have joined a channel, but can also be used for kicking.

The textoptions will equal the command type, such as "join", "part" or whatever.

So, you will want next to check what kind of information is being sent to you:

if (textoptions == "join") echo("You joined the channel successfully!");
if (textoptions == "part") echo("You left successfully");
if (textoptions == "privmsg") echo("Message!");

"privmsg" differs here - it contains messages from anyone, not just yourself. I'll explain about "privmsg" in a second. To know who is joining or leaving your channel you need to handle the "addchanneluser" and "deletechanneluser" types.

You may also receive information of the "notice" or "topic" types. These aren't so important yet and will be explained later.

Working With Messages Specifically

The type you will most likely be interested in receiving is the "privmsg" command. This is the most complicated, as if it isn't used correctly, you'll end up no further forward then you were at the beginning of this section.

  • When you send a "privmsg", you send the destination and the message.
  • When you receive a "privmsg", you receive the source, destination and message.

So, let's assume that you've joined the channel #test. If you want to send a message to #test, you would use the following command:

sendtext("irc", "privmsg", {"#test", "THIS IS MY MESSAGE"});

A sensible idea would be to create a function that makes that mildly less tedious, so let's use this for example:

function send(dest, msg)
{
  sendtext("irc", "privmsg", {dest, msg});
}

Now, you can just use send("#test", "lolmsg"); to send a message to a channel or user.

However! Like I said before with "privmsg", what you get back will not be the same!

Instead of receiving {"destination","message"}, you will get {"source","destination","message"}, where source is the nickname that you logged in with. This allows you to find out who sent a message in the case that you are writing a client script, or for using people's names as an identity, say, with a bot.

So, what about parsing an incoming privmsg?

function onReceiveText(texttype, textoptions, textlines)
{
 if (texttype == "irc")
 {
   if (textoptions == "privmsg")
   {
     // Do some stuff here
   }
 }
}

In the function, you can read who sent the message with textlines[0], where it was directed to with textlines[1] (say, #test) and the actual message with textlines[2].

Logging Out

If you are writing a client, or a bot that might have a reason to log out of the IRC server, you send a "logout" command like this:

sendtext("irc", "logout", "");

NOTE: The third parameter is ignored in this case.

Other IRC Commands

There are a couple of other IRC commands, such as TOPIC and NOTICE.

topic (Channel Topic)

TOPIC commands are used to set the topic of a channel. This might be useful for you if you are writing a client, and simply uses this format:

sendtext("irc", "topic", {"#channel", "Topic goes here"});

You should also receive any TOPIC commands in the same format.

notice (Notices)

NOTICE is similar to PRIVMSG, but is considered less important, and while PRIVMSG to a single player is automatically converted into a regular PM, NOTICE is not, and can so be used for sending information back to the user. The events bot is using NOTICE commands for event management.

addchanneluser (Channel Join Notifications)

ADDCHANNELUSER is received when a new user joins a channel. The received data contains the channel name, irc nick, Graal server and account name of the new user.

deletechanneluser (Channel Part Notifications)

DELETECHANNELUSER is received when a user leaves a channel. Only the channel name and the irc nick of the leaving user are received.

Multiple bots on one machine

It is possible to have more than one irc bot per machine - on the client you need to use two different weapon scripts which both login to the irc, on serverside you must use two independent database npcs or weapon scripts which both login to the irc and possibly use different nicknames.

Extra Information

Events Bots

For making it possible that events from your server are displayed on the global events list of Graal4, the server needs to have an EventsBot. That is an irc bot with a name starting with EventsBot. That npc or weapon npc automatically gets information about when events should be started. It needs to login to the irc and then wait for instructions using onReceiveText. If there is no EventsBot for the server, then events will not work for it. The EventsBot also needs to send some information to the IRCBot about who is Events Admin on the server, and which channel should be used for talking about the events. That must be done as answer to a "!eventsbotlogin" message from the IRC Bot:

function onCreated() doConnect();
function onInitialized() doConnect();
function onServerListerConnect() doConnect();

function doConnect()
{
  sendtext("irc", "login", "EventsBot");
  sendtext("irc", "privmsg", {"IRCBot", "!resetevents"});
}

function onReceiveText(texttype,textoption,textlines)
{
  if (texttype == "irc" && textoption == "privmsg" &&
  textlines[0] == "IRCBot" && textlines[2] == "!eventsbotlogin")
  {
    sendtext("irc", "privmsg", {"IRCBot", {"!eventadmins", "AccountName", "AccountName2"}});
    sendtext("irc", "privmsg", {"IRCBot", {"!eventchannel", "#ChannelName"}});
  }
}

The EventsBot will also need to start events on the server, and tell the IRCbot about the status of the events.

Quick Reference

Here are some quick reference notes.

Outgoing data

Usage:

sendtext("irc", command, parameters)

Format:

  • command: parameters - description

Outgoing data:

  • login: name - Connects to the IRC using name as the connection's nickname
  • logout: "" - Disconnects from the IRC
  • privmsg: {destination, message} - Sends a message to a channel or user
  • notice: {destination, message} - Sends a "notice" message to a channel or user
  • join: channelname - Join channelname
  • part: channelname - Leave channelname
  • topic: {channel, topic} - Set the topic for the channel

Incoming data

Usage:

function onReceiveText(texttype, textoptions, textlines)

Format:

  • textoptions: textlines - description

Note: The texttype will always be "irc" when information is sent from the IRC server. Incoming data:

  • privmsg: {source, destination, message} - Sends the source of a message, where the message was directed to (destination) and the message
  • notice: {source, destination, message} - Sends the source of a notice, where the notice was directed to (destination) and the notice
  • addchanneluser: {channel, name, (servername), (account)} - Mechanism used to alert IRC scripts when users have joined the current channel, and lists users in the channel upon join. Shows the channel that name joined.
  • deletechanneluser: {channel, name} - Mechanism used to alert IRC scripts when users have left the current channel
  • join: {channel} - Mechanism used to confirm that you have joined channel
  • part: {channel} - Mechanism used to confirm that you have left channel

Nickname Rules

If you are connecting from the serverside, you must start your nickname with one of the following:

  • IRCBot_
  • EventsBot_
  • Graal_

If an invalid nickname is provided from the serverside, you will automatically be assigned with an IRCBot_n nickname.

Nicknames should not contain spaces.

IRCBots for Old Scripting

It is now possible for servers using the old scripting engine to do IRCBots using the commands sendtext, requesttext, and the "if (receivetext)" event. A script example for a simple EventsBot:

if (created || initialized || serverlisterconnect) {
  doConnect();
}

function doConnect() {
  sendtext irc,logout,;
  sendtext irc,login,EventsBot_OldScript;
  setstring this.options,;
  addstring this.options,IRCBot;
  addstring this.options,!resetevents;
  sendtext irc,privmsg,#s(this.options);
  sendtext irc,join,#OldScriptTest;
}

if (receivetext) {
  sendtonc irc: #p(0) - #p(1) - #p(2) - #p(3) - #p(4) - #p(5) - #p(6) - #p(7);
  if (strequals(#p(0),irc) && strequals(#p(1),privmsg) &&
      strequals(#p(2),IRCBot)) {
    if (strequals(#p(4),!eventsbotlogin)) {
      setstring this.options,;
      addstring this.options,IRCBot;
        setstring this.options2,;
        addstring this.options2,!eventadmins;
        addstring this.options2,Stefan;
        addstring this.options2,Spark910;
      addstring this.options,#s(this.options2);
      sendtext irc,privmsg,#s(this.options);

      setstring this.options,;
      addstring this.options,IRCBot;
        setstring this.options2,;
        addstring this.options2,!eventchannel;
        addstring this.options2,#OldScriptTest;
      addstring this.options,#s(this.options2);
      sendtext irc,privmsg,#s(this.options);
    } else if (strequals(#p(4),!startevent)) {
    } else if (strequals(#p(4),!resetevent)) {
    }
  }
}

You also need to add a few script lines to the Control-NPC because in old scripting only the Control-NPC gets an event if the serverlister (=irc server) is reconnecting:

if (serverlisterconnect) {
  with (getnpc(EventsBot)) {
    // Forwarding the event to the Events Bot
    callnpc -1,serverlisterconnect;
  }
}