mIRC ScriptBox
Style Guide or The Art of Scripting

Collected by the #HelpDesk (DALnet) Team

Table of Content

  1. Introduction
  2. Keep your code clear and easy to read
  3. Use && and || operators in If-Then-Else statements when possible
  4. Use sensibly named variables
  5. Don't over use colours/bold/underline/reverse etc...
  6. Try and put your aliases, popups and remotes in one file
  7. Where possible, silence commands
  8. Simplify where possible
  9. If you are making an addon, try and use $scriptdir, not $mircdir
  10. Do not use timers to unset variables
  11. Don't let personal preferences interfere too much
  12. Remember the On/Off switch
  13. Local Aliases
  14. Local Variables
  15. Please check all documentation before proceeding...
  16. Script Speed Optimisation Tips
  17. Capitalizing
  18. Grouping Expressions using ()
  19. Grouping Code using {}
  20. More Style Tips


1. Introduction  Back to Top

Writing a script isn't that hard if you follow the rules given in the mIRC Help file. The most problems occur when you made a mistake either by a wrong syntax (which can be quickly resolved using the help file) or if you made one or more logical errors. The last type of errors is extremely hard to find.
Generally mIRC displays a line number where an error occured, so in most cases you will know where to look for an error.

In this Style Guide we will focus on some tips how to use clean coding.


2. Keep your code clear and easy to read  Back to Top

Try to avoid using pipes (The |'s separating commands) in your scripting. Its a lot easier to read and understand your coding when each command is put on a separate line. It also makes it easier for you to check through your code if you think there is an error in it. Take a look at the following examples:


on @*:TEXT:*shit*:#: IF $nick !isop $chan && $nick !isvo $chan { 
ban $chan $nick 3 | 
kick $chan $nick Do Not Say That Word | 
notice $nick Please refrain from swearing in $chan again. | 
msg $chan $nick has been warned about swearing in $chan again. }
NOTE: The four lines have to be on a single line. The line has been split up for display purposes only.
Looks much cleaner and tidier when written like this:
on @*:TEXT:*shit*:#: { 
  IF (($nick !isop $chan) && ($nick !isvo $chan)) { 
    ban $chan $nick 3 
    kick $chan $nick Do Not Say That Word 
    notice $nick Please refrain from swearing in $chan again. 
    msg $chan $nick has been warned about swearing in $chan again. 
  } 
} 
Note 1: You can see that we put each expression in separate brackets and finally surround them by a pair of brackets for the whole IF-Statement. (See more in Section 18 and Section 19) This would make future expanding or understanding the logical structure of the IF-Statement a lot easier.
Note 2: You could also combine the ban and kick commands into a single command. If you specify the -k switch to the /ban command, mIRC performs a ban/kick combination on the nickname (See the mIRC Help file).

Result: The script is a lot easier to read now. Also, there is a minor speed increase - but nothing that the human eye would notice but on biggers scripts it will count.



3. Use && and || operators in If-Then-Else statements when possible  Back to Top

Using && (Logical AND) and || (Logical OR) in if-then-else statements cut down on code. It also gives the code a more clean, easy to read, look. The following is an example using && and || statements.

The bad coding:

on @*:TEXT:*:#: { 
  IF (fuck isin $1-) { 
    IF ($nick !isop $chan) { 
      IF ($me isop $chan) { 
        kick $chan $Nick No Swearing. | ban $chan $nick 3 
} } } 
  IF (dick isin $1-) { 
    IF ($nick !isop $chan) { 
      IF ($me isop $chan) { 
        kick $chan $Nick No Swearing. | ban $chan $nick 3 
} } } 
  IF (shit isin $1-) { 
    IF ($nick !isop $chan) { 
      IF ($me isop $chan) { 
        kick $chan $Nick No Swearing. | ban $chan $nick 3 
} } } 
}
The compact coding:
ON @*:TEXT:*:#: { 
  IF ((fuck isin $1-) || (shit isin $1-) || (dick isin $1-)) { 
    ; Note: You don't need to test if you are Op since ON @*: does this already
	; But we don't want to ban our Op's so we check first 
    IF ($nick !isop $chan) { 
      kick $chan $nick No Swearing
	  ban $chan $nick 3 
    } 
  } 
}

4. Use sensibly named variables  Back to Top

When setting variables, try to give them a name which relates to their function - not just some made up mumbo-jumbo. By sensibly naming variables, it makes their purpose easily identifiable. This can come in handy when creating a script which depends on many variables, as each is easy to remember and identify - especially when bug fixing. Below is an example on how to, and not to, name variables.

