mScriptBox Tutorial
A Scripting Guide to mIRC 6.x

Written by blue-elf
Published with permission.

Table Of Contents

  1. Introduction
  2. How do scripts work in mIRC 6.0?
  3. Variables and Hash Tables
  4. Timers
  5. Custom Windows
  6. Advanced methods
  7. Loading popups and other script files
  8. $scid, $scon, /scid, /scon, and $cid
  9. Conclusion


1. Introduction  Back to Top

This tutorial focusses on mIRC v6.0 and greater. What makes this a landmark release is that multiserver capability has been added finally. For most of us, it has been years of waiting. And now the time has come.

Multiserver means the ability of an IRC client to connect to many servers at one time without having to open a different instance of the program. For many mIRC users, it will definitely be a new experience and might need some time to get used to.

On the surface, having multiserver support in mIRC might only mean more windows to deal with. However, for users who script, multiserver means a lot more than that. Although scripting methods and techniques haven't changed drastically, there are a few things to be considered in order for a script to work properly.

TIP: To minimize the confusion of which network or server an active window is on, press ALT+O (or open the main mIRC Options dialog), go to Connect, and to Options under the Category. There's a Multiserver button there where you can enable the option that says "Show in channel/query titlebars" either the name of the network or your own nickname.

This document is aimed at scripters so that they will know how to deal with multiserver mIRC easily and to minimize possible trial-and-errors.

Take note that the scripting examples might be slightly on the simple side. The reason for this is to accommodate those who do not script very much. I apologize if it gets a bit complicated. It's just that when scripting for a multiserver client, things aren't as simple as they used to be.


2. How do scripts work in mIRC 6.0?  Back to Top

The first thing that you have to understand is that, scripts behave the same way as they did before the multiserver capability was added. This means that all your events, raws, aliases still work like normal. But the difference is that it now triggers for all connections that you have, and the identifiers in the scripts return the values of those identifiers for the connection in which the script was called. To demonstrate, copy and paste the following alias in your aliases section:

MyInfo {
  echo -a I am connected to $server
  echo -a My nick on $server is $me
}

Now, fire up mIRC, connect to two servers. And just in case you don't know yet how to connect to a second server, simply type /server -m .

After connecting, type /MyInfo in the Status Windows for every connection and you'll see that the information vary depending on where you typed the alias.

In the same way, all the other events, raws, etc work the same way. If a raw event was triggered in DALNet, mIRC will know that the event was triggered in DALNet and not in other networks.

Let's jump into other examples. Supposing you have the following code:

on @*:TEXT:*mule*:#mIRC:{
  kick $chan $nick no cows in here!
}

What this does is kick the person who says anything with the text "mule" in the channel #mIRC. I'm sure (and I hope) that everyone knows that already.

In mIRC 6.0, it still works like normal. But as I said, it will trigger for all the connections that you may have. This means that if you are connected to DALNet, EFNet, and Undernet, and you are opped in #mIRC of all networks, the above example will trigger.

In order to make that line more network-specific (meaning, to make it react differently depending on the server or network you are on), you will have to use if-else statements.

If you are not familiar with "if statements" but you can do simple ON TEXT events, then it's about time you have to learn a little bit of it.

Suppose we do not want the above ON TEXT event to trigger in Undernet. What we then do is to add a small if statement before the kick. Thus:

on @*:TEXT:*mule*:#:{
  IF ($network == Undernet) { return }
  kick $chan $nick no cows in here!
}

What this does is check what network you are connected to. If the network is Undernet, we use /return. Meaning, we don't let the script trigger. In all other networks, it will then trigger.

Alternatively, you can use the example below that will have the same effect as the above.

on @*:TEXT:*mule*:#:{
  IF ($network != Undernet) { 
    kick $chan $nick no cows in here! 
  }
}

The above example says that "if the network I am on is not undernet, then perform the kick".

