+2002-08-05 Clinton Ebadi <clinton@unknownlamer.org>
+
+ * source/Parser.C (parseCTCP): Removed call to htonl and fixed
+ DCC! Ack, sockets take their arguments in network byte order so
+ there is no need to convert to host...now DCC _sort of_ works.
+
+2002-08-04 Clinton Ebadi <clinton@unknownlamer.org>
+
+ * source/UserList.C (save): Increment iterator twice to get around
+ bug (see BUGS #2)
+
+ * source/ServerQueue.C: Now sends SEND_* hooks instead of
+ triggered general hooks.
+
+ * source/Interp.C (Startup): New scheme side defines: hooks/send/*
+ (* = the new SEND_ hooks, but lowercase).
+
+ * source/BotInterp.H: New hook types (SEND_..., ... = ACTION,
+ CTCP, PUBLIC, MESSAGE). These are triggered on send messages.
+
2002-08-01 Clinton Ebadi <clinton@unknownlamer.org>
* source/Bot.C (set_log_file): Oops! Fixed logging. Now the bot
=====================================================================
Version 2.1.1: foom
-- Hooks are now executed when the bot sends a privmsg. This now makes
- log scripts able to log what the bot said. This probably has other
- uses too and shouldn't have any real impact on performance (since it
- has to execute hooks on all incoming messages anyway, and there are
- probably a lot more incoming than outgoing).
- You can now "name" a hook using an extra arg to bot:addhook. This
name can be used to have multiple hooks of the same type with the
same regexp. The default name is "DEFAULT" so don't use that as the
name for your hooks.
-- There is a new macro for scripts--"not-from-me". This allows you to
- protect your hooks from calling themselves because they trigger
- themselves. See the manual for more about what it does
- (Scripting->Misc Scripting Stuff).
- Logging now works again (oops, I didn't realize I broke it until I
started to work on DCC).
+- DCC CHAT now "works." You can connect to the bot and talk to to it,
+ but it doesn't do anything useful.
+- New hooks: hooks/send/... where ... is one of action, ctcp, public,
+ or message. These are triggered when the bot does an
+ ACTION, sends a CTCP (_not_ a ctcp-reply), sends a PRIVMSG to a
+ channel, or sends a PRIVMSG to another user, respectively. There
+ will be more send hooks added later.
Version 2.1.0: Zug Zug
- Hooks can now be fallthrough or non fallthrough. You can set a hooks
* Finish adding commands to Scheme for sending messages
(e.g. bot:send-CTCP to send a CTCP message)
* Add util functions for doing stuff like quoting CTCP messages
+* Make it possible to use Scheme functions in the Parser itself
Networking:
+* Add hooks for DCC CHAT?
* Add a networked interface to guile repl
- Admins only
- - SSH? Telnet? DCC-Chat?
- - Access to repl will require user to authenticate
- - Allow server to be disabled at run because of security...
-* Fix DCC support
- - Note that Socket will have a buffer overflow problem with DCC
- because it uses a buffer of 512 characters, and a DCC line is not
- limited to 512 chars like an IRC line is. The question is: should
- I rewrite readLine to be more general (allocate buffer on the
- fly) or rename readLine to ircReadLine and add a more general
- readLine? I think I could use a static std::string and have it
- grow as needed, with a default size of 512.
-* Make connecting to irc.oftc.net work...I wonder if their ircd is b0rked
+ - Telnet
+ - Store authorized users and passwords in bot.telnet file
+ - Bot master can add new telnet users
+ - MUST HAVE PASSWORD
+* Make connecting to irc.oftc.net work...I wonder if their ircd is
+ broken
+* DCC FILE support
Documentation:
* Work on Texinfo manual (especially scripting section)
user must be at least a `min-level' user to use the new command. None
of the arguments are guaranteed to be passed; if they aren't they are
set to the empty string `""'. An example of a new command would be:
-(define (hello channel name) (if (string=? name "")}@* (bot:say
-channel "Hello world!") (bot:say channel (string-append "Hello "
-name "!")))
- (bot:addcommand "hello" hello #t 2 0)
+ (define (hello channel name)
+ (if (string=? name "")
+ (bot:say channel "Hello world!")
+ (bot:say channel (string-append "Hello " name "!")))
+
+ (bot:addcommand "hello" hello #t 2 0)
- This will display ``Hello World!'' if called as !hello and ``Hello
-World `USER''' if called as !hello USER.
+ This will display "Hello World!" if called as `!hello' and "Hello
+World `USER'" if called as `!hello USER'.
\1f
File: bobot++.info, Node: Hooks, Next: Scheme User Levels, Prev: Adding New Commands, Up: Scripting
*Note Hook Types::). `regex' is a standard regular expression. If
`regex' is matched, `function' will be called. `function' will take a
different number of args depending on the hook type. `pri' specifies
-the priority of the hook---higher priority hooks are executed first.
+the priority of the hook--higher priority hooks are executed first.
This argument is optional and defaults to `0'. `fall' is `#t' if the
hook is a fallthrough hook and `#f' is the hook is not a fallthrough
hook. This arg is also optional and default to `#t'. `name' is the
-optional name of the hook that defaults to ``DEFAULT''. If you set the
+optional name of the hook that defaults to "DEFAULT". If you set the
name then you can have more than one hook that matches the same regexp,
as long as they have the same name. E.g. in a log script you could have
the regexps for the log function all be `".*"' and set their names to
* `hooks/name' (this is the Scheme variable name of the hook)
- Description of the hook
- - # of args
- - `arg1': desc
+ - ARG1 ARG2 ... ARGN
+ - ARG1: desc
- - `arg2': desc
+ - ARG2: desc
- ...
- - `argN': desc
+ - ARGN: desc
That said, here is the list of available hooks: FIXME: write docs
* `hooks/action'
- - Description of the hook
+ - This hook is triggered when someone performs an action.
- - # of args
- - `arg1': desc
+ - FROM, TO, ACTION
+ - FROM: this is the address of the person that performed
+ the action in the form `USER ! NICK @ HOST' (without the
+ spaces).
+
+ - TO: This is the target of the action, which is either a
+ channel or the Bot's nick.
+
+ - ACTION: This is the text of the action. E.g. if someone
+ did `* foobar does baz', then ACTION would be the string
+ `"does baz"'.
* `hooks/nickname'
- Description of the hook
\1f
File: bobot++.info, Node: High Level Message Functions, Next: Low Level Message Functions, Prev: Sending Messages, Up: Sending Messages
-``High Level'' Message Functions
---------------------------------
+"High Level" Message Functions
+------------------------------
...
\1f
File: bobot++.info, Node: Low Level Message Functions, Prev: High Level Message Functions, Up: Sending Messages
-``Low Level'' Message Functions
--------------------------------
+"Low Level" Message Functions
+-----------------------------
- The ``Low Level'' messaging functions allow you to do things like
-send CTCP messages. You probably want to read rfc 2812 and the CTCP spec
+ The "Low Level" messaging functions allow you to do things like send
+CTCP messages. You probably want to read rfc 2812 and the CTCP spec
before using these. If you have no idea what these do, read rfc 2812
(IRC Client Protocol) and CTCP spec. These functions all return
`*unspecified*' always, so don't use the return value for anything.
bot:exit-hook THUNK' where THUNK is an argumentless procedure (a
thunk). When the bot exits your thunk will be called.
- Since a bot calls hooks on things it says, you have to be careful
-about hooks that output text that might match itself. E.g. if you have
-a hook that matches `"foo"' and the hook displays `"foo to the
-whatsit?"', then the hook will call itself over and over until the
-stack overflows! To protect against this I wrote the macro
-`not-from-me'. You call it like this: `(not-from-me from (stmts if not
-from bot) (stmts if from bot))'. E.g.
-
- (bot:addhook hooks/public "foo"
- (lambda (f t p)
- (not-from-me f ((bot:say t "foo to the what!")))))
-
- This say ``foo to the what!'' to the channel that ``foo'' was said in
-and do nothing otherwise. You can optionally specify an action to be
-executed if the message is from the bot:
-
- (bot:addhook hooks/public "foo"
- (lambda (f t p)
- (not-from-me f ((bot:say t "foo to the what!"))
- ((bot:say t "moof")))))
-
- That will do the same thing as the first example, but the bot will
-say ``moof'' if it said ``foo'' before. That probably isn't a very nice
-thing to do, but it works as an example. You can have as many staments
-as you want in the clauses.
-
\1f
File: bobot++.info, Node: Concept Index, Next: Function Index, Prev: Scripting, Up: Top
* Menu:
+* exit-hook: Misc Scripting Stuff.
* hooks/action: Hook Types.
* hooks/ctcp: Hook Types.
* hooks/ctcp-reply: Hook Types.
Node: User Levels\7f2673
Node: Scripting\7f3677
Node: Adding New Commands\7f4585
-Node: Hooks\7f5828
-Node: Creating a Hook\7f6766
-Node: Hook Types\7f7908
-Node: Scheme User Levels\7f10381
-Node: Sending Messages\7f11510
-Node: High Level Message Functions\7f12107
-Node: Low Level Message Functions\7f12325
-Node: Misc Scripting Stuff\7f13084
-Node: Concept Index\7f14710
-Node: Function Index\7f14892
-Node: Variable Index\7f15153
+Node: Hooks\7f5852
+Node: Creating a Hook\7f6790
+Node: Hook Types\7f7929
+Node: Scheme User Levels\7f10869
+Node: Sending Messages\7f11998
+Node: High Level Message Functions\7f12595
+Node: Low Level Message Functions\7f12809
+Node: Misc Scripting Stuff\7f13562
+Node: Concept Index\7f13981
+Node: Function Index\7f14163
+Node: Variable Index\7f14424
\1f
End Tag Table
at least a @code{min-level} user to use the new command. None of the
arguments are guaranteed to be passed; if they aren't they are set to
the empty string @code{""}. An example of a new command would be:
-@verb{|
+
+@example
(define (hello channel name)
- (if (string=? name "")}@*
+ (if (string=? name "")
(bot:say channel "Hello world!")
(bot:say channel (string-append "Hello " name "!")))
(bot:addcommand "hello" hello #t 2 0)
-|}
+@end example
+
This will display ``Hello World!'' if called as @kbd{!hello} and
``Hello World @code{USER}'' if called as @kbd{!hello @var{USER}}.
@cindex Background on Hooks
Hooks are a powerful feature of Bobot++. Hooks are a hybrid of ircII
-hooks and tiny fugue (a MUD bot) hooks. The basic idea of a hook if
-that you match a text against regular expression and call a function
-if text in a message matches that regex. The different types of hooks
-provided by Bobot++ correspond to the different classes of messages
-that Bobot++ can recieve. A Hook also has several properties,
-including its priority and whether or not it is a fallthrough
-hook. Higher priority hooks are executed before lower priority hooks
-and fallthrough hooks are executed before non-fallthrough hooks of the
-same priority. A fallthrough hook can match and processing of hooks
-will continue; as soon as the first non-fallthrough hooks matches
-processing of hooks stops.
+and tiny fugue (a MUD bot) hooks with a little bit of extra stuff
+added in. The basic idea of a hook if that you match a text against
+regular expression and call a function if text in a message matches
+that regex. The different types of hooks provided by Bobot++
+correspond to the different classes of messages that Bobot++ can
+recieve. A Hook also has several properties, including its priority
+and whether or not it is a fallthrough hook. Higher priority hooks are
+executed before lower priority hooks and fallthrough hooks are
+executed before non-fallthrough hooks of the same priority. A
+fallthrough hook can match and processing of hooks will continue; as
+soon as the first non-fallthrough hooks matches processing of hooks
+stops.
@menu
* Creating a Hook::
@item
Description of the hook
@item
-# of args
+@var{arg1} @var{arg2} ... @var{argn}
@itemize @minus
@item
-@code{arg1}: desc
+@var{arg1}: desc
@item
-@code{arg2}: desc
+@var{arg2}: desc
@item
...
@item
-@code{argN}: desc
+@var{argN}: desc
@end itemize
@end itemize
@end itemize
@code{hooks/action}
@itemize @minus
@item
-Description of the hook
+This hook is triggered when someone performs an action.
@item
-# of args
+@var{from}, @var{to}, @var{action}
@itemize @minus
@item
-@code{arg1}: desc
+@var{from}: this is the address of the person that performed the
+action in the form @samp{@var{user} ! @var{nick} @@ @var{host}}
+(without the spaces).
+@item
+@var{to}: This is the target of the action, which is either a channel
+or the Bot's nick.
+@item
+@var{action}: This is the text of the action. E.g. if someone did
+@samp{* foobar does baz}, then @var{action} would be the string
+@code{"does baz"}.
@end itemize
@end itemize
These are a few useful things that I thought people writing scripts
might want to know.
+@vindex exit-hook
If you want to execute code when the bot exits, just do
@code{add-hook! bot:exit-hook @var{thunk}} where @var{thunk} is an
argumentless procedure (a thunk). When the bot exits your thunk will
be called.
-Since a bot calls hooks on things it says, you have to be careful
-about hooks that output text that might match itself. E.g. if you have
-a hook that matches @code{"foo"} and the hook displays @code{"foo to
-the whatsit?"}, then the hook will call itself over and over until the
-stack overflows! To protect against this I wrote the macro
-@code{not-from-me}. You call it like this: @code{(not-from-me from
-(stmts if not from bot) (stmts if from bot))}. E.g.
-
-@example
-(bot:addhook hooks/public "foo"
- (lambda (f t p)
- (not-from-me f ((bot:say t "foo to the what!")))))
-@end example
-
-This say ``foo to the what!'' to the channel that ``foo'' was said in
-and do nothing otherwise. You can optionally specify an action to be
-executed if the message is from the bot:
-
-@example
-(bot:addhook hooks/public "foo"
- (lambda (f t p)
- (not-from-me f ((bot:say t "foo to the what!"))
- ((bot:say t "moof")))))
-@end example
-
-That will do the same thing as the first example, but the bot will
-say ``moof'' if it said ``foo'' before. That probably isn't a very
-nice thing to do, but it works as an example. You can have as many
-staments as you want in the clauses.
+@c Since a bot calls hooks on things it says, you have to be careful
+@c about hooks that output text that might match itself. E.g. if you have
+@c a hook that matches @code{"foo"} and the hook displays @code{"foo to
+@c the whatsit?"}, then the hook will call itself over and over until the
+@c stack overflows! To protect against this I wrote the macro
+@c @code{not-from-me}. You call it like this: @code{(not-from-me from
+@c (stmts if not from bot) (stmts if from bot))}. E.g.
+
+@c @example
+@c (bot:addhook hooks/public "foo"
+@c (lambda (f t p)
+@c (not-from-me f ((bot:say t "foo to the what!")))))
+@c @end example
+
+@c This say ``foo to the what!'' to the channel that ``foo'' was said in
+@c and do nothing otherwise. You can optionally specify an action to be
+@c executed if the message is from the bot:
+
+@c @example
+@c (bot:addhook hooks/public "foo"
+@c (lambda (f t p)
+@c (not-from-me f ((bot:say t "foo to the what!"))
+@c ((bot:say t "moof")))))
+@c @end example
+
+@c That will do the same thing as the first example, but the bot will
+@c say ``moof'' if it said ``foo'' before. That probably isn't a very
+@c nice thing to do, but it works as an example. You can have as many
+@c staments as you want in the clauses.
@node Concept Index, Function Index, Scripting, Top
@unnumbered Concept Index
Bot::~Bot()
{
- // TODO: is it ok to delete iterators!?!
-
Person *p;
while (spyList.size() != 0) {
p = (*spyList.begin()).second;
void
Bot::addDCC(Person * from, unsigned long address, int port)
{
- DCCConnection * d = new DCCConnection(this, from->getAddress(),
+ DCCConnection * d = new DCCConnection(this, from->getAddress (),
address, port);
if (!d->connect())
logLine ("DCC Connection failed from " + from->getAddress ());
return;
}
- logLine ("DCC Connection worked!");
+ logLine ("DCC CHAT accepted from" + from->getAddress ());
dccConnections.push_back(d);
}
void
Bot::rehash()
{
- for (std::map<String, Channel *, std::less<String> >::iterator it = channelList->begin();
+ for (std::map<String, Channel *, std::less<String> >::iterator it =
+ channelList->begin();
it != channelList->end(); ++it)
serverConnection->queue->sendWho((*it).first);
}
ACTION, NICKNAME, SIGNOFF, CTCP, CTCP_REPLY,
DISCONNECT, FLOOD, INVITE, JOIN, KICK, LEAVE,
MODE, MESSAGE, NAMES, NOTICE, PUBLIC,
- PUBLIC_NOTICE, RAW, TIMER, TOPIC
+ PUBLIC_NOTICE, RAW, TIMER, TOPIC,
+ // send hooks
+ SEND_ACTION, SEND_CTCP, SEND_PUBLIC, SEND_MESSAGE
};
};
#include "DCCParser.H"
#include "DCCConnection.H"
-DCCConnection::DCCConnection(Bot *b, String n, unsigned long address, int port)
+DCCConnection::DCCConnection(Bot *b, String n, unsigned long address,
+ int port)
: Connection(address, port), bot(b), nuh(n),
lastSpoken(time(0)), autoRemove(true)
{ }
{
if (!socket.connect())
return false;
-
return true;
}
#include "Connection.H"
#include "Bot.H"
+#include "DCCPerson.H"
class DCCPerson;
class UserCommands;
// DCCParser.C -*- C++ -*-
// Copyright (c) 1998 Etienne BERNARD
+// Copyright (C) 2002 Clinton Ebadi
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// DCCPerson.C -*- C++ -*-
// Copyright (c) 1998 Etienne BERNARD
+// Copyright (C) 2002 Clinton Ebadi
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
scm_c_define ("hooks/raw", scm_long2num(Hook::RAW));
scm_c_define ("hooks/timer", scm_long2num(Hook::TIMER));
scm_c_define ("hooks/topic", scm_long2num(Hook::TOPIC));
+ scm_c_define ("hooks/send/public", scm_long2num (Hook::SEND_PUBLIC));
+ scm_c_define ("hooks/send/message", scm_long2num (Hook::SEND_MESSAGE));
+ scm_c_define ("hooks/send/action", scm_long2num (Hook::SEND_ACTION));
+ scm_c_define ("hooks/send/ctcp", scm_long2num (Hook::SEND_CTCP));
// procedures
}
void print_short_help (const char *name)
{
- std::cerr << "Usage: " << name << " [--help] [--version] [--no-background]\n\t"
+ std::cerr << "Usage: "
+ << name
+ << " [--help] [--version] [--no-background]\n\t"
<< "[--config-file file] [--config-dir dir] [--debug]\n\t"
<< "[--config dir-under-configpath]\n\t"
<< "[--sys-config dir-in-sysconfdir]\n\t"
<< "[--user-config dir-userdir] [--debug]\n"
<< "\n--help shows long help.\n";
- /* " -b Do not run in background.\n"
- " -f file Use file as config file.\n"
- " -d dir Use dir as current dir.\n"
- " -c config Use config as config\n"
- " -s config Use config as config (only search sysdir)\n"
- " -u config Use config as config (only search userdir)\n"
- " -D Debug mode (input/output printing and no background mode.\n";
- */
}
void print_long_help (const char *name)
String command = st.nextToken ();
String rest = st.rest ();
- /* for (int i = 0; functions[i].name != 0; i++)
- if (command == functions[i].name) {
- functions[i].function(cnx, from, rest);
- break;
- }
- */
+
if (fptr temp_func = functions[command])
temp_func (cnx, from, rest);
delete from;
// FIXME: debug DCC
st2.nextToken ();
unsigned long address =
- ntohl (strtoul ((const char *) st2.nextToken (), 0, 0));
+ strtoul ((const char *) st2.nextToken (), 0, 0);
int port = atoi ((const char *) st2.nextToken ());
if (port >= 1024 && Utils::getLevel (cnx->bot, from->getAddress ()))
cnx->bot->addDCC (from, address, port);
#include <map>
#include <string>
-// struct userFunctionsStruct {
-// // String name;
-// void (*function)(ServerConnection *, Person *, String, String);
-// int minLevel;
-// bool needsChannelName;
+typedef void (*fptr)(ServerConnection *, Person *, String);
+
+// fptr is a parser function which may either be Scheme or C. fptr
+// used to be what funptr is now, but I decided to make the bot even
+// more extensible and it is now a function-like object that can be
+// used just like it was a function pointer.
+
+// This will take a lot of work to make it actually work...lots of
+// SMOBs have to be written :(
+
+// class fptr
+// {
+// private:
+// typedef void (*funptr)(ServerConnection *, Person *, String);
+// union
+// {
+// funptr Cfunc;
+// #ifdef USESCRIPTS
+// SCM Sfunc;
+// #endif
+// };
+// bool C;
+
+// public:
+// ftpr () { Cfunc = 0; Sfunc = 0; C = true; };
+// operator= (funptr f) { Cfunc = f; C = true; };
+// operator= (SCM f) { Sfunc = f; C = false; };
+
+// operator ()(ServerConnection * s, Person * p, String str)
+// {
+// if (C)
+// Cfunc (s, p, str);
+// else {}
+// // ... SCM not supported for now
+// }
// };
class userFunction {
static void sendNotice(Person *, String);
private:
- typedef void (*fptr)(ServerConnection *, Person *, String);
static std::map<std::string, fptr, std::less<std::string> > functions;
};
String message)
{
sendPrivmsg(to, String("\001") + command + " " + message + "\001");
- // hook stuff (only get action for now)
- // I don't think it is useful to generate messages for other types
- // of CTCP stuff.
- puts (command);
#ifdef USESCRIPTS
if (command == "ACTION")
{
- Interp::bot->botInterp->RunHooks (Hook::ACTION,
- MNICK+ " " + to +
+ Interp::bot->botInterp->RunHooks (Hook::SEND_ACTION,
+ MNICK + " " + to +
" " + message,
scm_listify (Utils::
string2SCM (MNICK),
string2SCM (message),
SCM_UNDEFINED));
}
+ else
+ Interp::bot->botInterp->RunHooks (Hook::SEND_CTCP,
+ MNICK + " " + to + " " +
+ command + " " + message,
+ scm_listify (Utils::
+ string2SCM (MNICK),
+ Utils::
+ string2SCM (to),
+ Utils::
+ string2SCM
+ (command),
+ Utils::
+ string2SCM (message),
+ SCM_UNDEFINED));
#endif
}
#ifdef USESCRIPTS
if (message[0] != '\001')
if (Utils::isChannel (dest))
- Interp::bot->botInterp->RunHooks (Hook::PUBLIC,
+ Interp::bot->botInterp->RunHooks (Hook::SEND_PUBLIC,
Interp::bot->nickName + " " + dest +
" " + message,
scm_listify (Utils::
string2SCM
(message), SCM_UNDEFINED));
else
- Interp::bot->botInterp->RunHooks (Hook::MESSAGE,
+ Interp::bot->botInterp->RunHooks (Hook::SEND_MESSAGE,
Interp::bot->nickName + " " +
message,
scm_listify (Utils::
if (!file)
return;
- ++it; // We skip the bot's entry
+ // FIXME: fix bug (see BUGS) and inc once
+ ++it; ++it; // We skip the bot's entry
for ( ; it != l.end(); ++it)
if ((*it)->isStillValid()) {
file << (*it)->mask.getMask() << ":"