Bad way:

alias colournicks { 
  set %misterdem $nick(#,0) 
  set %whattanick 0 
  :start 
  inc %whattanick 1 
  cline $rand(0,15) # $nick(#,%whattanick) 
  if (%whattanick < %misterdem) { goto start } 
  unset %whattanick 
  unset %colournicks 
}
Good way:
alias colournicks { 
  SET %colournicks.totalnicks $nick(#,0) 
  SET %colournicks.currentnick 0 
  :start 
  INC %colournicks.currentnick 1 
  cline $rand(0,15) # $nick(#,%colournicks.currentnick) 
  IF (%colournicks.currentnick < %colournicks.totalnicks) { GOTO start } 
  UNSET %colournicks.* 
}

Another thing to remember with variables is to UNSET them once you have finished using them (UNSET %colournicks.*), as this leaves more room for other variables to occupy the variable file.


5. Don't over use colours/bold/underline/reverse etc...  Back to Top

While there is no restriction on how much colour a person can put in their script, excessive control codes can make the text extremely hard to read or just plain annoying. Try to limit yourself to about 10 control codes per line of text. By control codes, I mean colour,bold,underline and reverse. You sometimes may have to go over 10 codes, but hopefully not too often.

If you don't want to limit your control codes, you have a higher chance of getting banned from a channel for excessive colours/bold/etc...


6. Try and put your aliases, popups and remotes in one file  Back to Top

By putting all your code in one file, it makes it easier for both you and the person using your script to use. Because its only one file, it means the user does not have to worry about finding and loading additional files. It also makes it a lot easier for you to edit the script, as all the code is in one file, thus you won't have to change from remotes to aliases to popups and back etc...

To put aliases into your remote file, simply put the word alias in front of it. Example:

alias colournicks { 
  set %colournicks.totalnicks $nick(#,0) 
  set %colournicks.currentnick 0 
  :start 
  inc %colournicks.currentnick 1 
  cline $rand(0,15) # $nick(#,%colournicks.currentnick) 
  if (%colournicks.currentnick < %colournicks.totalnicks) { goto start } 
  unset %colournicks.* 
}

To put popups in a remote file, use the menu command. Example:

menu Channel, Menubar { 
  Remote-Menu Popups 
  .Aren't They Grand? : /echo -a Yes. 
  .Section 1 
  ..Command A : /echo -a command A 
  .. Command B : /echo -a command B 
  .Section 2 
  ..Command A : /echo -a command A 
  .. Command B : /echo -a command B 
}

The "channel, menubar" part of the menu command defines which area(s) the popups should appear. For a full explanation see Popup Menus in the mIRC Help file index.


7. Where possible, silence commands  Back to Top

Things like timers and raw commands can be silenced, which gives the script a cleaner, tidy look. Stuff like below can be silenced or hidden:

-> Server: privmsg bobby hiya! (/raw privmsg bobby hiya!) 
* Timer 1 activated (/timer 1 1 /beep 10) 
* Timer 1 halted 

All that is needed, is a "." between the "/" and the command. Example:

/.timer 1 1 /beep 10
will be silenced. Try it.


8. Simplify where possible  Back to Top

Simplifying, or making aliases for commonly used commands, is a great way to make more room on a file. One of the most common examples is the /kb (kick/ban) alias. It puts two commands into one alias, so you do not have to always type out /kick $chan $nick and /ban $chan $nick etc... Here is what a typical /kb alias looks like:

alias kb { 
  kick $1 $2 $3- 
  ban $1 $2 3 
}

9. If you are making an addon, try and use $scriptdir, not $mircdir  Back to Top

Many mIRC users don't always put scripts they have downloaded into their main mIRC directory. One of the reasons behind this can be that they like to keep everything separate and not mess up their main dir. So when doing any file functions, try and use $scriptdir, as it is the directory of where the current script is located. And by using $scriptdir, you will keep all your scripts stuff inside the directory the user put it in. Example:

on *:JOIN:#: { writeini $+($scriptdir,seen.ini) $nick join $chan @ $time }

This would write to the file seen.ini in the scripts directory, for this example, lets call it c:\mirc\seen\. If you had used $mircdir, then the seen.ini would have been created in the c:\mirc folder - which is not where the user wanted the seen script to be.


10. Do not use timers to unset variables   Back to Top

I have seen so many scripts do this and its just a waste of space. The /set command has a switch to unset itself after x amount of seconds. Simply put a -uSECONDS after /set and before the variable to tell mIRC how many seconds should it wait before unsetting the variable. Example:

on @*:TEXT:*:#: { 
  IF (%repeat. [ $+ [ $chan [ $+ [ $nick ] ] ] ] === $1-) { 
    kb $chan $nick Repeating Is Prohibited.
  } 
  SET -u30 %repeat. [ $+ [ $chan [ $+ $nick ] ] ] ] $1- ) 
}

