|----------------------------------------| | Guide to TCL scripting for Eggdrop 1.6 | |----------------------------------------| Copyright (c) 2000-2003 Marijn van Zon (marijn@suninet.nl) This Guide is optimized for Eggdrop 1.6. With a few minor adjustments you can also use this Guide to write scripts for Eggdrop 1.3 and 1.4. If you want to write scripts for older Eggdrops like 1.1.5, then you should try an other Guide (see here (ftp://ftp.eggheads.org/pub/eggdrop/docs/tcl.doc)), because too much has changed since those versions. If you are linking or bookmarking this Guide please use http://www.suninet.nl/tclguide/. That link will always redirect you to the proper site, but my CGI scripts can change and I don't want to have to make "backwards compatibility" in my CGI scripts all the time. [Last updated on February 27th, 2002 - Current release is v1.10] 1. INTRODUCTION 1.1 CONTRIBUTORS 1.2 ACKNOWLEDGMENTS 1.3 REVISION HISTORY 1.4 NEW VERSIONS OF THIS GUIDE 1.5 FEEDBACK 1.6 DISTRIBUTION POLICY 2. IRC AND EGGDROPS 2.1 IRC 2.2 THE IRC PROTOCOL (RFC1459) 2.3 BOTS 2.4 EGGDROP 3. THE TCL LANGUAGE 3.1 OPEN SOURCE 3.2 THE BUILD-UP OF A TCL SCRIPT 3.3 INTERPRETATIONS 3.4 DIFFERENT TCL AND EGGDROP COMMANDS 4. BINDING EVENTS AND CREATING PROCEDURES 4.1 BINDING EVENTS 4.2 CALLING UPON PROCEDURES 4.3 CREATING A PROCEDURE 4.4 ENDING A PROCEDURE 5. VARIABLES 5.1 WHAT VARIABLES ARE 5.2 SETTING STRINGS 5.3 USING ARRAYS 5.4 RETRIEVING AN ARRAY LIST 5.5 LOCAL AND GLOBAL STRINGS 5.6 ADDING UP AND SUBSTRACTING 6. OUTPUTTING INFORMATION 6.1 LOGGING 6.2 SENDING OUTPUT TO A USER 6.3 SENDING COMMANDS TO THE IRC SERVER 6.4 SENDING COMMANDS TO OTHER BOTS 6.5 CHANGING CHANNEL MODES 6.6 KICKING USERS FROM CHANNELS 6.7 WRITING YOUR OWN SCRIPT 7. RUNNING COMMANDS UNDER CERTAIN CONDITIONS 7.1 MATCHING TWO ITEMS AGAINST EACH OTHER 7.2 LOOKING IF SOMETHING IS TRUE OR FALSE 7.3 MATCHING MORE THAN TWO ITEMS 7.4 EXECUTING WHEN IT'S SOMETHING ELSE 7.5 EXECUTING WHEN IT'S NONE OF THE ABOVE 7.6 WRITING YOUR OWN SCRIPT 8. WORKING WITH LISTS 8.1 WHAT LISTS ARE 8.2 CREATING LISTS 8.3 RETRIEVING AN OBJECT FROM A LIST 8.4 RETRIEVING MULTIPLE OBJECTS FROM A LIST 8.5 DETERMINING HOW MANY OBJECTS A LIST HAS 8.6 REPLACING OBJECTS IN A LIST 8.7 CONVERTING STRINGS INTO LISTS 8.8 WRITING YOUR OWN SCRIPT 9. WORKING WITH STRINGS 9.1 RETRIEVING A CHARACTER FROM A STRING 9.2 RETRIEVING MULTIPLE CHARACTERS FROM A STRING 9.3 DETERMINING HOW MANY CHARACTERS A STRING HAS 9.4 MATCHTING STRINGS AGAINST EACH OTHER 9.5 CHANGING THE STRING CASE 9.6 CONVERTING LISTS INTO STRINGS 10. LOOPING 10.1 LOOPING UNDER CONDITIONS 10.2 LOOPING FOR EVERY VALUE 10.3 LOOPING FOR A NUMBER OF TIMES 10.4 WRITING YOUR OWN SCRIPT 11. WORKING WITH FILES 11.1 OPENING FILES 11.2 CLOSING FILES 11.3 READING FROM FILES 11.4 WRITING TO FILES 11.5 THE END OF THE FILE 11.6 CHECKING IF A FILE EXISTS 11.7 WRITING YOUR OWN SCRIPT 12. MISCELLANEOUS 12.1 TIMERS 12.2 RANDOM NUMBERS 12.3 CHECKING FOR STRINGS 12.4 ADVANCED MATH 12.5 TEXT DECORATION 12.6 CHECKING USER FLAGS 12.7 CATCHING ERRORS A. IRC SERVER COMMANDS A.1 MESSAGES AND NOTICES A.2 CTCP'S A.3 INVITING A.4 JOINING CHANNELS A.5 PARTING CHANNELS ---------------------------------------------------------------------------------------------------------------- 1. INTRODUCTION This is the Guide to TCL scripting for Eggdrop 1.6. It is made to help people get underway in making TCL scripts for Eggdrop bots. Examples will be used to explain commands and I will try to keep everything as simple as possible. When you finish reading this guide you will know in general how to make various kinds of scripts. You will (hopefully :p) understand the descriptions in tcl-commands.doc and the TCL man pages than aswell. I advice you to look at tcl-commands.doc after you've finished reading this Guide, because if you understand everything that is discussed in this Guide and know most of the commands and binds in tcl-commands.doc aswell you'll be an expert in TCL scripting for Eggdrops. I started making scripts for Eggdrop 1.3.27 and I always write my scripts for the newest (STABLE) version of Eggdrop which is at the moment the 1.6 series. I can't guarantee that all the commands discussed here will work on earlier versions of Eggdrop, but I am reasonable sure that they will work on most versions of Eggdrop 1.3 and 1.4 aswell with sometimes a few minor adjustments. Whether or not all of these will still work on future versions like 1.7.x or 1.8.x depends on how much the Eggdrop developers decide to change in the TCL section. I have always worked with TCL 8.x. I don't know if all the TCL commands are also valid in older and newer versions. At the moment the standard is 8.0.3 on most machines, so I don't think that you have to worry about that and I think that the commands will work on newer versions aswell. If the commands don't work on other versions you either have to find an other way to let your script do what you want it to do or ask the system administrator to upgrade (or downgrade) the TCL version to 8.0.3. 1.1 CONTRIBUTORS This guide would not have been possible without all the information and help contributed by other people, namely the eggdrop developers (currently Eggheads), the people from the channel #eggdrop on IRCnet and your reactions. My special thanks go out to: - Robey Pointer for creating Eggdrop. - Eggheads for taking over from Robey Pointer and keeping it such a great program. - NML_375 from #eggdrop @ IRCnet for all his comment and help. 1.2 ACKNOWLEDGMENTS Eggdrop is copyrighted by Robey Pointer. All changes made by Eggheads from version 1.3.28 and up are copyrighted by Eggheads. TCL is copyrighted by the Regents of the University of California, Sun Microsystems, Inc., Scriptics Corporation, and other parties. Eggdrop and TCL are not affiliated with this TCL Guide or myself. 1.3 REVISION HISTORY *1.10:* General: Converting the TCL Guide to PHP required it to be torn apart and reconstructed paragraph by paragraph so I took the occasion to go through the whole Guide again and corrected a few typo's here and there, the only general specific change is that now all the syntaxes of commands are underlined. Chapter 9.6: Well I completely screwed up with the description on the [join] command. It said that had to be a string and after saying that open and close braces would be removed I gave an example in which I didn't exclude them from the result at all. Sorry for this :(. *1.09:* Appendix A-4 and A-5: added a note that the channel records of Eggdrop itself also have to be changed when manually joining and parting your bot (Glimpse) *1.08:* Chapter 4.3: explained what "args" as parameter does Chapter 12.7: added a section about the [catch] command (stdarg) *1.05:* Text version: a printable/downloadable text version of the Guide has been made (multiple) Chapter 4.3: a note has been added on the difference between "arg" and "args" (stdarg) Chapter 5.6: a note has been added stating that the string used with incr has to be a valid numeric number (stdarg) Chapter 12.1: an example has been added has been added regarding using the [list] command in timers (stdarg) *1.00:* The Guide has gone final. There are proberbly still various things that aren't entirely correct, typo's and things like that, but all the chapters have been finished. Please email (marijn@suninet.nl) me any comments, suggestions, requests to add something or anything else regarding this Guide. 1.4 NEW VERSIONS OF THIS GUIDE You can always find the most recent version of this guide at http://www.suninet.nl/. If you make a translation of this document into another language, please let me know and I'll include a reference to it here. 1.5 FEEDBACK Feedback is appreciated a lot. Comments, suggestions, found mistakes, anything is always welcome. Please email (marijn@suninet.nl) them to me and I will see what I can do. 1.6 DISTRIBUTION POLICY Copyright (c) 2000-2003 by Marijn van Zon. This document may be distributed under the terms set forth in version 2.0 (12 January 1998) of the LDP license at http://www.suninet.nl/COPYRIGHT.html. This guide is free documentation; you can redistribute it non-commercially and/or modify it under the terms of the LDP license. This document is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. See the LDP license for more details. Any commercial redistribution of this document in any form whatsoever without the written authorization of the author is not allowed. Note that although this document is distributed under the term of version 2.0 of the LDP licensed this guide is not listed on the LDP, nor is it affiliated in any (other) way with the LDP. ---------------------------------------------------------------------------------------------------------------- 2. IRC AND EGGDROPS You proberbly already know what IRC and Eggdrops are and what you can do with them, but to help newbies I will give some basic information about them before we start creating real scripts. 2.1 IRC IRC stands for [Internet Relay Chat]. An IRC network is made of multiple servers connected to each other. On IRC you have channels, how many depends on which network you are. Most networks allow you to create your own channels so you can be with a group of friends in your own channel, without outsiders. You could compare IRC with a hotel. You can go visit people in their rooms or get your own room. 2.2 THE IRC PROTOCOL (RFC1459) You are proberbly using a program like mIRC (http://www.mirc.com/) in Windows, X-Chat (http://xchat.org/) in X11 or BitchX (http://www.bitchx.com/) in Linux to connect to an IRC server, but what exactly does that program do? These programs send special commands to the IRC server to make sure that what you type arrives at the correct destination. If you are going to write TCL scripts which have to send information to someone through a message, notice, ctcp or something else you can not simply do something like [notice ]. You would have to send the specific command to the IRC server just like mIRC, X-Chat and BitchX do, so you will also need to know a bit about how an IRC server interacts with IRC clients if you want to accomplish your goals. While you are reading this guide you will learn what the most commenly used commands are and how to use them. They aren't very difficult so don't worry about that or what they exactly are. If you are familiar with RFC protocols and understand what they say, you can do a search on RFC1459.txt and read it to learn more about the IRC protocol. Appendix A (http://www.suninet.nl/guide?chap=13&pg=-1) also discusses various IRC server commands. 2.3 BOTS The word bot comes from robot. Bots are programs which connect to an IRC server and perform commands you tell them to do. Most bots are very configurable and can be expanded with scripts. The main purpose of bots are to guard channels. On IRC servers without ChanServ a channel is removed when nobody is on them. When you have a bot which is permanently online you can put it in the channel to prevent it from being removed. Bots can also store information about people who are not allowed in the channel and kick/ban them when they join. This way you don't have to do everything yourself and also have a channel that can't be taken by someone else just because he/she joined while nobody was around. 2.4 EGGDROP Eggdrop is a bot written in C. It is highly configurable and can be easily expandeded with TCL scripts. You can change almost everything with TCL scripts. Eggdrop comes with its source. This lets C programmers fix bugs and change anything they want on a more deeper level aswell and also make modules for it written in C aswell. Eggdrop keeps a user database in which users are identified by their hostname and a password they set themselves. You can link mutiple eggdrops together to make them share user databases and send commands to each other so they perform commands like opping each other. With the right scripts, there is nothing Eggdrop can't do. ---------------------------------------------------------------------------------------------------------------- 3. THE TCL LANGUAGE The TCL script language isn't something from Eggdrop specifically. It's a standalone language used by Eggdrop. To help you understand things better later on, this will be a chapter about what TCL exactly is and what it does. 3.1 OPEN SOURCE TCL is an open source language. This means that your program is run by sending the source code (the script itself) of it to the TCL program which processes it, without compiling it unlike languages like C. This means that people can always look at your programs source code and see how it is written, because of this you will have less problems with incompatibility on different computers because the TCL language is always the same. 3.2 THE BUILD-UP OF A TCL SCRIPT The commands of a TCL script can roughly be divided into two parts. The first part is executed when you run the script and the other part is executed when you call upon it. Commands which are executed when the script starts are outside a procedure (I'll explain later on how you can define procedures). In a TCL script are (in general) multiple procedures. The commands within these procedures are only executed when the procedure is triggered. A procedure can be triggered in two ways, either by calling upon it yourself or when an event is triggered that's bound to the procedure. 3.3 INTERPRETATIONS Like all programming languages you have to place certain events between special characters to let the language know what to do with your event. TCL uses the following characters to define events: <> - the greater than and is less than signs These two signs aren't used in TCL itself, but it is used in documentation to indicate that a parameter is required if you want to execute the command. "" - quotes Quotes are used to mark text. Everything between quotes is considered to be text, except when it is enclosed in brackets or is marked as a variable (you will learn about variables later on). [ ] - brackets Brackets are used to execute commands. The first word inside the brackets is the command that you want to execute and the rest are the parameters of the command. They are also used in documentation to indicate that a parameter is optional and not required to run the specified command. { } - braces Braces are used to indicate where something starts and stops. This can be a part of the script or a command for example. ( ) - parentheses Parantheses are used to define multiple things. It is used to declare that a variable is an array and that certain parts of the [if] command belong together for example (how this works and what it is will be explained later on). $ - variable This defines that the word directly following the $ (without seperation of a space) is a variable. Shortly said a variable is a place where you store information in to call upon it later. This will be discussed more extensive later on aswell like I said above. ; - semicolon When TCL sees a semicolon, it processes everything behind it as if it were on a new line. This way you can put more than one command on a single line to make your script shorter and still let it work. If you don't use a semicolon and put a second command behind your first one, TCL will see it as extra comment for the command in front of it and will give (most likely) an error because the command isn't supposed to have such a parameter or it makes your script malfunction. # - hash When TCL sees a hash at the beginning of a line, it considers everything behind it as comment and will ignore it and skip to the next line. Many programming languages allow you to say where the comment starts and stops, like in HTML you define the start of comment with or like in C where you let the comment start with /* and let it stop with */. This doesn't apply for TCL. When you have put a hash at the beginning of a line, everything behind it (on that line) is ignored. Please note that you can not put a hash in the middle of a line and put comment behind it, this will give an error because TCL doesn't see it as comment in that case. Instead, you can put a semicolon in front of your hash so that TCL will interpret the things behind it as they were on a new line and then it will see it start with a hash and thus will consider everything behind it as comment. It's a bit complicated, but that's just the way it is and I don't think that it will ever change, you'll get used to it ;). Once you realize how it works it's not that difficult. \ - backslash A backslash is used to let TCL see the next character as text. Normally when you would put a bracket in your script TCL would see it as the start or end of a command, but when you put a backslash in front of it, TCL will see it as plain text and process the bracket instead of trying to execute a command. There are several exceptions to this though. There are a few codes that start with a backslash, followed by a number or letter to create a special character. In those cases the backslash means that there is a special code and not to see the next character as text. For example: \0 (that's the number 0, not the letter o) normally would make TCL see the 0 as text (TCL sees an 0 in general as text already, but the backslash enforces it and this is mearly an example to explain how it works), but \037 doesn't mean 037 with the 0 enforced as text, but it means that the following text must be underlined. This might seem a bit confusing aswell but once you get a grasp on how it works it's not so hard anymore like is with most things in TCL. For the rest all the commands and small examples within the text will be shown in [italic], so that they can easily be distinguished from comment and other text. Bigger examples will be shown in a different -----START----- font ------END------ and on seperate lines. 3.4 DIFFERENT TCL AND EGGDROP COMMANDS Because TCL is not just something from Eggdrop, there are two types of commands. Commands of TCL itself that will work on scripts outside Eggdrop aswell and commands that have been added by Eggdrop. All the commands that Eggdrop adds to the TCL language are listed in tcl-commands.doc, which can be found in the doc/ directory of Eggdrop. You can get information about a TCL command from the manpages. You can access the manpages by typing [man n ] on a *nix computer which has the TCL manpages installed or browse through the HTML version of the manpages at http://dev.scriptics.com/man/tcl8.0/TclCmd/contents.htm. ---------------------------------------------------------------------------------------------------------------- 4. BINDING EVENTS AND CREATING PROCEDURES In this chapter you will learn what I consider the two most important commands in TCL, [bind] and [proc]. These two commands allow you to make the bot perform the actions you tell it do to when a certain event is triggered. 4.1 BINDING EVENTS With the [bind] command you can make the bot react on an event like a command, a message or a ctcp. *The syntax of the [bind] command is [bind ]*. I'll go through this now step-by-step. The is what event you want to make the bot react on. For example, [ctcp] would bind a ctcp or [msg] would bind a message. You can find all the available types in eggdrop/doc/tcl-commands.doc. The refer to the flags you give users in your Eggdrop. This goes in the form of |. Here are a few examples to explain it faster and easier: [code] #Bind to everyone (including users who are not in the userlist) bind - #Bind to everyone (including users who are not in the userlist) bind * #Bind to global owners bind n #Bind to global operators bind o #Bind to channel masters (NOT global masters) bind -|m #Bind to global masters and channel masters bind m|m #Bind to global operators and channel owners bind o|n [/code] The is on what parameter the bind is triggered. If you would use the bind type [msg], the parameter would be the first word of the message or if you were to use the bind type [dcc] it would be the new partyline command you want to create. For example, [bind dcc test ] would create a partyline command called [test] or [bind msg hello ] would make the bot react when it receives a message with "hello" as its first word. The is simply what procedure the bot must run when the bind is triggered. 4.2 CALLING UPON PROCEDURES A procedure is nothing more than a set of commands that you can call upon anywhere in your TCL script. You call upon a procedure by placing somewhere in your script [ [parameters]] (i.e. in an other procedure, somewhere between brackets or just in your main script). These procedures perform a set of commands which, for example, can return a value after checking a few factors or can send something to the IRC server immediantly. 4.3 CREATING A PROCEDURE With the [proc] command you can create procedures. *The syntax of the [proc] command is [proc { } { }]*. The is how you want to call the procedure you are creating. This can be anything you want and you will have to use this name when you want to call upon it. In this guide I will start every procedures name with [:]. This isn't required, but it's (in my opinion) handy to do so, because this way you immediantly know what a proc is for which is especially handy when you are working with large scripts. The are the variables the procedure most put its received parameters in. You need to specify a variable for each parameter here that will be sent to the procedure. All the variables you give have to be seperated by spaces. For example, if you would have the line [proc test { nick channel } { }] and somewhere else in your script [test MyNick #test], the procedure would put "MyNick" in the variable "$nick" and "#test" in the variable "$channel". The procedure also always wants to know the exact amount of parameters it is given. If you give the procedure 4 variables for example and you call upon it with 5 parameters, Eggdrop will give an error similar to 'proc called with too many arguments' or if you call upon it with 3 parameters and have given it 4 variables, it will give an error similar to "no argument given for ...". There is one exception to this rule though. If you call the last variable of "args" than you may call upon the procedure with more parameters than you have defined in . In this case all of the parameters you give to the procedure up from the point where the argument "args" start is put in $args. They are put into $args as if the [list] command was used to make $args. You'll learn what the [list] command is later on, but you should already know about the difference between "args" and any other name. For example if you would call upon [proc test { nick channel args } { }] with [test $nick $channel $handle $host], it would put the passed on $nick in $nick, $channel in $channel and both $handle and $host in $args, but I strongly discourage using the "args" parameter until you understand what lists are and what the [list] command does. The is basicly the commands that you want the procedure to execute. The body doesn't have to be on one line, that's why the proc starts with an open-brace. You can put a new command on each new line and it'll still be part of the procedure until you close the body up with a close-brace. Besides calling upon procedures yourself, you will most likely want to use [bind] aswell. The [bind] command runs a procedure when a certain event is triggered. Besides information about the bind, tcl-commands.doc also gives you information about with what parameters the procedure will be called upon when the bind is triggered and what information the parameters will contain. Lets take a look at the [bind msg] explanation from tcl-commands.doc: [code] (1) MSG bind msg procname [/code] Everything behind [procname] are the parameters that will be sent to the procedure. Most procedures for [bind msg] will thus look like [proc msg:test { nick host hand arg } { }]. 4.4 ENDING A PROCEDURE Next to of course just ending the body of your procedure with a close-brace, you can use the command [return] to let the procedure end anywhere within the body. How the procedure will end depends on how you use the return command. *The basic syntax of the [return] command is [return .]* The is what the procedure must output. In general you end the proc with either 0 or 1 as . In most cases using 0 as will result in a quiet return where the bot goes it's normal way as it would do if the bind didn't exist, meaning that it won't log anything and won't override built-in features like flood protection for example either. When you are returning with 1 the bot usually either logs the command that has been performed by i.e. a [bind dcc] or it doesn't react itself on the event (when you are using [bind flud] for example, returning 1 will make the bot not react on the flood but let the TCL script handle it entirely). You can find more in tcl-commands.doc about how the different binds react on which return value. Besides 0 or 1 you can also have the procedure return text if you want. That would make the procedure roughly said an interactive variable. You would still have to call upon it like [proc [parameters]], but it would return information like a variable would. For example, [proc test { nick } { return "Hello $nick." }] would be the same as [set test "Hello $nick."], only you would call upon them differently and you could let the procedure check things and possibly output something different than the input, but still having the output like it's a variable. ---------------------------------------------------------------------------------------------------------------- 5. VARIABLES You've already come across the term variable a few times in this Guide, but in this chapter you'll learn what they exactly are. 5.1 WHAT VARIABLES ARE Variables are used in almost every TCL script. I haven't seen a TCL script yet that didn't use them. A variable is simply said something in which you can store information. This information can be anything and is dynamic. It can be different each time you run your procedure or script. In many programming languages there are different types of variables. You would have to declare each variable and also if it is a number or if it contains letters. This doesn't apply for TCL. In TCL every variable is a so called string, meaning that it doesn't matter if it contains letter or numbers, it can be one of those or both. This limits the things you can do with it a bit but it also makes the language a whole lot easier. From now on I'll also refer to a variable as a string. 5.2 SETTING STRINGS Strings can be set and unset with the [set] and [unset] commands. *The syntax of a set command is [set ] and* *of the unset command it's [unset ].* Besides [set] and [unset] there is also [append]. This command works in the same way as the [set] command, but the difference is that this command adds something to the string instead of changing it. The [append] command adds something directly at the end of a string and is equal to [set "$"]. The is the name of the string you want to change. The name must consist of letters, marks (-) and/or numbers only. If you put any other characters in it it might cause errors because the script inteprets them wrong. The is what you want to set the string to or append to the string if you are using [append]. This can be a number, some text, the output of a command or anything else for that matter. What you want to put in your string is not limited to anything, but some characters do need to be escaped by a backslash for instance to make sure TCL doesn't interpret them wrong. Also note that if you want to put a piece of text in a string that you enclose it in quotes. Besides setting a string, you can also unset a string. This simply makes the string non-existend again as it was before you created it. *NOTE:* TCL will return an error when you try use a string that doesn't exist (a string can exist with nothing in it ([set test ""]) though). 5.3 USING ARRAYS Besides normal strings, there are also arrays. An array is a group of strings brought together "under one roof". You can use these arrays in exactly the same way as you can use normal strings and more. The biggest advantage of an array over a normal string is that because they are all under the same roof they can also be processed all at once with some commands, making it easier for you because you don't have to check all your strings one by one since the entire array is already loaded. The syntax of an array is [$array(string)], where [array] is the name of the array and [string] is the name of the string within the array. Once you have created an array you can't create a string with the same name. For example, if you have $test(), you won't be able to have $test aswell. Here are a few examples: [code] #Put the word "that" in the string [what] of the array [test] set test(what) "that" #Put the word "now" in the string [when] of the array [test] set test(when) "now" #The following will produce an error since there already is an array called test! set test "testing" #Put the word "what" in the string [test_what] set test_what "what" #Put the word "now" in the string [test_when] set test_when "now" [/code] 5.4 RETRIEVING AN ARRAY LIST After you've made an array you might want to get a list of all the strings that are in the array, for example getting a list of the array $test() from the previous example. The command used for this is [array names]. *The syntax of an [array names] command is [array names ].* In this case is the name of the array you want to get the names from like "test" in the example. Now if we were to put [array names test] in the previous example, it would result in getting "what" and "when" returned. Right now you proberbly don't have any use for this, but later on it might come in usefull. 5.5 LOCAL AND GLOBAL STRINGS There are two types of strings. Local ones and global ones. A local string only exists while a procedure is running and a global string exists as long as your script is running so in this case while your bot is running. All the strings created outside a procedure are automaticly global strings and all the strings created within a procedure are automaticly local strings. While you are in a procedure you can turn a local string into a global string by using the [global] command. Global and local strings work in exactly the same way, the only difference is when and where they exist. *The syntax of a global command is [global ].* Every string must be seperated by a space and if you want to have a global array, you only have to give the array name, not the string of the array too. Also because this command already knows that what you are giving as input is a string, you musn't put a $ in front of the names of your strings. A $ is used to identify something as being a string and replace that part with the contents of the string. Basically putting a $ in front of it will replace the whole name of the string including the $ with the contents in that string. For example if $test contained "hello" and you were to do [global $test], TCL would make of this [global hello] and that's not what we want, in this case, now do we. If we would take the [set] example where we have the array [test] with the strings "(when)" and "(what)" in it, you also don't have to use the global command like [global test(when) test(what)], but you can use [global test] which would give you access to the global strings "(what)" and "(when)" of the array [test] aswell. The global command converts entire arrays, not just individual strings. A final note: the [global] command is usually put on the first line of the procedure. This is not neccesary, but it's more like an unwritten rule. It's also easier to overview your script if the [global] is on the first line rather than somewhere hidden deep inside the procedure. 5.6 ADDING UP AND SUBSTRACTING With the [incr] command you can easily add up and substract numbers from strings. *The syntax of an [incr] command is [incr [+/-][number]].* Here are a few simple examples to explain the command fast and easy: [code] #Add 1 up to the string [test]. incr test #Add 2 up to the string [test]. incr test 2 #Add 3 up to the string [test] (the + sign is not required for addition). incr test +3 #Substract 4 from the string [test] (the - sign is, logically, required for substraction). incr test -4 [/code] With the [incr] command you can only add up and substract. Dividing, multiplying and things like that have to be done with the [expr] command, but that's something for later on. Note that the string you are inputting also has to be a valid numeric number or the [incr] command will return an error. ---------------------------------------------------------------------------------------------------------------- 6. OUTPUTTING INFORMATION In this chapter you will learn how to output information. There are a lot of commands with which you can output information. Some of them are used to send the information to different places and others are used to output the information just in a different way. 6.1 LOGGING There are four commands with which you can make Eggdrop log something. These commands are [putlog], [putcmdlog], [putxferlog] and [putloglev]. The difference between these commands is that each command sends the log message to a different log level (the various log levels are described in the Eggdrop configfile). Here is a list of the log levels according to eggdrop.complete.conf of Eggdrop 1.6.8: [code] # Events are logged by certain categories. This way, you can specify # exactly what kind of events you want sent to various logfiles. # # The most common log file flags are: # m private msgs/ctcps to the bot # k kicks, bans, mode changes on the channel # j joins, parts, netsplits on the channel # p public chatter on the channel # s server connects/disconnects/notices # b information about bot linking and userfile sharing # c commands people use (via msg or dcc) # x file transfers and file-area commands # r (if use-console-r enabled) EVERYTHING sent to the bot by the server # o other: misc info, errors -- IMPORTANT STUFF # w wallops: msgs between IRCops (be sure to set the bot +w in init-server) # # There are others, but you probably shouldn't log them, it'd be rather # unethical. ;) There are also eight user-defined levels (1-8) which # are used by Tcl scripts. [/code] The [putlog] command sends the log message to level o, the [putcmdlog] command sends the log message to level c, the [putxferlog] command sends the log message to level x and the [putloglev] command sends the log message to the level you specify. *The syntax of a [putlog], [putcmdlog] or [putxferlog] command is [putlog ""].* *The syntax of a [putloglev] command is [putloglev ""].* The is to which log level(s) you want to send the log message. You can use * to send the log message to all the log levels. The is to the log file of which channel you want to send the log message. Use * to send the log message to the general log. The is the message you want to log. The default console modes make masters and owners see all messages sent to the log level o in the partyline. Because of that [putlog] is the mostly used log command and you can be pretty sure that the output of it is shown to masters and owners in the partyline which is good because this way you can make your script send out notices of events throughout the partyline for masters and owners. You can also use this for debugging (finding out where something went wrong). If you place "waypoints" in your script by putting a [putlog] command every couple of lines in your script that sends a message like "Waypoint X" to the log you can easily see in the partyline where your script goes wrong when it doesn't do something the way you want it do it. Without these "waypoints" it can take a lot longer before you pinpoint the location of the fault. 6.2 SENDING OUTPUT TO A USER You can send output to a user on the partyline with the command [putdcc]. *The standard syntax of a [putdcc] command is [putdcc ""].* The is to which socket the bot must send the output to. Every in- and outgoing connection of your bot has a so-called socket number. Such a connection can be the connection to the IRC server, a linked bot or a user who is on the partyline. You can see the socket numbers of your bot that are currently in use with the [dccstat] command in the partyline. Some binds, like [bind dcc], send the socket number of the user who executed the command to the procedure after which you can use that socket number to send information directly to that user alone with the [putdcc] command. The is what you want as output. For example, [putdcc $idx "Hello"] would send the text "Hello" to the socket number that is in $idx. 6.3 SENDING COMMANDS TO THE IRC SERVER There are three commands with which you can send commands to the IRC server. These commands are [putserv], [puthelp] and [putquick]. All of these commands do exactly the same thing, but they use different queues. The [putquick] command uses the fastest queue and is used if you want something to be sent to the server immediantly. The [putserv] command uses the normal queue for server messages and is also the command to use when you are sending something to the server that doesn't need to be rushed. The [puthelp] command uses the slowest queues and is used to send things like messages and notices to other people, because that usually has a lower priority. The syntax a [putquick], [putserv] or [puthelp] command is [putserv [options]]. The is the command and parameters you want to send to the IRC server. If you want to send a message to someone or a channel, the syntax would be ["PRIVMSG :"] or if you want to send a notice the syntax would be ["NOTICE :"], where is the nickname or channel to which you want to send the message and is the message that you want to send. For example [puthelp "PRIVMSG foo :Hi there."] would send the message "Hi there." to the nickname "foo". The [options] are the parameters for the command. At the moment there is only one parameter and that's -next. This will push your command to the front of the queue that the command uses and thereby sending it faster. More information about IRC commands can be found in Appendix A (http://www.suninet.nl/tclguide/index.php?chap=13). 6.4 SENDING COMMANDS TO OTHER BOTS There are two commands with which you can send commands to other bots. These are [putbot] and [putallbots]. As you might have guessed, [putbot] will send the command to a specific bot and [putallbots] will send the command to all the bots that are currently linked. *The syntax of a [putbot] command is [putbot ] and* *the syntax of a [putallbots] command is [putallbots ].* The is the botnet-nick (usually the same as the normal nick) of the bot to which you want to send the command to. The is the command you want to send and the parameters with it. The first word is the actual command you want to send and what [bind bot] will react on and the rest is extra information or parameters that you want to send with it which are placed in the last parameter of the procedure. Note that here, like with the set command, you have to enclose between quotes. 6.5 CHANGING CHANNEL MODES There are two ways to change channel modes. These are doing it manually with a [putserv] command or using the [pushmode] command which is meant for it and is also faster. The [pushmode] command bulks up all the mode changes until the script or procedure has ended and than tries to send as many modes at once to the IRC server (if you've done 3x +o, the bot will make it 1x +ooo if your IRC server allows it). *The syntax of a [pushmode] command is [pushmode [parameters]].* The is which channel the mode change must affect. The is the mode change you want. This can be something for the channel itself (like +i or -m) or it can be something towards a user on the channel (like +v or -o). The [parameters] are the parameters for the mode change. If you are setting or unsetting a key on the channel for example this would be the key you want to place on or remove from the channel or if you are opping somebody this would be the nickname you want to op. *NOTE:* You can only perform ONE mode change per [pushmode] command. So using -ooo as would not work. The bot will do this for you after the script or procedure ends. 6.6 KICKING USERS FROM CHANNELS There are two ways to kick somebody off the channel. These are doing it manually with a [putserv] command or by using the [putkick] command which is meant for it. *The syntax of a [putkick] command is [putkick [reason]].* The is from which channel you want to kick the nickname(s). The are the nickname(s) you want to kick. This can be either one nickname or multiple nicknames seperated by comma's. How many people you can kick at once depends on your IRC server. The [reason] is the reason you want the bot to display when the nickname(s) are being kicked. 6.7 WRITING YOUR OWN SCRIPT Now it's time to see if you understood it all and are able to use the commands. Try to write a script that voices everyone who joins a channel and than says "Welcome !" in the channel. Good luck. You can find a small example of such a script here: http://www.suninet.nl/tclguide/example-ch6.tcl. It's not very complicated and it's not the only way to write a script like that, but if you get stuck you can always look at it to see how it can be done. ---------------------------------------------------------------------------------------------------------------- 7. RUNNING COMMANDS UNDER CERTAIN CONDITIONS In this chapter you will learn how to run a set of commands only under certain conditions. This is also one of the most important things, there is almost no script, or program for that matter, that doesn't use this. With this you can verify user input for instance to see if it was valid or ask what should be done. 7.1 MATCHING TWO ITEMS AGAINST EACH OTHER With the [if] command you can make the script perform a set of commands only when something you specify is equal or not to something else. This can be a command that has to be performed, two strings, practically anything. I don't know how I can explain this very well in words, but it's a simple command once you understand it so I'll just tell you how it works and give some examples to explain it later on. *The syntax of an [if] command is [if { } { }].* The two 's are what the [if] command must compare to each other. This can be two strings, a command (don't forgot to enclose it with brackets like was told in Chapter 3.3 (http://www.suninet.nl/tclguide/index.php?chap=3&pg=3)) or some text (enclosed in quotes like also told in Chapter 3.3 (http://www.suninet.nl/tclguide/index.php?chap=3&pg=3)) or anything else that can be used to match something against it. The tells the [if] command how to compare the two actions with each other. This can be either two things, namely == for 'is equal to' or != for 'is not equal to', when one of the 's is not a number. If both 's are a number you can also use the 'is greater than' (>) and the 'is less than' (<) signs to see whether something is larger than the other (you can for example use as 's two commands that calculate how many characters there are in a string which both result in a number). Last but not least, by putting an 'is' (=) sign after the 'is greater than' or 'is less than' sign you can change 'is greater/less than' into 'is greater/less than OR is the same amount as'. The is basicly the commands you want the [if] command to execute. The body doesn't have to be on one line, that's why the [if] command starts with an open-brace. You can put a new command on each new line and it'll still be part of the [if] command until you close the body up with a close-brace just like with a procedure. 7.2 LOOKING IF SOMETHING IS TRUE OR FALSE Sometimes you might want to only check if something is true or false. In computer programs 0 always stands for false and 1 for true. You can do [if {$one == 1} { }], but there also is an other way to do the same thing. To see if something is true you can also simply do [if {$one} { }] and instead of [if {$one == 0} { }] you can do [if {!$one} { }]. You can replace the string with a command or anything else. Inputting only one in the [if] command without a makes it check if it is true. By adding the ! in front of the without giving a or a second it checks if it is false. Note that in this case 'true' equals any number above 0, not just 1. 7.3 MATCHING MORE THAN TWO ITEMS Besides matching only one or two items you can also set multiple conditions. After the second action you can additionally add either && for 'and' or || for 'or' and put in another check. After that you can do the same thing again and again and so on, but this doesn't mean it will work yet when you are using both &&'s and ||'s. To prevent the [if] command from making a mistake when you use ||'s and &&'s in the same [if] command, you can put the parts you want the && and || to react on between parentheses. I don't know a better way to say this, so I'll just give an example. When you have [if {$test(start) == $test(stop) && $test(when) != "" || $test(what) != ""} { }] and you want the [if] command to work only when [ $test(start) == $test(stop) ] and [ $test(when) != "" || $test(what) != "" ], this proberbly wouldn't work. The [if] command would proberbly see this as [ $test(start) == $test(stop) && $test(when) != "" ] or [ $test(what) != "" ]. This is fixed by putting the last part between parentheses which would make the command look like: [if {$test(start) == $test(stop) && ($test(when) != "" || $test(what) != "")} { }]. Experimenting with this is the best way to figure out how it exactly works. 7.4 EXECUTING WHEN IT'S SOMETHING ELSE It is also possible to put a second [if] command after your first [if] command. This can be used for instance when you first want to check string A and if it doesn't comply with your conditions you want it to check string B. The way to do this is by putting [elseif] after your closing brace. What comes after [elseif] works in exactly the same way as the normal [if] command. A small example: [code] if {$nick == "SomeNick"} { } elseif {$chan == "#eggdrop"} { } [/code] In this case if $nick equals "SomeNick" will be executed. The commands in will be ignored even if $chan equals "#eggdrop". However, if $nick would not equal "SomeNick", but $chan would equal "#eggdrop" than will be executed. If neither of the strings match nothing will be executed and the script will move on. You can put in as many [elseif] commands as you like. After the first one a second one can be placed and so on. 7.5 EXECUTING WHEN IT'S NONE OF THE ABOVE Besides [elseif] you can also use [else]. The body within [else] is executed when all of your [if] and [elseif] checks fail. You can use [else] without having [elseif] in your [if] command. Here are two small examples: [code] if {$nick=="SomeNick"} { (...) } elseif {$chan=="#eggdrop"} { (...) } elseif {$host != "*!eggdrop@eggheads.org"} { (...) } else { (...) } if {$enabled} { (...) } else { (...) } [/code] Unlike [elseif], you can use [else] only once per [if] command. 7.6 WRITING YOUR OWN SCRIPT Now lets expand the small script you made in the previous chapter a bit. Make the script work for only two channels which you can set at the beginning of the script in arrays. If the channel matches one of those two let the bot auto-voice the user and if it doesn't than only send the welcome message to the channel. If you want an example you can look here: http://www.suninet.nl/tclguide/example-ch7.tcl. ---------------------------------------------------------------------------------------------------------------- 8. WORKING WITH LISTS In this chapter you will learn what lists are and how to break them apart. 8.1 WHAT LISTS ARE A list is simply said a set of objects, unlike a string which is a set of characters. The object on itself can be a string again or another list with a new set of objects. These objects are usually seperated by spaces, but they can contain spaces themselves too. Where an object in a list starts and stops can be defined with open and close braces. The commands that will be discussed in the paragraphs 8.3, 8.4, 8.5 and 8.6 treat what they receive as input as lists. This means that if you input something into it that has braces in it, it will see this as the start and end of an object within the list, so be carefull with these commands when you are processing nicknames for example. It will also add braces if required, like for instance when you have brackets in what you input into the command, in that case braces are added to mark the start and end of an object. A small example: If you were to have the list "test {foo bar} temp", than the first object of this list would be "test", the second "foo bar" (not "{foo" and "bar}", because the braces mark the start and end of the object) and the third "temp". Like said above, an object can also contain a new list with new objects. This might seem strange but I'll give an example. If you were to have the list "test {foo {foo bar}} temp" than "foo {foo bar}" would be the second object. If you were to break that apart again than the first object would be "foo" and the second "foo bar" and so on. Just think of it like having a large box, with a smaller box inside that one and an even smaller box inside that one again. Pretty much the principle of a Russian Matryoshka doll if you know what that is :-). Lists can be contained within strings, but they can also be the output of a command. The most important thing is to watch out with the commands you're using and know whether they are processing what you put into them as a list or a string and whether they output it as a list or a string, because if they process it as a list you will have to make sure the objects in it are the way you want them and not have unforseen braces, brackets or spaces that mess the whole thing up. 8.2 CREATING LISTS You can create lists with the [list] command. *The syntax of a [list] command is [list [object(s)]].* The [object(s)] are the objects that you want in the list. You must seperate each object with a space and you can define specifically where an object starts and stops with braces like said above like said in the previous paragraph. The [list] command outputs a list compiled of the objects that it was given. For example [list "test" {foo bar} "temp"] would return a list in which the first object is "test", the second "foo bar" and the third "temp". 8.3 RETRIEVING AN OBJECT FROM A LIST Gettings object from lists is fairly easy. The command for this is [lindex]. *The syntax of a [lindex] command is [lindex ].* The is the list from where the object must be retrieved. This can be anything from a string containing a list ($test can contain a list for example by putting the output of a [list] command in it with [set]) to a command, just as long as it's a valid list and not a plain string. The is the number of the object you want to retrieve. Each object in the list has its own number, starting at 0. The first object is 0, the second 1, the third 2, and so on. It's a bit confusing, but if you're used to programming this will seem familiar. A small example: [lindex $test 5] returns the 6th object from the list that is in $test. The [lindex] command outputs a string containing only the object itself without the open and close braces that mark the start and stop of the object if those are present in the list. 8.4 RETRIEVING MULTIPLE OBJECTS FROM A LIST When you want to get more than one object we just change the command a bit. For this we use the command [lrange]. *The syntax of a [lrange] command is [lrange ].* The is the list from which the objects must be retrieved just like with the [lindex] command. The is the number of the object from which the command should start cutting the list. The numbering of the objects works in the same way as they do in the [lindex] command. The is the number of the object at which the command should stop cutting the list. Instead of a number you can also use the word [end]. This makes the command cut from the given start object until the end of the list. A small example: [lrange $test 3 7] returns the objects 4 through 8 from the list that is in $test or [lrange $test 4 end] would return the objects 5 and up until the end of the list in $test. The [lrange] command outputs a newly compiled list of the objects you choose, leaving any open and close braces to mark the start and stop of an object which were already in the list intact and adds new ones if neccesary. 8.5 DETERMINING HOW MANY OBJECTS A LIST HAS This can be done with the [llength] command. *The syntax of a [llength] command is [llength ].* The is the list from which the objects must be retrieved just like with the [lindex] command. This command outputs a string containing a number which equals the objects the list you specified holds, starting at 1 for one object and 0 if the list is empty. 8.6 REPLACING OBJECTS IN A LIST You can also replace objects in a list. For this we use the command [lreplace]. *The syntax of a [lreplace] command is [lreplace [object(s)]].* The is the list from which the objects must be retrieved just like with the [lindex] command. The is the number of the object from where the command should start replacing objects in the list. The numbering of the objects works in the same way as they do in the [lindex] command. The is the number of the object at which the command should stop replacing objects in the list. Instead of a number you can also use the word [end]. This makes the command replace the objects from the given start object until the end of the list. The [object(s)] is with what you want the command to replace the above given objects. If you don't specify anything to replace the above objects with, they are removed from the list. Note that this also has to be a valid list on its own. A small example: [lreplace $test 3 7 "foo" "bar"] returns the list with the objects 4 through 8 replaced with the objects "foo" and "bar" or [lreplace $test 4 end] would return the list with the objects 5 and up until the end of the list removed from it. The [lreplace] command outputs a newly compiled list with the replaced objects you choose, leaving the open and close braces that mark the start and stop of an object which were already in the list intact and adds new ones if neccesary. 8.7 CONVERTING STRINGS INTO LISTS You can convert a string into a list with the [split] command. *The syntax of a [split] command is [split [character]].* The is the string you want to convert, which can be a string or a command that outputs a string. The [character] is the character that will be used for splitting the string into objects. Whenever [split] encounters the given character it will see it as a marker for the end of an object and the start of a new one. If no character is provided a space is used to split the string. The [split] command returns a newly compiled list in which the first object is everything in the up to the [character], the second object everything between the second and third [character] and so on. Any braces that are in the will be considered a part of the object and new braces or backslashes will be added to enforce that the brace is a part of the object if neccesary. A small example: [split "test {foo bar} temp"] would result in a list where the first object would be "test", the second "{foo", the third "bar}" and the fourth "temp". Also [split "test.{foo.bar}.temp" .] would result in the same thing. 8.8 WRITING YOUR OWN SCRIPT Now lets expand the auto-voice scripts again and make it a small greeting script aswell. Make it so that both channels are in the same list and that you can set a different greeting for two nicks that can support up to 2 lines, where the nick with its greeting is in the same list (this isn't very practical but it makes you use more of the commands you've learned so far). Also show the greeting on all the channels, not only the ones where the bot doesn't voice. This is already a bit harder than the previous scripts, but not too difficult yet. You can find an example here: http://www.suninet.nl/tclguide/example-ch8.tcl. Also don't forget that this is an example, your script could do the very same thing and be completely different. ---------------------------------------------------------------------------------------------------------------- 9. WORKING WITH STRINGS In this chapter you will learn how to break strings apart. 9.1 RETRIEVING A CHARACTER FROM A STRING This command works in exactly the same way as the [lindex] command, only the command is a bit different. To retrieve characters from a string we use the command [string index]. *The syntax of a [string index] command is [string index ].* The is the string from which the characters must be retrieved. This can be anything from a string as in variable to a command. The is the number of the character you want to retrieve, where 0 is the first character, 1 the second character, 2 the third character and so on like in the other commands for lists except that it now goes per character instead of per object. The [string index] command outputs a string that contains the charater you specified. If you were to input a list which contains an object that is defined with braces, the braces will also be regarded as characters in the string and not as items to designate a start and stop of an object. For example, [string index "test {foo bar} temp" 5] would return "{" and not "f" or "foo bar". 9.2 RETRIEVING MULTIPLE CHARACTERS FROM A STRING This command works in exactly the same way as the [lrange] command, only the command is a bit different. To retrieve multiple characters from a string we use the command [string range]. *The syntax of a [string range] command is [string range ].* The is the string from which the characters must be retrieved. This can be anything from a string as in variable to a command. The is the number from which character you want to start cutting, where 0 is again the first character 1 the second character and so on. The is the number up until which character you want to cut, where 0 is the first character and so on like in the above commands. Here you can use "end" aswell to designate that there must be cut until the end of the string. The [string range] command outputs a string that consists of the cut text. If you were to input a list which contains an object that is defined with braces, the braces will also be regarded as characters in the string and not as items to designate a start and top of an object. For example, [string range "test {foo bar} temp" 5 8] would return "{foo" and not "foo ". 9.3 DETERMINING HOW MANY CHARACTERS A STRING HAS This can be done with the [string length] command. *The syntax of a [string length] command is [string length ].* The is the string from which the characters must be retrieved. This can be anything from a string as in variable to a command. The [string length] command outputs a string that contains a number which equals the amount of characters the string has. If you were to input a list which contains an object that is defined with braces, the braces will also be regarded as characters in the string and not as items to designate a start and top of an object. For example, [string length "test {foo bar} temp"] would return 19 including the braces and not 17. 9.4 MATCHTING STRINGS AGAINST EACH OTHER At some points you might want to know if a certain piece of text is in a string. You can find out if it is or is not with the [string match] command. *The syntax of a [string match] command is [string match ].* The is what you want to look for in the string. Besides normal characters you can also put an asterix (*) or question mark in the pattern. An asterix means that anything is matched and a question mark only one character. For example if you have "*foo*" as [string match] would find a match if it finds "foo" anywhere in the string, so if it was looking for "*foo*" in "test foobar" it would give a match. If you were to use "?foo*" it would find a match only if the string has the characters "foo" on the second through fourth place regardless of any suffixes, so if it was looking for "?foo*" in "test foobar" it would not match but "*foobar" would match. The is the string in which [string match] will look for the . This can be anything from a variable string to a command that outputs a string. The [string match] outputs a 1 if it finds a match and 0 if it doesn't. 9.5 CHANGING THE STRING CASE A lot of things in TCL are case sensitive. For example in most cases "FOO" would not be the same as "foo", so in some cases it might be wise to first convert something to lower case or upper case alone before matching it against something. This can be done with [string tolower] and [string toupper]. *The syntax of a [string ] command is [string ].* The is to which you want to convert the case, [tolower] will convert your string to lower case and [toupper] will convert it to upper case. The is the string what you want to convert. This can be anything from a variable string to a command that outputs a string. The [string ] command outputs a string in which all the characters are converted to lower or upper case. If you were to input a list which contains an object that is defined with braces, the braces will also be regarded as characters in the string and not as items to designate a start and top of an object. For example, [string tolower "test {foo BAR} temp"] would return "test {foo bar} temp" and not "test foo bar temp". 9.6 CONVERTING LISTS INTO STRINGS You can convert a list into a string with the [join] command. *The syntax of a [join] command is [join [character]].* The is the list you want to convert into a string, which can be anything from a string containing a list or a command that outputs a list for example. The [character] is the character that will be put between the former list objects in the new string. If no character is provided a space is put between the objects. The [join] command returns a string with all the objects in the seperated by the [character]. Any braces that are in the which are used to define the start and end of a object will not be included in the new string, only the objects are transformed into a string. A small example: [join [list "test" {foo bar} "temp"]] would result in a string containing "test foo bar temp" or [join [list "test" {foo bar} "temp"] .] would result in "test.foo bar.temp" (notice that the space in "foo bar" has NOT been replaced by a dot because the [join] command joins the *objects* of a list and not the words!). ---------------------------------------------------------------------------------------------------------------- 10. LOOPING In this chapter you will learn about different kinds of loops. Under many circumstances it is very handy if you can make your script perform the same set of commands multiple times, because often you have no control over how big something is and thus how often the same set of commands must be performed. 10.1 LOOPING UNDER CONDITIONS The most basic loop is the [while] loop. This will keep going until a set of given actions match each other. The [while] command also looks a lot like the [if] command. *The syntax of a [while] command is [while { } { }].* The works in exactly the same way as the [if] command. It's possible to simply replace "while" with "if" and make the script execute the set of commands only once without running into any errors except for ones that come from your script, but not from the [while] command itself. The is also just like in the [if] command and is the set of commands you want the loop to execute while it's running. Here is an example of a [while] loop: [code] set test "[chanlist #eggdrop]" set ops 0 while {$test != ""} { if {[isop [lindex $test 0] #eggdrop]} { incr ops } set test "[lreplace $test 0 0]" } puthelp "PRIVMSG #eggdrop :There are currently $ops people opped in #eggdrop." [/code] 10.2 LOOPING FOR EVERY VALUE An other loop is the [foreach] loop. In this loop you put one or more lists and than a set of commands will be executed for every object in that list. *The syntax of a [foreach] command is [foreach [ ...] { }].* The 's are in which string the object must be placed. You can put as many lists into the loop as you like, just as long as there is always a string with it to put the object in. Also because foreach already knows that its dealing with a string here you musn't put a $ in front of it. The will contain the object of the list as if [lindex] was used on it. The is the list of which the loop must get the objects from. The is the set of commands you want to execute and works in the same way as the of the [while] loop. Here is an example of a [foreach] loop: [code] set ops 0 foreach nickname [chanlist #eggdrop] { if {[isop $nickname #eggdrop]} { incr ops } } puthelp "PRIVMSG #eggdrop :There are currently $ops people opped in #eggdrop." [/code] 10.3 LOOPING FOR A NUMBER OF TIMES The third and final loop is the [for] loop. This loop is fairly simple and is mostly used when somebody wants to loop something for a number of times. *The syntax of a [for] command is [for { } { } { } { }].* The is the command you want to be executed before the loop starts. This can be setting a string to 0 for instance. The is the check that is made while the loop is running and works in the same way as the check in the [while] loop. The is the command you want to be executed every time a loop has ended. This can be increasing a string with 1 for instance. Here is an example of a [for] loop: [code] set test "[chanlist #eggdrop]" set ops 0 for { set number 0 } { $number < [llength $test] } { incr number } { if {[isop [lindex $test $number] #eggdrop]} { incr ops } } puthelp "PRIVMSG #eggdrop :There are currently $ops people opped in #eggdrop." [/code] 10.4 WRITING YOUR OWN SCRIPT Lets start a new script. Make a script that when a global operator messages "ops" to the bot, it sends a message to *all* the channels how many people there are currently opped there and how many bot operators there are in it. Remember that the goal is not to make the best and most efficient script right now, but trying to put into it as many things as possible that you've just learned. You can find an example script here: http://www.suninet.nl/tclguide/example-ch10.tcl. ---------------------------------------------------------------------------------------------------------------- 11. WORKING WITH FILES In this chapter you will learn how to read from and write to files. Files are more work but with them you preserve your information even after the bot has restarted. 11.1 OPENING FILES Before you can use a file you will have to open it. This can be done with the [open] command. *The syntax of an [open] command is [open [access]].* The is the name of the file you want to open. The [access] is what you want to do with the file. Here is a list of different access types according to the man pages of TCL 8.0.4: [code] r Open the file for reading only; the file must already exist. This is the default value if access is not specified. r+ Open the file for both reading and writing; the file must already exist. w Open the file for writing only. Truncate it if it exists. If it doesn't exist, create a new file. w+ Open the file for reading and writing. Truncate it if it exists. If it doesn't exist, create a new file. a Open the file for writing only. The file must already exist, and the file is positioned so that new data is appended to the file. a+ Open the file for reading and writing. If the file doesn't exist, create a new empty file. Set the initial access position to the end of the file. [/code] The [open] command command returns a so called channel. You will need this channel if you want to do something with the file so you have to "catch" this channel. This can be done by putting the channel in a string. An example would be [set fs [open $file r]], which would save the channel of $file to $fs. 11.2 CLOSING FILES After you are done with a file you have to close it. Closing is told right now before reading and writing so that later on complete and better examples can be given, what's the point of opening and closing files if you don't do anything with them, right? :) You can close files with the [close] command. *The syntax of a [close] command is [close ].* The is the channel of the file you cought with the [open] command. After you've closed a file you will have to re-open it before you can read from and write to it again. 11.3 READING FROM FILES You can read from a file either per line or per piece. With the [gets] command you can read from a file per line. *The syntax of a [gets] command is [gets [string]].* The is the channel of the file you cought with the [open] command. The [string] is the string in which you want to place the retrieved line If you specify a string the gets command will return how many characters were in the line and put the line in the string, if you do not specify a string the line itself is returned. A small example: [code] set fs [open $file r] gets $fs line(first) gets $fs line(second) close $fs [/code] This would put the first line of $file in $line(first) and the second line of $file in $line(second). You can read a piece of the file with the [read] command. *The syntax of a [read] command is [read [bytes]].* The is the channel of the file you cought with the [open] command. The [bytes] are the number of bytes you want to read from the file. If you do not specify the number of bytes the whole file is read. A small example: [code] set fs [open $file r] set info [read $fs 10] close $fs [/code] This would put the first 10 bytes of $file into $info. 11.4 WRITING TO FILES You can write to a file with the [puts] command. *The syntax of a [puts] command is [puts ].* The is the channel of the file you cought with the [open] command. The is what you want to write to the file. A small example: [code] set fs [open $file w] puts $fs "$nick" close $fs [/code] This would write $nick to $file. 11.5 THE END OF THE FILE In some cases you might not know how large a file and thus don't know up until where to read from the file. With the [eof] command you can find out if the end of the file has been reached. *The syntax of an [eof] command is [eof ].* The is the channel of the file you cought with the [open] command. The [eof] command returns a 1 if the last command that accessed the channel gave an "end of file" report and a 0 if it didn't. A way to use the [eof] command is in a [while] loop for instance. Here's an example: [code] set found 0 set fs [open $file r] while {![eof $fs]} { gets $fs line if {$line == $nick} { set found 1 } } close $fs if {$found} { putdcc $idx "$nick was found!" } else { putdcc $idx "$nick was not found." } [/code] This would check every line of $file and tell whether or not one of those lines was equal to $nick. 11.6 CHECKING IF A FILE EXISTS If you are working with files it is also good to be able to check if a file exists, else you'd have to create an empty file every time you install your script since TCL will return an error if you try to open a file that doesn't exist. This can be done with the [file exists] command. *The syntax of a [file exists] command is [file exists ].* The is the name of the file you want to check. Note that the path from which all external commands or file requests are filed is the directory in which your eggdrop binary resides, so if you want a file from somewhere outside the Eggdrop directory you will have to include the whole path to the file in . The [file exists] command returns 1 if the file exists and a 0 if it doesn't. 11.7 WRITING YOUR OWN SCRIPT Lets get back to the auto-voice and greeting script again. Make it so that everyone who joins one of the two channels gets voiced and that everybody is greeted in every channel with "Welcome $nick" or their personal greeting which can be set with a channel command called !greeting using a file as the database for the greetings. You can find an example of the script here: http://www.suninet.nl/tclguide/example-ch11.tcl. ---------------------------------------------------------------------------------------------------------------- 12. MISCELLANEOUS This chapter will cover various minor things that aren't big enough to have their own chapter, but are important and usefull enough to be mentioned. 12.1 TIMERS You can also make your script execute something after a certain number of minutes or seconds. This can be done with the [timer] and [utimer] commands. *The syntax of a [timer] command is [timer