Hello everyone, and welcome to an introduction to mIRC scripting. As a scripter, your new best friend will be the mIRC help file. You can access the help file by typing: /help or you can search for a specific item in the help file by typing: /help $nick (for help regarding the $nick alias). It is a common misconception that only "noobs" use help files. Let me assure you, this is not the case. I frequently use the help file for reminders :D Most of your questions will be answered within the help file. It is very well documented, and provides excellent examples for most everything. You should consult this before you ask questions in #scripting because chances are you will just be directed back to it in the end. With that said, let us dive into mIRC scripting. The most common type of scripts are a remote scripts. Generally speaking, "bots" are just a series of remote scripts. These are triggered remotely (hince the name), so to run a bot, you must be running a seperate client. One client will be your default client, and the other will be your bot, with the script(s) loaded. To access the remote script editor, either press alt+r, or go to Tools, and select Scripts Editor. At this point, we now must decide what we want to script. Let's start with a simple command where a user types @ping and the bot replies Pong!. This might seem confusing at first, but don't get discouraged! We will pick it apart piece by piece. The exact code for this will look like the following: on *:TEXT:@Ping:#:{ msg $chan Pong! } Each part of a remote script begins with the word "on". Imagine when you see lines in that format, it says "on text" rather than "on *:TEXT:..." There are TONS of other types (such as on action for /me events, or on join), which can all be seen by typing simply, /help on. Many of these are rarely used, so it is not important to know every one of them. The second part you'll see I put a "*" (on *:TEXT:...). This is where you put the access level to be matched. To match all users with or without access, you can either use the wildcard, "*", or "1". All users have a default access level of 1 if not specified elsewhere (note: this has nothing to do with channel access whatsoever.) The third part is rather obvious... "TEXT" means the script is looking for a text event (as opposed to an ACTION event (/me) etc.). This does not have to be in all caps, but, in my experience, it tends to help the eye find things easier in longer scripts. The fourth part is the matchtext. This is where the script searches for specific text within a message, and is also where wildcards come in extremely handy. If you want to match a line of text that starts with "Hi" you would use "Hi*". For "Hi" anywhere in in the text, you would use "*Hi*". The thing with the latter is that "while" will match the text, so that portion of the script will be executed, even if that is not what you intended. (Finally) the fifth part (on *:TEXT:@ping:#:{) is where you specify where the script should be looking for the previous statements. To match any window (Private message, channel etc), you can use the wildcard, "*". For all private message windows, you can use, "?", and for all channels you can use what we used here, "#". Of course you can specify specific channels such as #channel1, or a list of channels such as #channel1,#channel2 (note: there are no spaces between channel names, just commas) You probably know you can change your nick by typing /nick MyNewName. If you put that same command within brackets ( { here } ), the "/" is not needed. If you notice in our ping script, we had: on *:TEXT:@Ping:#:{ msg $chan Pong! } We did not use /msg, but instead msg. I'm sure you've already realised by now that $chan is our target channel. MIRC has a bunch of built-in aliases that come in handy in this manner. Other common built-in aliases used in remote scripts include $nick (the nick of the user who triggered the event (often used in "notice $nick ...")), $0 (returns the number of words (ex: "this is some test text" returns "5")), and $me (returns your current nickname). To get at the text someone says, you can use $1 (word #1), $2 (word #2) etc., or if you want everything they said, you can use $1-. Similarly, to get all the words after the first, you can use $2-. You can also specify a range between any set of words ($5-9 for example would return words 5, 6, 7, 8, and 9). Now to make a wild leap into variables... There are three different types of variables, and we will deal with two of them today. 1) A global variable, 2) A local variable, and 3) A binary variable. 1) Global variables are set by /set %variable_name value. After they are set, you can view them in the Variables tab (found next to the Remotes tab in the scripts editor). You can also view the value of a variable by typing //echo -a %variable_name (the -a flag for echo means "echo in the active window"). These variables are set until you unset them. Setting a variable with the same name to a different value will just over-write the old variable. To unset a global variable, use /unset %variable_name. 2) Local variables only exist within the function they are created in and are declared /var %variable_name = value. This saves you from having to unset the variable when you're through using it, but you cannot access it after the code is done executing. 3) Binary variables have no size limit (unlike the other two), and are limited only by memory, can only be accessed by commands /bread and /bwrite, so they can't be printed or assigned or edited, and are automatically destroyed when a script finishes processing. Because you cannot access them "normally," we will not be discussing them. With one more bit of knowledge, we can begin to code. We now delve into if then else. This part is incredibly logical, so if you do not get it, take a step back and try to think less about the code and more about how it should work. (Note: I highly suggest reading the help file on this (/help if then else) if you do not know the operators available. However, here are a few: equals (a==b), greater than (a>b), less than, (a 3) { msg $chan Five really is greater than three! }. If 5 were to be less than 3, however, the code that would message the channel would not execute. Elseif Syntax: elseif (this is true) { do this }. This is the exact same as above, but it requires at least one "if" before it. Else Syntax: else { do this }. Same idea, but there is not "if" statement. This code will execute if the "if" and "elseif" statements failed to. Now that the boring crap is out of the way, we can get to the actual code! Congratulations on making it this far (hopefully understanding at least most of what I said). Now to have a little fun with what we know. Let's take a somewhat over-used "action" on mIRC clients, and mess with it a bit. The action, "ME slaps YOU around a bit with a large trout" is seen everywhere, so let us take those trouts and keep them in a jar just to see how many we get. In pseudocode (the logic behind the code w/o the code itself), this is what we have: If my nick is in the action, and "slaps*around a bit with a large trout" (remember the wildcards from earlier) is in the action, then respond and add the trout to however many trout we currently have. Simple enough... on *:ACTION:*slaps*around a bit with a large trout:#: { if ($me isin $1-) { inc %trout.count describe $chan takes the trout and puts it in his/her trout jar. I have collected %trout.count trout! } } There are a few things here than you might not know. "isin" will find if v1 is a word in v2 (In this case, v1 = $me, and v2 = $1-). Also, "inc" will incriment an integer value by 1. You could acheive the same effect by using set %trout.count $calc(1 + %trout.count) (Calc does exactly what you think it does). Also, scripts do not recognize /me as a command, rather, they use "describe". It is important to note that in mIRC, if a variable does not exist when you "inc" it, it will create the variable, set it to 0, and the "inc". In most languages this is not the case. Variables also have flags you can set (again, view the help file for more info). A fairly common way to do a simple flood protection would be to set a variable to unset after a few seconds, and if that variable is set, do not process the command. Taking our last script, this is easy to throw in. The flag to unset a variable after 5 seconds would be -u5 (set -u5 %myvar text). on *:ACTION:*slaps*around a bit with a large trout:#: { if (!%trout.spam) { if ($me isin $1-) { set -u5 %trout.spam SPAMMED!!! inc %trout.count describe $chan takes the trout and puts it in his/her trout jar. I have collected %trout.count trout! } } } Again, some syntax you might not have seen before: if (!%variable) is the same as if (%variable == $null) ($null essentially means "does not exist"). You can set %trout.spam to anything in this case, because the script just checks to see if the variable exists before continuing, not what the value is. What if you need to do something over and over? Well, after all, this is what computers were designed to do. This leads to the "while" statement. It works similarly to the "if" statement, but will keep cycling through the code over and over until the "if" statement is false. Example: alias loop { var %x = 1 while (%x < 10) { echo -a %x inc %x } } There are two parts to this example really (condensing things a bit now). An alias is something run either by $aliasname or /aliasname (usually depending on if it returns a value or not. This example does not return anything, so you would normally access it by /loop) You can load this up in your remotes and try it out for yourself. What exactly happens? Let's walk through it. The variable "x" is set to "1" to start with. Then we hit the while loop which checks if "x" is less than "10". It is less than "10", so we continue. It echo's the value of "x" into the active window, and then increases "x" by 1. It now jumps back up to the beginning of the loop and checks, once again, if "x" is less than "10". "X" is now "2", so it proceeds (etc) until "x" is "10", at which point it will break out of the loop and exit. Also, if you ever make a mistake and enter an infinite loop, pressing alt+break (the key next to scroll lock) will break the loop keeping you from having to restart mIRC. Before I end the class, I want to hit on two mIRC aliases that are incredibly useful. One is really simple if you understand "if" statements. That is, $iif. This is used the exact same way "if" is used, but it combines if|then|else all in one place. $iif(this is true,do this,else do this). A nice place to use this in is if the a command was used in a channel (if it was used in a private message, generally you want to message them back. If it's in the channel, you generally notice them so they don't have to change windows). For this I use: $iif($chan,notice $nick,msg $nick) My text. This works in that "if $chan exists" (which it only does if the command was used in a channel), "notice the user 'My text'" otherwise "message the user 'My text'". If you understood all of the above easily, then this next part is for you. If you're still having troubles with it, you can ignore this next bit. Assuming you do not know regex (and I admit, I am even shaky on it), $gettok will be your new best friend for parsing nearly everything (that and a combination of $left and $right). To understand how gettok works, you should first understand ASCII characters. A computer can only store numbers (remember, deep down it's 0s and 1s), so when it stores characters it really has a number for each letter. To figure which character is which you can //echo -a $chr(44) (comma), or the reverse: //echo -a $asc(a) (97). Also, there are tons of charts online. Just google "ASCII Table" Thinking back to how $1 $2 etc. works, you can simulate this with $gettok. This is done by taking our text, say "my test text", and finding the Nth "token". For complete words, we use character 32 (which is the space character). We would use gettok in the following manner: $gettok(my test text,1,32) for the word "my". Similarly, $gettok(my test text,2,32) returns "test" and $gettok(my test text,3,32) returns "text". You can use any character, which is what makes this tool so powerful. You might not have a use for this right away, but it will come in handy very soon, especially if you try to parse websites (oooooh, sockets!) without regex. I cannot stress enough the importance of the help file. Once you get the hang of scripting, this is where 90% of your learning will come from. Do not be afraid to ask questions, but make sure that we can't copy paste the answer out of the help file, or we will be less likely to help later! No one likes a lazy coder ;) I want to thank all of you for coming out so late on a Sunday to attend this class. I apologize for the time, but I live in Alaska and I'm -9GMT. If you have any questions, ask away!