This repeat kicker will kick a user if he says something twice in a row, as long as the user repeats the line within 30 seconds.


11. Don't let personal preferences interfere too much   Back to Top

Many scripters think that their colour scheme will be liked by all. Usually this is not the case - maybe one to two thirds of the scripts users may like it, but there is always that last third. One way of making you script easily colour customizable is to use the $colour identifier. This is especially helpful when using the haltdef function.

What the $colour identifier does is display the numerical value of a colour for a certain event. To see what I mean, in an editbox type //echo -a my kick colour is $colour(kick) - which is this $chr(3) $+ $colour(kick ) colour. Below is examples on how to put it to practical use:

on ^*:KICK:#:{
  HALTDEF
  echo $chan $chr(3) $+ $colour(kick) $+ (!)KICK(!) $nick just kicked $knick $+ : $1- (!)KICK(!) 
}

on ^!*:JOIN:#:{
  HALTDEF
  echo $chan (!)JOIN(!) $chr(3) $+ $colour(join) $+ (!)JOIN(!) $nick just joined $chan $+ . (!)JOIN(!) 
}

To see a list of all the events you can enter into $colour, press ALT and K. The box that pops up displays the events allowed, and allows you to change each events colour.


12. Remember the On/Off switch  Back to Top

Never assume people always want an autogreet on, or that they always want a auto sound-get on. I have seen many people come into #HelpDesk and ask how can I turn this autogreet off. Most of the time we tell them to remove a "on *:JOIN:#: msg $nick" line from their remotes - that is if they know how to go about remotes. A growing number of people don't, and thus will either remove the script all together, or just type /remote off.

It would make life a whole lot easier for them, you the scripter (The emails you receive asking how to turn it off), and help channel personal, if you used a popup switch. You could use variables or groups, it doesn't make too much of a difference.

Variable example:

menu menubar { 
  AutoGreet ( $+ %autog.setting $+ ) 
  .On : { 
    SET %autog.setting ON 
    SET %autog.chan $$?="What channel(s) do you want to run it in?" 
    SET %autog.msg $$?="Enter autogreet message:" 
  } 
  .Off : SET %autog.setting OFF 
} 
on !*:JOIN:%autog.chan:{
  IF (%autog.setting == ON) { 
    .msg $nick %autog.msg 
  }
}

Simply put, the %autog.setting variable must be set to "ON" before the script will do an autogreet. There is a popup to set the variable to ON, and one to set it to OFF. If you are not to familiar with variables, then try the Variable topic in the mIRC Help File index.

Group example:

