1 programming your sirc client 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 4 5Warning: to understand this you need to know perl (the programming 6language; type "man perl" for more info on it), and to have read the 7README thoroughly. 8 9For a real usable example of sirc script, look at the file n0thing.pl; 10if you wonder how you could do something in sirc script, try 11understanding the functions defined in there. 12 13 14Commands: 15 16From /loaded scripts and .sircrc.pl, you can define new commands 17and give their implementation in perl. 18 19These scripts are actually files of perl code, and they get loaded 20right into sirc's context. 21 22To define a new command, all you need to do is define a sub with the 23name cmd_yourcommandname which does whatever you want it to do, and call 24&addcmd("yourcommandname"); 25 26You can also define some help for the command, by calling 27&addhelp("yourcommandname", "First line of help\nSecond line of help..."); 28 29Your sub gets all of its arguments in the global variable $args 30(unparsed), its own name in $cmd, and the whole command line in $line. 31 32It can also use a number of routines from the sirc client: 33 34 &load("file"); loads a sirc script, searching in @loadpath. 35 the ".pl" extension is optional. 36 37 &dosplat; turns a * into the current channel name, if it's 38 the first word of $args 39 40 &getarg; to get the first word of $args in $newarg and 41 the rest in $args 42 43 &yetonearg; same thing, removing a trailing : in $args if 44 there's one 45 46 &eq("txt1", "txt2"); tests case-insensitive equality 47 48 &sl("text"); to send a line of text to the server (the 49 trailing "\n" gets added automatically) 50 51 &tell("txt"); sends text to the screen, adding a "\n", and 52 only if not in silent mode 53 54 &print("txt"); sends text to the screen, adding a "\n", regardless 55 of silent mode 56 57 &getuserline("str", "prompt"); 58 prints "str" on the screen, puts "prompt" as a 59 temporary prompt if using ssfe, and prompts the 60 users for a line, returning it in $_ 61 62 &getuserpass("str", "prompt"); 63 same for prompting passwords; ssfe will not echo 64 the password 65 66 &dostatus; redisplays the status line 67 68 &msg("nck", "msg"); sends a message, printing it. the destination 69 can be a nick, a channel, or a =nick (DCC CHAT) 70 71 ¬ice("nck", "msg"); sends a notice, printing it. the destination 72 can be a channel or a nick 73 74 &say("msg"); says somethign on the current channel, printing it 75 76 &describe("nck", "msg"); sends a /describe, printing it 77 78 &me("msg"); does an action on the current channel, printing it 79 80 &connect($fh, "host", port); 81 opens a tcp connection with the given host and 82 port. the first argument ($fh) must be a 83 variable and &connect sets it to the value of 84 the file handle associated with the connection. 85 &tell's a message and returns 0 if there's an 86 error, otherwise returns 1. 87 88 &listen($fh, port); opens listening socket bound to the given port; 89 lets the system pick a port if the specified 90 port is 0 (or the second argument is not passed 91 at all). the first argument must be a variable 92 and &listen sets it to the value of the file 93 handle associated with the listening socket. 94 &tell's a message and returns 0 if there's an 95 error, otherwise returns the port on which the 96 socket listens. 97 98 &accept($nfh, $ofh); accepts a connection on the file handle $ofh 99 (which must refer to a listening socket), and 100 returns it in $nfh; $nfh must be a variable 101 and will be changed by &accept. $ofh is 102 automatically closed by &accept. returns a 103 boolean value, but does not print an error 104 message in any case. 105 106 &resolve("address"); resolves a hostname into a packed in_addr (i.e 107 a 4-byte string representing the IP address). 108 the argument can be a hostname, an IP address 109 written in dotted quad notation, or a (large) 110 number representing the address, "read" as a 111 32-bit number in network order. if the 112 resolution fails, returns a false result ("" or 113 0 or undef). 114 115 to get a dotted quad from what &resolve returns, 116 use join(".", unpack("C4", &resolve("whatever"))) 117 118 &newfh; returns a fresh name for use as a filehandle 119 120 &doset("variable", "value"); 121 sets a value to a SET variable; the value is 122 validated, and this has no effect if the value 123 is incorrect or the variable does not exist. 124 this is the only way scripts should ever change 125 the values of SET variables, except possibly 126 those that they define themselves. 127 128 &docommand("command"); interprets a command line as if it were typed at 129 the keyboard. a *single* leading "/" will 130 disable alias/function expansion on the 131 command. 132 *warning* this calls the command dispatcher 133 recursively from itself, which is pretty bad. 134 there is a test against loops (a limit on 135 recursion, set to 20), but it's mostly up to 136 *you* to make sure your scripts work. 137 perl being a language with strong and powerful 138 control structures (unlike ircII...), recursion 139 at this level should be avoided whenever 140 possible. 141 142 143You have access to the a number of global variables; note that some have 144been removed because they have been turned into SET variables, to be 145read in %set and written to with &doset. 146 147Unless otherwise specified, these variables should be treated as 148read-only by scripts. 149 150 $version sirc's version - should always be a number, and 151 never be modified by a user function 152 153 $add_ons additional modules loaded; scripts can add a 154 "+scriptname" to it 155 156 $restrict set to true if sirc is running in restricted 157 (secure) mode, which disallows access to the 158 shell and to the filesystem 159 160 $maxrecursion number of times &docommand may be called recursively 161 before giving a "max recursion exceeded" error (you 162 can change this one, but it is not guaranteed to 163 work on future versions where this might become a 164 SET variable) 165 166 $nick your current nick 167 168 $server your current server 169 170 @channels list of channels you're on 171 172 $talkchannel your current channel (or '' if none) 173 174 %mode associative array with the modes of the different 175 channels we're on. the channel names are all in 176 lower case, and the mode is a string of letters 177 without +'s or -'s, and without 'k' or 'l' either 178 since those are treated separately. the value 179 for channels without any mode is '', while the 180 value for channels we're not on is undef. 181 182 %chankey keys to channels, undef if none or we're not on 183 the channel. channel names are in lower case. 184 185 %limit limits to channels, undef if none or we're not 186 on the channel. channel names in lower case. 187 188 %haveops associative array of booleans, true if we have ops 189 on the channel. channel names are... you know how 190 191 $umode user mode, string of letters without +'s or -'s 192 193 $query whoever you're querying, or '' if no-one 194 195 %aliases associative array of defined substitution aliases; 196 the alias name is in CAPS 197 198 %set associative array of SET variable values; the 199 variable name is in CAPS too 200 201 %notify associative array of the notify list; the value 202 for a given nick is 0 for "absent", or the time 203 of the most recent notification for this nick 204 205 $bindaddr this is the IP address of the machine to which 206 outgoing connections are bound, as far as sirc 207 can tell. it changes when the "localhost" SET 208 variable changes, and is set to the IP of the 209 proxy machine when sirc is running with socks 210 support loaded. $bindaddr is a 4-byte string 211 representing a packed in_addr; you can get an 212 integer out of it (as used by DCC CHAT/SEND) 213 with unpack("N", $binaddr); and a dotted quad 214 with join(".", unpack("C4", $bindaddr)); 215 216 217 218Unless otherwise specified, commands and hooks should never modify the 219parameters that are passed to them (i.e. do somethign like 220$_[1]="some value". If they wish to modify local copies of them, they 221should start with local(...)=@_; 222 223Also, if your script is going to use global variables, please make sure 224they're not likely to clash with sirc's own (same goes for file 225descriptor names, and procedures). A good convention would be to give 226all these variables and procedures a name that starts with the script 227name, or with a few letters from it. For example, in n0thing.pl all the 228script's global variables and internal procedures have names that start 229with "n_". 230 231Example, which could be put into a file and /load'ed directly, of 232a command that will yeek on a channel if you specify one, and at a 233nick if you do too: 234 235~~~~~~~~~~~~~~~~~~~~~~~~~~~ [snip snip] ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 236sub cmd_yeek { 237 &dosplat; # if the 1st arg is *, replace it with $talkchannel 238 &getarg; # get 1st arg in $newarg 239 local($channel)=($talkchannel); # by default we talk to $talkchannel 240 if ($newarg =~ /^[\#\&]/) { # if the 1st arg starts with # or & 241 $channel=$newarg; # talk there instead 242 &getarg; # and get an extra arg 243 } 244 if ($newarg) { # look at whether we specified who we're yeeking at 245 &describe($channel, "look at $newarg and *yeeeks*"); 246 } else { # or not 247 &describe($channel, "*yeeks* at the crowd"); 248 } 249} 250 251# \cb is the way to specify ^B in perl 252&addcmd("yeek"); 253&addhelp("yeek", "Usage: \cbYEEK\cb [<channel>] [<nick>] 254Yeeks at the given nickname or at the whole channel. 255Examples: /yeek someone 256 /yeek * someone 257 /yeek #channel 258 /yeek #channel someone"); 259 260~~~~~~~~~~~~~~~~~~~~~~~~~~~ [snip snip] ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 261 262Hooks: 263 264From /load'ed scripts, as well as from .sircrc.pl, you have 265the possibility to define subs to be called when specified events 266occur. This is the equivalent of ircII's /on's. 267 268To declare a hook, you must define a subroutine called "hook_somename" 269which does whatever you want done when a hook of type "hook_type" is 270triggered, and then call &addhook("hook_type", "somename"); 271 272To remove a hook, you call &remhook("hook_type", "somename"); 273 274Numeric hooks are also available, for every 3-digit number; to declare 275one of those, define a soubroutine called "hook_somename" which does 276what you want, and call &addhook("xxx", "somename"), where xxx is the 277number of the numeric reply. To remove one of these, you call 278&remhook("xxx", "somename"); 279 280Subs called from hooks have access to the same functions and variables 281listed above for functions, plus a few specific ones (wherever 282applicable): 283 284$who is the nick that triggered the hook 285$user is the corresponding username 286$host is the corresponding hostname 287 288Hooks can also set the variable $silent if they want to provide the 289display for the event (via &print) and inhibit the default. This 290is the direct equivalent of the "^" switch on ircII /on's, except 291for "raw_irc". 292 293Hooks marked with a * can also set the special variable $skip and cause 294the line to be ignored by the client. This is in general a bad idea, 295use $silent whenever possible. Only the hooks where this provides some 296actual additional functionality have this possibility. For "raw_irc" 297this is the equivalent of "^" switch on ircII's /on raw_irc. 298 299The following hooks are available, and get called with the following 300arguments: 301 302action activated by a ctcp action; 303 1st arg is the nick or channel it was sent to 304 2nd arg is the message 305 306command * activated by the user typing a command (regardless of 307 whether it is a /command or just a line of text) 308 1st arg is the user's line 309 this hook is special in that (like "print" and 310 "status"), it is explicitly allowed to modify its 311 argument ($_[0]) to change what command should be 312 interpreted. 313 setting $skip=1 in the hook will make sirc ignore the 314 command 315 316chat_disconnect activated when a dcc chat is lost (but not when the 317 user closes one with DCC CLOSE CHAT) 318 1st arg is the nick associated with the chat 319 320ctcp * activated by any ctcp, BEFORE the client parses 321 and eventually answers the ctcp. 322 1st arg is the nick or channel it was sent to 323 2nd arg is the ctcp command 324 3rd arg are the arguments 325 326ctcp_reply activated by ctcp replies; 327 1st arg is the nick or channel it was sent to 328 2nd arg is the ctcp command 329 3rd arg are the arguments 330 331dcc_chat activated by received text over a dcc chat 332 1st arg is the nick 333 2nd arg is the text 334 335dcc_disconnect activated when a dcc get or send is finished or closed 336 (even when the user closes one with DCC CLOSE GET/SEND) 337 1st arg is the nick associated with the chat 338 2nd arg is the filename 339 3rd arg is the number of bytes transferred 340 4th arg is the number of seconds the transfer took 341 342dcc_request activated by a received dcc chat or send request, and 343 after the client has processed the request. this is 344 the hook to use if you want to implement any kind of 345 auto-dcc. 346 1st arg is the type ("CHAT" or "SEND") 347 2nd arg is the machine address (a 32-bit integer) 348 3rd arg is the port 349 for a DCC SEND offer: 350 4th arg is the file name 351 5th arg is the file lenght 352 353disconnect activated by losing the connection to the server, or 354 breaking it with /disconnect (but not with /server). 355 no arguments are passed 356 357input * activated whenever the client wants to ask the user 358 for a line through &getuserline (i.e. when you got 359 disconnected, or need a new nick, or some script called 360 &getuserline). 361 1st arg is the "long" prompt 362 2nd arg is the "short" one 363 if the hook sets $skip, then &getuserline won't ask 364 the user for anything, and the contents of $_ will 365 be returned 366 367invite activated by invites; 368 1st arg is the channel you're invited to 369 370join activated by joins; 371 1st arg is the channel that $who is joining 372 373kick activated by kicks; 374 1st arg is the nick of the person who got kicked 375 2nd arg is the channel that they got kicked from 376 3rd arg is the reason 377 378leave activated by parts; 379 1st arg is the channel that $who is leaving 380 381mode activated by mode changes; 382 1st arg is the channel or user the change applies to 383 2nd arg is the mode change itself 384 385msg activated by msgs; 386 1st arg is the message 387 388nick activated by nick changes 389 1st arg is $who's new nick 390 391notice activated by notices 392 1st arg is the nick or channel it was sent to 393 2nd arg is the message 394 395server_notice activated by notices from servers 396 1st arg is the nick or channel it was sent to 397 2nd arg is the message 398 399notify_signon activated by a notify signon 400 1st arg is the nick 401 $user and $host are *not* set to anything meaningful 402 403notify_signoff activated by a notify signoff 404 1st arg is the nick 405 $user and $host are *not* set to anything meaningful 406 407print * activated by the printing of any line to the screen 408 1st arg is the line to print 409 this hook is special in that (like "status" and 410 "command") it is explicitly allowed to modify its 411 argument ($_[0]) to change what line should be 412 printed. 413 setting $skip=1 in the hook will prevent the line from 414 being actually printed 415 416public activated by non-ctcp messages to a channel; 417 1st arg is the channel 418 2nd arg is the message 419 420raw_irc * activated by any server line 421 $who is the originator (user or server) 422 $user is his username ('' if it comes from a server) 423 $host is his hostname (same comment) 424 1st arg is the command 425 2nd arg are the arguments 426 427send_action activated when we send a /me or a /de 428 ($who, $user and $host do not apply here) 429 1st arg is the nick/channel 430 2nd arg is the action 431 432send_ctcp activated when we send a ctcp 433 1st arg is the nick or channel the ctcp is being sent to 434 2nd arg is the complete ctcp text (type and arguments) 435 436send_dcc_chat activated when we send text over a dcc chat 437 ($who, $user and $host do not apply here) 438 1st arg is the nick we're sending to 439 2nd arg is the text 440 441send_text activated when we send a /msg or speak on a channel 442 ($who, $user and $host do not apply here) 443 1st arg is the nick/channel 444 2nd arg is the msg 445 446send_notice activated when we send a notice 447 ($who, $user and $host do not apply here) 448 1st arg is the nick/channel 449 2nd arg is the notice 450 451signoff activated when someone signs off 452 1st arg is the quitting comment 453 454status activated when sirc redraws the status line (as a 455 result of &dostatus being called, either internally 456 or by a script). 457 1st arg is the proposed status line 458 this hook is special in that (like "print" and 459 "command") it is explicitly allowed to modify its 460 argument ($_[0]) to change what should go to the status 461 line 462 463topic activated when someone changes the topic 464 1st arg is the channel 465 2nd arg is the new topic 466 467<3-digit nb> * activated by that particular server numeric reply 468 1st arg is whatever the server sent after the number, 469 unparsed (which means there's still a : in front of the 470 last argument) 471 472 473Example, which could be put into a file and /load'ed directly, of 474a hook that will rejoin a channel whenever you are kicked: 475 476~~~~~~~~~~~~~~~~~~~~~~~~~~~ [snip snip] ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 477# auto-rejoin hook 478 479sub hook_kicked { 480 local($kicked, $where_from, $reason)=@_; 481 # local vars with the args 482 if (&eq($kicked, $nick)) { # if *we* got kicked 483 &sl("JOIN $where_from"); # send a JOIN to the server 484 } 485} 486&addhook("kick", "kicked"); # activate the hook 487 488~~~~~~~~~~~~~~~~~~~~~~~~~~~ [snip snip] ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 489 490Another example, to display the username and hostname with each message 491(which is better done with /set printuh anyway): 492 493~~~~~~~~~~~~~~~~~~~~~~~~~~~ [snip snip] ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 494# userhost-on message hook 495 496sub hook_uhmsg { 497 &tell("[\cb${who}!${user}\@${host}\cb] $_[0]"); # print everything 498 $silent=1; # disable the default display 499} 500&addhook("msg", "uhmsg"); # activate the hook 501 502~~~~~~~~~~~~~~~~~~~~~~~~~~~ [snip snip] ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 503 504 505SET variables: 506 507A script can access SET variables: 508 509. to read them, you just need to look at $set{"VARIABLE"}, where the 510 variable name is written in caps 511 512. to set them, call &doset("variable", "value"); the variable name 513 can be in either case (case is not significant) and the value is 514 checked 515 516A script can also add its own SET variables, providing a default value 517and a hook to check and set a new value. To do this, the script must: 518 519. set the variable to a default value, with $set{"VAR"}="whatever"; 520 sirc will not let the user /set a variable if a value for it in %set 521 does not exist 522 523. define a sub called set_somename, and call &addset("var", "somename"); 524 the variable name can be passed in either case. 525 526The subs that serve as hooks for SET variables get called with the 527proposed value as the first argument. They may (or not) change the 528actual value in $set{"VAR"}, to the value given or to another one. By 529convention, they should not &tell anything, and should ignore invalid 530values. 531 532What goes in $set{"VAR"} must still be human-readable; for things like 533toggles, it is suggested that the values in $set{"VAR"} should be "on", 534"off" or similar, and that &doset can set a variable (internal to the 535script) to 0 or 1, which will be the one actually checked by the script. 536 537 538Userhost requests: 539 540Sometimes in a function you need to know the full username and hostname 541for some nick. If this happens in a hook, and the nick is the one who 542did the action, then the nick is in $who and the userhost data is 543already in $user and $host. 544 545Otherwise, you have to call the perl function &userhost giving it three 546arguments: the nickname, what you want evaluated when the data is 547available, and what you want evaluated if the nick is not found on IRC; 548if the third argument is ommited, sirc will print the default message 549"*?* somenickname not found on IRC". 550 551Unlike with earlier versions of sirc, it is possible to do more than 552one userhost request in a short time before getting the answers from 553the server. 554 555 556Example: a function that prints someone's country code 557 558~~~~~~~~~~~~~~~~~~~~~~~~~~~ [snip snip] ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 559# country code 560 561sub printcountry { # prints $host's country code 562 563 if ($host =~ /\.([^.]+)$/) { # match the last part of the host 564 local($c)=($1); # put it in local var $c 565 $c="USA" if $c =~ /^edu$/i; # if it's a .edu, say it's USA 566 $c="USA (probably)" if $c =~ /^com$/i || $c =~ /^org$/i || $c =~ /^net$/i; 567 # if it's a .org, .com or .net, it's 568 # probably in the USA too 569 if ($c =~ /^\d+$/) { # if it's a number 570 &tell("*** out of luck, $who has an IP address :p"); 571 # complain, it's an IP 572 } else { # otherwise 573 &tell("*** $who is in $c"); 574 # announce the result 575 } 576 } 577} 578 579sub cmd_country { # this is the command 580 581 &getarg; # get the argument in $newarg 582 if ($newarg) { # if it's there 583 &userhost($newarg, "&printcountry;"); 584 # request a userhost with &printcountry as 585 # action to take 586 } else { # otherwise 587 &tell("*** Whose?"); # complain 588 } 589} 590 591&addcmd("country"); # install the command 592 593~~~~~~~~~~~~~~~~~~~~~~~~~~~ [snip snip] ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 594 595 596Timers: 597 598It is possible in sirc to specify an action to be done but delayed, a 599certain number of seconds later, just like with ircII's /timer function. 600This is only precise up to the second. 601 602To do this, you call the function &timer with the number of seconds to 603wait as the first argument, and the string to be evaluated as the second 604argument. 605 606A third argument can be supplied; it needs to be a non-zero number, and 607will be used as the reference number for the timer. Setting a timer when 608another with the same reference number exists will delete the first 609timer. If no number is specified, the timer cannot be deleted. 610 611To delete a timer with reference number $n, call &deltimer($n); 612 613This is simple enough, but if you really need an example, here comes: 614to print "hello" in 10 seconds, you'd do &timer(10, "&tell('hello')"); 615 616If you want to be able to cancel it, you'd do 617&timer(10, "&tell('hello')", 6); and then to cancel it you'd do 618&deltimer(6); It goes without saying that the '6' is arbitrary. 619 620 621Adding file handles to the main select() loop: 622 623As of sirc 2.2, scripts can add file handles to the main loop, and set 624hooks on them to get control when data is available. 625 626It is up to the script to first open the file handle, which can refer to 627a network connection, a tty, a pipe (as in open(FH, "program |")), etc. 628 629When dealing with network sockets, it is strongly suggested to use the 630API provided by sirc (&connect, &listen, &accept, &resolve, &newfh and 631$bindaddr) rather than using the raw perl functions. This will have the 632effect of making these scripts work transparently over socks proxies, 633when the socks module is loaded. If you need some extra functionality, 634though (such as UDP sockets, or accepting multiple connections from the 635same listening socket), you can use perl's own functions. 636 637To get control back when data is available over a filehandle, you add 638it to the set of fh's sirc select()s from, with 639 640&addsel($fh, "somename", flag); where sel_somename is the sub that will 641get control back when there is something to read, and flag is 1 if you 642want sirc to buffer the connection and break it into lines for you, and 6430 if you don't want sirc to touch the data at all. 644 645The convention for sel_somename subs is different in the two cases: 646 647. for buffered filehandles, it is passed a single argument containing 648 a line that was read, including the final \n. if the connection is 649 closed or broken, this argument is '' and the filehandle has been 650 already closed and removed from the set of fh's to select() on. if 651 you close it yourself, you must use &remsel to tell sirc to remove 652 it from the set. 653 654. for unbuffered filehandles, no arguments are passed; the hook is called 655 when select() indicates that the fh is ready for reading, and it is 656 up to the hook to read from it (making sure not to block, so sysread 657 should be used and not <>), and eventually close it and remove it 658 with &remsel. 659 660To remove a filehandle from the set of fh's being select()ed on, call 661&remsel($fh); where $fh is the filehandle. 662 663Note that sirc never does any checking that a filehandle you give it is 664valid. Having a closed or invalid fh in the set of select()able ones, 665or not actually reading the data on an unbuffered sel_* hook, will cause 666sirc to hog the CPU by not blocking in select(). 667 668For an example of a nontrivial use of all of this, see the script ftp.pl 669which implements an ftp client inside sirc (it can be found at the sirc 670webpage at http://www.eleves.ens.fr:8080/home/espel/sirc/sirc.html). 671 672 673Bots: 674 675It is possible to make bots in sirc script, just like you make bots in 676ircII. It's even probably not a bad idea, since you have a proper and 677powerful programming language (perl) at your disposition, with all the 678boring network programming and parsing of server stuff already done for 679you. 680 681However, sirc was never meant as a bot client, and I have no intention 682of filling it up with bells and whistles for bot support, so I've only 683provided minimal support for this, with the -l and -q options. 684 685The idea is, you program your bot as a set of internal functions and 686hooks and a calls to &addhook and to &docommand, and then load the sirc 687this way (obviously without ssfe): 688 689nohup sirc -d -q -l <botfile> -i <bot's ircname> <nick> <server> >/dev/null & 690 691All of this without the <>'s, of course. The >/dev/null is there to 692suppress the output, since you won't be reading it on the screen 693anyway. 694 695In the bot, make sure you catch (with a numeric hook) the lines that 696tell "nick in use" or "invalid nick", and send lines to the server with 697some random nick, and get them skipped, or the bot will freeze trying to 698ask the user for a nick. You should also set up a hook on "disconnect", and 699make it do a &docommand("server 0") or "server some.server". 700 701Also remember that the file gets loaded even before the server connection 702is made, so calls to &sl and most &docommand's at that point will fail. 703 704Here's an example of a bot that connects, joins a channel, reconnects if 705disconnected, responds to a few commands, ops its owner, and logs all it 706sees except public stuff to a file; you'd load this one, assuming you 707saved it in a file called "mybot" and want to call it BubbleBot, with: 708 709nohup sirc -d -q -l mybot -i "bot in sirc" BubbleBot some.server.edu >/dev/null 710 711 712~~~~~~~~~~~~~~~~~~~~~~~~~~~ [snip snip] ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 713$botowner="espel\@clipper.ens.fr"; # change it to your address 714 715$logfile=$ENV{'HOME'}."/.botlog"; 716&docommand("log on"); 717 718sub hook_publicstuff { # don't print the public chatter (so it 719 $silent=1; # doesn't fill the logfile 720} 721&addhook("public", "publicstuff"); 722 723sub hook_connected { 724 &sl("JOIN #BotTub"); 725} 726&addhook("376", "connected"); # join on the "end of MOTD" numeric 727 728srand; # init random number generator 729 730sub hook_badnick { 731 local($n); 732 $n="B".(int(rand(1000000))+4587454); # send a garbage nick... 733 &sl("NICK $n"); 734 $skip=1; 735} 736&addhook("432", "badnick"); # if told that ours is taken 737&addhook("433", "badnick"); 738 739sub hook_disc { # if we got disconnectedj 740 sleep(30); # wait 30 seconds (so we don't bring the machine 741 # down to a crawl if the server is down) 742 &docommand("server 1"); # reconnect to the same server 743} 744&addhook("disconnect", "disc"); 745 746sub hook_joined { # whenever someone joins 747 local($ch)=($_[0]); 748 $ch =~ tr/A-Z/a-z/; # put channel in lowercase 749 if (&eq($botowner, "$user\@$host") && $haveops{$ch}) { 750 &sl("MODE $ch +o $who"); # op if that's the owner and we have ops 751 } 752} 753&addhook("join", "joined"); 754 755sub hook_message { 756 if (&eq($botowner, "$user\@$host")) { # if it's a msg from the owner 757 if ($_[0] =~ /^die$/i) { # die -> die 758 &docommand("quit"); 759 } elsif ($_[0] =~ /^say /i) { # say <something> -> say it 760 &say($'); 761 } elsif ($_[0] =~ /^nick /i) { # nick <nick> -> change nicks 762 &sl("NICK $'"); 763 } 764# add more commands here 765 } 766} 767&addhook("msg", "message"); 768 769~~~~~~~~~~~~~~~~~~~~~~~~~~~ [snip snip] ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 770 771