But if you want a certain script to trigger only for a certain network, you would need to write it this way:

(Supposing we want the script to trigger in DALNet only)

on @*:TEXT:*mule*:#:{
  IF ($network == DALNet) { 
    kick $chan $nick no cows in here! 
  }
}

Simple enough? I sure hope so.


3. Variables and Hash Tables  Back to Top

Before you wonder why I am mentioning variables, hash tables, and files here it's because these will be affected greatly. If your variables are not set correctly, it will cause confusion in your script.

As I mentioned above, even though I mentioned that scripts still work the same way, and even if the values of the identifiers vary depending on where a script is 'called', your variables, and hash tables remain 'global'. Their values are dictated by you. Meaning, if you set %variable to the value of X, it will remain as having the value of X no matter which server or network the variable will be 'called'.

To demonstrate, connect to two servers once again. In the first connection, type in the Status Window the following:

/set %test one

And then, type:

//echo -a The value of $+(%,test) is %test

It will echo 'The value of %test is one'. And then type the same //echo line in your second connection, and you'll see that the value of %test is still 'one'.

The same principle applies to hash tables, so I see no need of explaining in great detail about hash tables.

This means that if you have an ON TEXT event, and you set variables for some things, that variable will have the same value in all the connections.

Consider the example below:


on @*:TEXT:*moo*:#mirc:{
  INC %Moo [ $+ [ $wildsite ] ] 1
  IF (%Moo [ $+ [ $wildsite ] ] > 1) {
    kick # $nick you have said moo twice!
    UNSET %Moo [ $+ [ $wildsite ] ]
  }
}

What this does is set a variable with %Moo*!*@*wildsite and increases it. If the value of that variable is greater than 1, then the user who said 'moo' will be kicked from the channel.

In older mIRCs, that script will not cause any problems at all. However, in the latest version of mIRC, a different situation occurs.

Supposing you and the user are on #mIRC both in DALNet and Undernet, and the person says 'moo' in DALNet, the variable is already increased by 1. Now if the person says 'moo' in Undernet, the variable is already increased by 1 as well. This means that the value of the variable will already be 2! The user will then be kicked in one of the networks that you will be on, even if he or she only said 'moo' once in #mIRC for that network.

So if the user says 'moo' in DALNet once, and then says 'moo' in Undernet for the second time, the script will trigger and will kick him or her from #mIRC in Undernet.

Although the example given here is very simple, the effect of this is much much bigger. This would affect existing flood scripts, for instance.

To solve possible conflicts, you have to set variables even more dynamically. Using $server and $network will not be enough. For $server, it will still cause a problem because you can be connected to the same server twice. And as for $network, the same thing as well. Plus the fact that there are servers that do not give a value for $network (mostly in EFNet).

TIP: Use $cid when setting variables. $cid is a new identifier that returns the "Connection ID" for a connection. Basically, mIRC 'tags' each connection with a unique number. For example, instead of doing:

/set %Flood $+ $chan $+ $wildsite 1
; this creates a %Flood#Channel*!*@*.wildsite variable.

...you would now do:

/set %Flood $+ $cid $+ $chan $+ $wildsite 1
; this creates a %Flood1#Channel*!*@*.wildsite variable.

The only difference between the two? The first one doesn't have "1" after the %Flood. But that's just assuming your CID is 1. Remember that $cid can be any positive integer number, but not 0.

And that will make the variable more unique, and thus avoiding conflicts.

TIP: One way to overcome the problem of $network having a $null value is to add a server in your servers.ini and be sure to specify its group to its real network name. You can add/edit servers via the Connect dialog.


4. Timers  Back to Top

Similarly with identifiers, timers are also connection-specific. This means that a /timer run in a certain connection will only have an effect on that connection. Timers are also dependent on the connection.

For example, if I have this snippet:

on *:CONNECT:{ .timer 0 30 ping $!me }