menu menubar { 
  AutoGreet ( $+ $group(#autog) $+ ) 
  .On : { 
   .enable #autog 
   SET %autog.chan $$?="What channel(s) should I run in?" 
   SET %autog.msg $$?"="Enter autogreet message:" 
  } 
.Off : .disable #autog 
} 

#autog on 
on !*:JOIN:%autog.chan:{ msg $nick %autog.msg }
#autog end

Similar to the variable example, this one instead of setting a variable on or off turns a group on (.enable #autog) or off (.disable #autog).


13. Local Aliases  Back to Top

If you have some aliases that no other script should use, then don't let any other script use them. First, I would start the alias name with the initials of the script. Next in the remote file where you have your alias, put "-l" after alias and before the alias name. This makes that particular alias internal - thus no other scripts can use it. Example:

alias -l web-calc { 
  IF (($isfile($1)) && ($isid)) {
    RETURN $calc($ctime - $file($1).mtime)
  }
}

What that alias does is return the time in seconds since a filename was last modified. Its for a web type script, thus the alias starts with web. Because its not used by any other remote files it has a "-l" to make it an internal alias. Making aliases internal is also handy if you plan on releasing many scripts which share some code (e.g. the classic atime alias).


14. Local Variables  Back to Top

With the release of mIRC 5.6, you can now have local variables. These variables are only around while that particular code (be it a popup, remote or alias) is being run. This makes it A LOT easier to keep a small variables file. If you are still using an old (< 5.6 ) mIRC then you can still use: set -u0 %var value. Its almost the same.

If you are running mIRC 5.6 or greater then you should use the /var command to create a local variable. The format is similar to /set, except without so many switches:

/var %variablename value

If you are running mIRC 5.61 there is a problem with the /var command when your value contains identifiers or other variables. I strongly recommend you to use:

/var %variablename = value

15. Please check all documentation before proceeding...  Back to Top

What makes you think when someone uses an identifier you made that they used the correct paramaters and format? Don't let your vast knowledge of your script blur your vision - it does happen. The solution is to do error checking. This means make the script check if they first parameter is a channel - or a nickname - or a number - or whatever its ment to be. This means make the script check if its being called as an alias or an identifier. Example:


alias hd-colour { 
  IF (($isid == $true) && ($1 != $null)) { 
    RETURN $calc($count($1-,$chr(3)) + $count($1-,$chr(2)) + 
	$count($1-,$chr(15)) + $count($1-,$chr(22)) + $count($1-,$chr(31)))
	; NOTE: The above 2 lines have to be on a single line 
  } 
  IF (($3 ischan) && ($1 !isop $3)) { 
    kick $3 $1 In excess of 10 control characters ( $+ $2 $+ )- 30second ban
    ban -u30 $3 $1 2 
  } 
}
The above alias has two functions:
  1. When used as a custom identifier - $hd-colour(text to count control codes) - it will count & return the number of colour, bold, underline, reverse, and remove codes in the text.
  2. When used as a /command - /hd-colour <nick> <control codes detected> <channel> - it will remove the person from the channel for using too many control codes.

The ($isid == $true) checks if hd-colour was called as a /command or a $identifier. If it does, and if there is text to count (&& ($1 != $null)) then it should return the number of control codes. The ($3 ischan) checks if the 3rd parameter is actually a channel you are on, and we also need to make sure we are not kicking or banning an op! (&& ($1 !isop $3)).

Now what would happen if we didn't check if it was called as an identifier? Would it return the control codes and kick the person anyway? Or if we left out the ($1 !isop $3) would it kick ops? The if-statements in that alias help maintain a error msg free script.

on @*:TEXT:#:{
  IF ($hd-colour($1-) > 10) { 
    hd-colour $nick $hd-colour($1-) $chan 
  }
}

You may ask why don't I just automatically goto the kick/ban part if its > 10 control codes using the alias instead of the IF ($hd-colour($1-) > 10) etc...
Well the reason is that with this method I can place that IF ($hd-colour($1-) > 10) ... into multiple events (on NOTICE, on ACTION, on TEXT) while keeping only one copy of the code. It also means you can have different limits for different events - on NOTICE could maybe allow 15 control codes, while on ACTION 12 and on TEXT only 10. This makes updating the colour kicker quick and easy - it also saves some disk space.


16. Script Speed Optimisation Tips  Back to Top

Note: The below tips are based upon advice from Khaled & Breaker (Tips #1-4) and from Hammer's scripting experiments (Tip #5-6). Some of these tips may contradict some General Tips.

Tip #1: Unset en masse
Using wildcards to unset variables is faster than individually unsetting them.

Tip #2: Leave commands by them self
Putting everything on a separate line allows faster parsing. I mean *everything*. Every open and close squiggly bracket... everything. This holds true for every programming language. I quote:

"Yup putting everything in one big lump is faster but it sucks from a readability point of view ;) It's the same with any language, e.g.. in C it's faster to have everything inline instead of calling a routine, but I doubt putting the whole of mIRC into one single routine with a billion goto statements would be practical ;)"

Tip #3: or faster parsing in if-then-else statements use ()'s & {}'s
Using ()'s and {} where necessary allows faster parsing. If they aren't present, mIRC has to do a bit of guess work as to where an operator ends and an argument starts etc.

Tip #4: Custom alias slowdown
Having to call on routines (say, custom aliases for example) within routines is extremely slower than putting the routine in code. mIRC can't process information in parallel, so it has to find the called routine, execute it, and then return back to parsing the original routine. This actually relates back to the second point I made. Khaled's quote was actually a comment on a question I asked of this nature.

Tip #5: Single or Multiple lines?
Clearly, single lines are faster. On average, 2.89% faster. That's not enough to break with good programming style unless you need to optimize an area of script for speed. Unless speed is critical in that section of your script, stick to the multiple lines for easier readability. You might have to fix/modify that script later; multiple lines are far easier to read. The experiment which this conclusion is based upon can be found here.

Tip #6: Using / does make a small difference
Clearly, without a slash is faster in a script. On average, 1.53% faster. That's not enough to break with good programming style unless you need to optimize an area of script for speed. Of course, good programming style would suggest that you not use the / in a script anyway. The experiment which this conclusion is based upon can be found here.

Tip #7: Alias first, Remote later
If anyone has ever bothered to test if the aliases file is checked before a remote file when someone types a /command, they would find that the aliases files come first. While I can't imagine the difference being significant, some people may want to know anyway.


17. Capitalizing  Back to Top

This is based a lot on personal taste. So I personally use to capitalize a only few Keywords like Setting/Unsetting Variables, Conditionals and Halting. Here are a few examples:

SET %var abc
UNSET %var

IF ((%var > 0) && (%var < 10)) {
  SET %digits 1
}
ELSEIF (%var > 9) {
  SET %digits 2
}

VAR %var = 1
WHILE (%var < 10) {
  commands
  INC %var 1
}
Again this is a personal choice but here is a list of keywords I use to capitalize:
SET, UNSET, VAR, INC, DEC, GOTO, RETURN, IF, ELSEIF, ELSEIF, WHILE, HALT, HALTDEF, BREAK, EventNames


18. Grouping Expressions  Back to Top

Giving an example of poor coding:
IF %a > %b || %a == %b echo -s $+(%,a) is %a

This code is pretty hard to read and also it would make finding an error difficult. I will explain that part a bit later.
A better coding exampe would be:

IF (%a < %b || %a == %b) <some commands>
We can do better by adding brackets to the expressions:

IF ((%a < %b) || (%a == %b)) <some commands>
The blue brackets surround each expression and the red bracket groups the whole IF expression together.


19. Using { }  Back to Top

Not bad, but would would happen if we made a mistake in the <some commands> part, like typing etc.?

Well it would give the line number of the IF, but where exactly is the error? Is it the IF part or the action taken after the IF?
We can make it easier to track it down. Look at this:


IF ((%a > %b) || (%a == %b)) {
	<some commands>
}

As you can see we added braces '{' and '}' so we can see where the IF-Block starts and ends. Also we did intend the echo line to see that it is inside the this IF-Block


20. Additional Style Tips  Back to Top

Math helps:
Math helps, believe me. Math helps a lot, it gives you a logic way of thinking. If you want to write good applications, you should be good in math. Write a single sentence every line. That means, don't use pipes (at least don't overuse them). Two reasons: It's easier to read (and faster to understand/modify) and also if you get an error, mIRC will tell you the line. If you have like 50 piped sentences in that line, it's going to be a pain to find the error. When you change something, backup your old code. Don't hard-code anything in your addons/script. Make your code as general and multi purpose as possible.

More Style Tips:
  1. Print, read and memorize mirc.hlp
  2. Use local variables. They are faster and cleaner than normal variables.
  3. Don't use variables to store settings, use an ini file, a hash table or used enable/disable groups.
  4. Use /while for loops
  5. If you use IF's and WHILE's you should use parenthesis and brackets, this is faster, easier to read, and also will tell the mIRC parser exactly what you want
  6. Use the /filter command. It's one of the best command in mIRC.
  7. Don't use 'isin' to check if something is in a list. Use $istok() instead.
  8. If you are writing an addon, prefix %variables, #groups, timers, hash tables and dialogs with the name of your addon. If possible, also prefix your custom windows.
  9. In remote events, don't use "on 1:event:". Use "ON *:event:", it's faster and it will trigger users with named levels. If you have an "ON 1:join" event, and a user with ONLY a level called "autoop", he won't trigger that event when he/she joins the channel.
  10. Use the -n flag in $read end $readini to avoid evil evaluations
  11. Use &event and $halted in your addons to avoid conflicts.
  12. In an addon, use $scriptdir instead of $mircdir
  13. Don't use /halt in aliases, use /return if you want to exit a process and give control to the calling process. As a rule, use /halt only in events.
  14. If you are new in dialogs, you should take a look at our Dialog Tutorial to learn how and when the controls are used.
  15. Use the $colour() identifier. Remember that not everybody use your background colour.
  16. Use local aliases to avoid conflicts with other scripts.
  17. You should NEVER use brackets or timers with messages sent by remote users.
  18. Don't use $exists(), use $isfile() and $isdir() instead.
  19. If your script/addon sends tons of replies (@find, !seen, ctcp help, etc) write a queue system or the user will be easily flooded off.
  20. When working with files/directoriess, enclose them with quotes (") to handle filenames with spaces

Final Suggestions:
  1. Don't rip. Ripping is lame, it's for kiddies. If you want to use something from a script, ask the author. Always give credit for other people's work.
  2. Don't use UpPPerCaSE/loWErCaSE or Ê|| chars in your script. If you do that, only kindergarten kids will use it.
  3. Don't send colours to channels, it's very annoying and might get you kicked/banned especially in help channels.