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  &notice("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