That will create a timer that will ping yourself every 30 seconds. If you get disconnected in that connection, that timer will stop running unless it was a timer with the -o switch (see /help /timers for more info on the switches).

Luckily, mIRC 6.0 has given us a new switch for the /timer command and that is -i. This means that if you do a /timer -i N N /command, that timer will keep on working even if the original connection has been closed.

For example:

on *:CONNECT: { 
  .timerMyTimer -i 0 30 IF ( $!server ) { ctcp $!me ping }
}

This creates a timer called MyTimer and it executes every 30 seconds sending a ctcp ping to yourself if you're connected to a server.

Oddly enough, that above snippet makes the timer 'global' and it executes in all your connections.

So if you're doing some titlebar routines for instance, it will be useful to use the -i switch in the timer.

WARNING: Timer names are still global. This means that you're not allowed to have two timers of the same name for different networks. Meaning, if you have a timer1 in one connection, the other connections cannot 'use' that name. If you do use that name, the timer will be reset and will work from where it was last called.

As an example, connect to two servers. Then in the first server, type:

	//timer1 0 3 echo -s $!network

And then in the second connection, type the same command. You'll notice that the timer1 will stop in the first connection and will then be called from the second connection.


5. Custom Windows  Back to Top

Unlike timers, custom windows have a global effect. This means that a window is just a window. If you open a @window in EFNet, and later on you close the Status Window for EFNet, that @window will not close by itself.

The versions.txt for mIRC 6.0 mentions three new switches for the /window command:

	    -i - makes a @window dynamically associate with the currently
	         active connection. You can use $window().anysc to check
	         if this setting is active.

	    -v - closes window when associated status window is closed
	         The on close event is triggered for the window.

			 If this switch isn't used and the status window is
	         closed, the @window is moved to the end of the switchbar,
	         and is associated with the first available status window.

	    -z - places window button at end of switchbar

For the -i switch, what happens is this:

If I am on McLean.VA.us.undernet.org, and I type /window -i @test for that connection, a window named @test is created. Once I close the Status Window for that server, the @test will 'jump' or move to the next group of windows that is still open.

The other switches should be self-explanatory.


6. Advanced methods  Back to Top

If you have flood protection scripts, what you can do is store your settings based on network and channels. Personally, here's a sample of what I use:


alias WriteFloodSettings {
  writeini -n settings.ini DALNet#mIRC LongSentence on
  writeini -n settings.ini DALNet#mIRC MaxLength 300
}

This would create an entry in the settings.ini that will look like this:

[DALNet#mIRC]
LongSentence=on
MaxLength=300

Then I would retrieve info based on network and channel. A sample alias that grabs info would be this:

alias GetInfo { return $readini(settings.ini,n,$+($network,$1),$2) }

The $1 would contain the channel, and the $2 would contain the setting that I require.

Applying the above example in an event results in something like below:

on @*:TEXT:*:#:{
  IF ($GetInfo($chan,LongSentence) == on) {
    IF ($len($1-) >= $GetInfo($chan,MaxLength)) {
      kick $chan $nick Your sentence is too long!
    }
  }
}

The above example not only prevents self-conflicting events, it has also made the script network and channel specific at the same time.

If you notice, I use $network in the GetInfo alias. The value of the $network will be that of the network which called the alias, as mentioned earlier.

If you're using some other method of storing data in your script, the same concept can be applied. For instance, if you use variables, you would need to create your variables such as:

%DALNet#mIRCLongSentence on
%DALNet#mIRCMaxLength 30

How you grab the info is something that I leave to you.

Or if you're using hash tables, it's just as convenient as using .ini files. You can have a hash table named DALNet#mIRC, and you can simply use:

$hget(DALNet#mIRC,LongSentence)
$hget(DALNet#mIRC,MaxLength)

I will not deal much on how to retrieve info from dynamic variables and hash tables because I assume that only intermediate or advanced scripters will be using them.


7. Loading popups and other script files  Back to Top

I've seen many scripts for 5.91 or older versions of mIRC that loads and un- loads certain files depending on the network the user was on. Unfortunately, for mIRC 6.0, this method may not be very useful. Obviously, it's because you can now be connected to many networks and servers at the same time.

Apart from the earlier methods I've mentioned, there doesn't seem to be any workaround for script files, for the meantime.

But for popups, there will be a lot of $iif usage for your popups to be more dynamic. For example, I have something like this in my personal script:

	; take note that /ns is just an alias for /nickserv command
	$iif($network == DALNet,&NickServ)
	.&Access
	..&List:ns access list
	..&Add...:ns ACCESS ADD $$input(Enter address,1,NickServ Access Add)
	..&Del...:ns ACCESS DEL $$input(Enter address,1,NickServ Access Del)
	..&Wipe...:if ($input(Wipe your access list?)) ns ACCESS WIPE
	.&Drop:ns DROP $$input(Enter a nick and code,1,Nickserv Drop Nick)
	.&Ghost:ns GHOST $$input(Enter nick and password,1,NickServ Ghost Nick)
	.&Identify:ns IDENTIFY $$input(Enter password for $me ,1,Identify Nick)
	.&Recover:ns RECOVER $$input(Enter nick and password,1,NickServ Recover)

This effectively hides the &Nickserv item if the active network is not DALNet. Alternatively, you can use $style(2) to disable items. The important thing to remember is the $iif() statement. You would only use that for the main header of a menu, and then the submenus will either be disabled or hidden.


8. $scid, $scon, /scid, /scon, and $cid  Back to Top

There are many new identifiers and commands in mIRC 6.0. I will however deal with but a few of them which I find most important.

Let's start with $cid identifier.

Each connection that mIRC makes has a unique identification number. This num- ber is called $cid and can range from 1 to a million or more (of course, that is an exaggeration since no one in their right minds would attempt to connect to a million servers at once).

For those of you who are familiar with sockets, this $cid is comparable to $sockname. Take note that $cid are not necessarily in order. Meaning, your first connection can have a $cid of 1 and your second connection can have the value of 7 for the $cid.

TIP: If you wish to do a looping thru all your connections, it is not wise to use a single number and increate it treating it as the $cid for the reason that I mentioned. The best thing would be something like below:


alias ShowMyConnections {
  VAR %i = 1
  WHILE ($scon(%i)) {
    echo -a My connection number %i has a cid of $scon(%i).cid
    INC %i
  }
}

This brings us to the next identifiers, $scon and $scid.

The mirc.hlp gives the following explanation for these identifiers:

$scid(N)[.id] Returns the connection id, where N is a $cid value.

If N = 0, returns total number of open server windows.

If you specify a property which is an identifier, it returns the value of that identifier for that connection. This also works for custom identifiers.

$scon(N)[.id] Returns the connection id, where N is the Nth connection.

If N = 0, returns total number of open server windows.

If you specify a property which is an identifier, it returns the value of that identifier for that connection. This also works for custom identifiers.

The descriptions are pretty clear.

In short, these identifiers can be used to grab information from your current connections even if you are calling an alias from a separate connection.

Remember that all identifiers return the values of those identifiers for the connection in which it is called. This means that if I type //echo -a $me in my Undernet connection, it will echo the value of $me for Undernet. And if I type that same command in another connection, the value of $me is not always the same,unless of course you are using the same nickname in all connections.

So if I have an alias, and the active window is connected to Undernet, I can not just type:

//echo -a My nick for the first connection is $me

To do that, you would need $scon or $scid. Thus:

//echo -a My nick for the first connection is $scon(1).me

We can say that $scon() and $scid() identifiers behave the same way. But the difference between the two is, $scon() treats the number inside it as being the Nth connection, and not as a connection ID.

Here's a snippet that shows some info in all the connections you are on:

alias ShowConnectionInfo {
  VAR %i = 1
  WHILE ($scon(%i)) {
    echo Info for $ord(%i) connection:
    echo Nick: $scon(%i).me
    echo Server: $scon(%i).server
    echo Usermodes: $scon(%i).usermode
    echo ----
    INC %i
  }
}

The above alias will loop thru all your connections and tell information for each one. Try it and see what it actually does.

TIP: $scon(1) will return the $cid value for the 1st connection. This doesn't work the same way for $scid().

$scon and $scid do not accept identifiers that take parameters. This means, $chan(1) would work, but you can't do $scon(1).chan(1) -not only does it look funny, it seems a bit kludgy.

But the good thing about these identifiers, is that they apply to your custom identifiers as well!

If you want to view all the channels you are on, on all networks, you would need the /scid or the /scon command. For example:

alias MyChans {
  VAR %i = 1, %result
  WHILE ($chan(%i)) {
    %result = $addtok(%result,$ifmatch,44)
    INC %i
  }
  RETURN %result
}
alias ShowAllChannels {
  VAR %i = 1
  WHILE ($scon(%i)) {
    echo -a $ord(%i) connection in network: $scon(%i).network
    echo -a Channels: $scon(%i).MyChans
    INC %i
  }
}

Thus, type /ShowAllChannels will tell you all your channels in all servers.

Notice that the original usage would be $MyChans, and yet we were able to use it with $scon.

Alternatively, instead of using the identifiers, you can use /scid and /scon commands. Here is the same /ShowAllChannels alias above, only it's using the /scid command.

alias ShowAllChannels {
  VAR %i = 1
  WHILE ($scon(%i)) {
    scid $ifmatch
    echo -a $ord(%i) connection in network: $network
    echo -a Channels: $MyChans
    INC %i
  }
  scid -r
}

The mirc.hlp explains that /scid and /scon allow you to force scripts to per- form something as though they were called from a certain connection. What I did in the above alias is loop thru all my connections, and use the /scid to 'switch' to the connection id and make it echo the information. Hence, if the alias was called in the first connection with the id 8, the value of $network and $MyChans will then become the values of the connection id specified in the /scid command (in the 'scid $ifmatch' line).

There are a few tricks that you can do with /scid and /scon. For example, if we use /scid -a or /scon -a, the command will be executed as though it was called from that connection or connection id. For example:

alias aquit { scid -a quit $1- }

The above alias will make you quit in all your connections with the $1- as the quit message.

TIP: You can use $!identifiers in /scid -a and /scon -a so that they will be re-evaluated. For example:

//scid -a ctcp $!me ping

That will ping yourself in all the connections. We are using $!me because it will prevent the $me identifier from being evaluated first. Let's say my nick in Undernet is blue-elf, and my nick on EFNet is blue-elf`, if I type:

//scid -a ctcp $me ping

..in an Undernet channel window, the $me will be evaluated to blue-elf. Thus, it will send a '/ctcp blue-elf ping' command in all connections.

Whereas, if you use $!me, the command is sent to every connection as:

'ctcp $me ping' and not 'ctcp blue-elf ping'.

I highly recommend that you read the mirc.hlp on the section where it deals with /scid and /scon as it explains these commands better.

One last example. Below is a custom identifier that tells whether you are connected to any server:

alias IsConnected {
  VAR %s = $scon(0)
  WHILE (%s) {
    IF ($scon(%s).status == connected) { RETURN $true }
    DEC %s
  }
  RETURN $false
}


9. Conclusion  Back to Top

There are many more identifiers, commands, and other issues that are not included in this document. There are just too many of them to be dealt with in a single document. The best way for you to familiarize yourself with the new mIRC is to read this alongside mIRC's versions.txt and the mirc.hlp.

If you have questions regarding this document, you can always email me. I do hope that this document will help and enable you to adjust your scripts and scripting methods to suit multiserver mIRC.