textwidth=70 reformatting along with some documentation fixes that I noticed

This commit is contained in:
James Vega 2004-02-19 16:06:16 +00:00
parent 940a934f6e
commit 5deb99192e
7 changed files with 271 additions and 262 deletions

View File

@ -1,107 +1,118 @@
Ok, some some explanation of the capabilities system is probably in order. Ok, some some explanation of the capabilities system is probably in
With most IRC bots (including the ones I've written myself prior to this one) order. With most IRC bots (including the ones I've written myself
"what a user can do" is set in one of two ways. On the *really* simple bots, prior to this one) "what a user can do" is set in one of two ways. On
each user has a numeric "level" and commands check to see if a user has a "high the *really* simple bots, each user has a numeric "level" and commands
enough level" to perform some operation. On bots that are slightly more check to see if a user has a "high enough level" to perform some
complicated, users have a list of "flags" whose meanings are hardcoded, and the operation. On bots that are slightly more complicated, users have a
bot checks to see if a user possesses the necessary flag before performing some list of "flags" whose meanings are hardcoded, and the bot checks to
operation. Both methods, IMO, are rather arbitrary, and force the user and the see if a user possesses the necessary flag before performing some
programmer to be unduly confined to less expressive constructs. operation. Both methods, IMO, are rather arbitrary, and force the
user and the programmer to be unduly confined to less expressive
constructs.
This bot is different. Every user has a set of "capabilities" that is This bot is different. Every user has a set of "capabilities" that is
consulted every time they give the bot a command. Commands, rather than consulted every time they give the bot a command. Commands, rather
checking for a user level of 100, or checking if the user has an "o" flag, are than checking for a user level of 100, or checking if the user has an
instead able to check if a user has the "owner" capability. At this point such "o" flag, are instead able to check if a user has the "owner"
a difference might not seem revolutionary, but at least we can already tell capability. At this point such a difference might not seem
that this method is self-documenting, and easier for users and developers to revolutionary, but at least we can already tell that this method is
understand what's truly going on. self-documenting, and easier for users and developers to understand
what's truly going on.
If that was all, well, the capability system would be "cool", but not many If that was all, well, the capability system would be "cool", but not
people would say it was "awesome". But it *is* awesome! Several things are many people would say it was "awesome". But it *is* awesome! Several
happening behind the scene that make it awesome, and these are things that things are happening behind the scene that make it awesome, and these
couldn't happen if the bot was using numeric userlevels or single-character are things that couldn't happen if the bot was using numeric
flags. First, whenever a user issues the bot a command, the command dispatcher userlevels or single-character flags. First, whenever a user issues
checks to make sure the user doesn't have the "anticapability" for that the bot a command, the command dispatcher checks to make sure the user
command. An anticapability is a capability that, instead of saying "what a doesn't have the "anticapability" for that command. An anticapability
user can do", says what a user *cannot* do. It's formed rather simply by is a capability that, instead of saying "what a user can do", says
adding a dash ("-") to the beginning of a capability; "rot13" is what a user *cannot* do. It's formed rather simply by adding a dash
a capability, and "-rot13" is an anticapability. Anyway, when a user issues ("-") to the beginning of a capability; "rot13" is a capability, and
the bot a command, perhaps "calc" or "help", the bot first checks to make sure "-rot13" is an anticapability. Anyway, when a user issues the bot a
the user doesn't have the "-calc" or the "-help" capabilities before even command, perhaps "calc" or "help", the bot first checks to make sure
considering responding to the user. So commands can be turned on or off on a the user doesn't have the "-calc" or the "-help" capabilities before
*per user* basis, offering finegrained control not often (if at all!) seen in even considering responding to the user. So commands can be turned on
other bots. or off on a *per user* basis, offering finegrained control not often
(if at all!) seen in other bots.
But that's not all! The capabilities system also supports *Channel* But that's not all! The capabilities system also supports *Channel*
capabilities, which are capabilities that only apply to a specific channel; capabilities, which are capabilities that only apply to a specific
they're of the form "#channel.capability". Whenever a user issues a command to channel; they're of the form "#channel,capability". Whenever a user
the bot in a channel, the command dispatcher also checks to make sure the user issues a command to the bot in a channel, the command dispatcher also
doesn't have the anticapability for that command *in that channel*, and if the checks to make sure the user doesn't have the anticapability for that
user does, the bot won't respond to the user in the channel. Thus now, in command *in that channel*, and if the user does, the bot won't respond
addition to having the ability to turn individual commands on or off for an to the user in the channel. Thus now, in addition to having the
individual user, we can now turn commands on or off for an individual user on ability to turn individual commands on or off for an individual user,
an individual channel! we can now turn commands on or off for an individual user on an
individual channel!
So when a user "foo" sends a command "bar" to the bot on channel "#baz", first So when a user "foo" sends a command "bar" to the bot on channel
the bot checks to see if the user has the anticapability for the command by "#baz", first the bot checks to see if the user has the anticapability
itself, "-bar". If so, it returns right then and there, compltely ignoring the for the command by itself, "-bar". If so, it returns right then and
fact that the user issued that command to it. If the user doesn't have that there, compltely ignoring the fact that the user issued that command
anticapability, then the bot checks to see if the user issued the command over to it. If the user doesn't have that anticapability, then the bot
a channel, and if so, checks to see if the user has the antichannelcapability checks to see if the user issued the command over a channel, and if
for that command, "#baz.-bar". If so, again, he returns right then and there so, checks to see if the user has the antichannelcapability for that
and doesn't even think about responding to the bot. If neither of these command, "#baz,-bar". If so, again, he returns right then and there
anticapabilities are present, then the bot just responds to the user like and doesn't even think about responding to the bot. If neither of
normal. these anticapabilities are present, then the bot just responds to the
user like normal.
From a programmatical perspective, capabilties are easy to use and flexible. From a programmatical perspective, capabilties are easy to use and
Any command can check if a user has any capability, even ones not thought of flexible. Any command can check if a user has any capability, even
when the bot was originally written. Commands/Callbacks can add their own ones not thought of when the bot was originally written.
capabilities -- it's as easy as just checking for a capability and documenting Commands/Callbacks can add their own capabilities -- it's as easy as
somewhere that a user needs that capability to do something. just checking for a capability and documenting somewhere that a user
needs that capability to do something.
From an end-user perspective, capabilities remove a lot of the mystery and From an end-user perspective, capabilities remove a lot of the mystery
esotery of bot control, in addition to giving the user absolutely finegrained and esotery of bot control, in addition to giving the user absolutely
control over what users are allowed to do with the bot. Additionally, defaults finegrained control over what users are allowed to do with the bot.
can be set by the end-user for both individual channels and for the bot as a Additionally, defaults can be set by the end-user for both individual
whole, letting an end-user set the policy he wants the bot to follow for users channels and for the bot as a whole, letting an end-user set the
that haven't yet registered in his user database. policy he wants the bot to follow for users that haven't yet
It's really a revolution! registered in his user database. It's really a revolution!
There are several default capabilities the bot uses. The most important of There are several default capabilities the bot uses. The most
these is the "owner" capability. This capability allows the person having it important of these is the "owner" capability. This capability allows
to use *any* command. It's best to keep this capability reserved to people the person having it to use *any* command. It's best to keep this
who actually have access to the shell the bot is running on. capability reserved to people who actually have access to the shell
the bot is running on.
There is also the "admin" capability for non-owners that are highly trusted There is also the "admin" capability for non-owners that are highly
to administer the bot appropriately. They can do things such as change the trusted to administer the bot appropriately. They can do things such
bot's nick, globally enable/disable commands, cause the bot to ignore a given as change the bot's nick, globally enable/disable commands, cause the
user, set the prefixchar, report bugs, etc. They generally cannot do bot to ignore a given user, set the prefixchar, report bugs, etc.
administration related to channels, which is reserved for people with the They generally cannot do administration related to channels, which is
next capability. reserved for people with the next capability.
People who are to administer channels with the bot should have the #channel.op People who are to administer channels with the bot should have the
capability -- whatever channel they are to administrate, they should have that #channel,op capability -- whatever channel they are to administrate,
channel capability for "op". For example, since I want inkedmn to be an they should have that channel capability for "op". For example, since
administrator in #supybot, I'll give him the #supybot.op capability. This is I want inkedmn to be an administrator in #supybot, I'll give him the
in addition to his admin capability, since the admin capability doesn't give #supybot,op capability. This is in addition to his admin capability,
the person having it control over channels. #channel.op is used for such since the admin capability doesn't give the person having it control
things as giving/receiving ops, kickbanning people, lobotomizing the bot, over channels. #channel,op is used for such things as
ignoring users in the channel, and managing the channel capabilities. The giving/receiving ops, kickbanning people, lobotomizing the bot,
#channel.op capability is also basically the equivalent of the owner capability ignoring users in the channel, and managing the channel capabilities.
for capabilities involving #channel -- basically anyone with the #channel.op The #channel,op capability is also basically the equivalent of the
capability is considered to have all positive capabilities and no negative owner capability for capabilities involving #channel -- basically
capabilities for #channel. anyone with the #channel,op capability is considered to have all
positive capabilities and no negative capabilities for #channel.
One other globally important capability exists: "trusted". This is a command One other globally important capability exists: "trusted". This is a
that basically says "This user can be trusted not to try and crash the bot." command that basically says "This user can be trusted not to try and
It allows users to call commands like Math.icalc, which potentially could crash the bot." It allows users to call commands like Math.icalc,
cause the bot to begin a calculation that could potentially never return (a which potentially could cause the bot to begin a calculation that
calculation like 10**10**10**10). Another command that requires the trusted could potentially never return (a calculation like 10**10**10**10).
capability is Utilties.re, which (due to the regular expression implementation Another command that requires the trusted capability is Utilties.re,
in Python (and any other language that uses NFA regular expressions, like which (due to the regular expression implementation in Python (and any
Perl or Ruby or Lua or ...) which can allow a regular expression to take other language that uses NFA regular expressions, like Perl or Ruby or
exponential time to process). Consider what would happen if the someone gave Lua or ...) which can allow a regular expression to take exponential
the bot the command 're [strjoin "" s/./ [dict go] /] [dict go]' time to process). Consider what would happen if the someone gave the
bot the command 're [strjoin "" s/./ [dict go] /] [dict go]'
Other plugins may require different capabilities; the Factoids plugin requires Other plugins may require different capabilities; the Factoids plugin
#channel.factoids, the Topic plugin requires #channel.topic, etc. requires #channel,factoids, the Topic plugin requires #channel,topic,
etc.

View File

@ -40,8 +40,8 @@ our example.
Using the Config plugin, you can list the values in a subgroup and get Using the Config plugin, you can list the values in a subgroup and get
or set any of the values anywhere in the configuration hierarchy. For or set any of the values anywhere in the configuration hierarchy. For
example, let's say you wanted to see what configuration values were example, let's say you wanted to see what configuration values were
under the "supybot" (the base group) hierarchy. You would simply issue under the "supybot" (the base group) hierarchy. You would simply
this command: issue this command:
<jemfinch|lambda> @config list supybot <jemfinch|lambda> @config list supybot
<supybot> jemfinch|lambda: nick, ident, user, server, <supybot> jemfinch|lambda: nick, ident, user, server,
@ -80,25 +80,25 @@ modifying the configuration values. It's important to know that when
you provide the group argument to config list that you must always you provide the group argument to config list that you must always
provide the full name of the group. For example, "config list provide the full name of the group. For example, "config list
commands" would be incorrect, even though we see "commands" in the commands" would be incorrect, even though we see "commands" in the
listing above. Remember, we just shorten the names by the group listing above. Remember, we just shorten the names by the group we're
we're listing so we can fit more such names in a single message. In listing so we can fit more such names in a single message. In this
this case, that would be "supybot", so to list everything in case, that would be "supybot", so to list everything in the commands
the commands subgroup of supybot, we do: subgroup of supybot, we do:
<jemfinch|lambda> @config list supybot.commands <jemfinch|lambda> @config list supybot.commands
<supybot> jemfinch|lambda: defaultPlugins <supybot> jemfinch|lambda: defaultPlugins
Okay, now that you've used the Config plugin to list configuration Okay, now that you've used the Config plugin to list configuration
variables, it's time that we start looking at individual variables variables, it's time that we start looking at individual variables and
and their values. their values.
The first (and perhaps most important) thing you should know about The first (and perhaps most important) thing you should know about
each configuration variable is that they all have an associated help each configuration variable is that they all have an associated help
string to tell you what they represent. So the first command we'll string to tell you what they represent. So the first command we'll
cover is "config help". To see the help string for any value or cover is "config help". To see the help string for any value or
group, simply use the "config help" command. For example, to see group, simply use the "config help" command. For example, to see what
what this "supybot.prefixChars" configuration variable is all about, this "supybot.prefixChars" configuration variable is all about, we'd
we'd do this: do this:
<jemfinch|lambda> @config help supybot.prefixChars <jemfinch|lambda> @config help supybot.prefixChars
<supybot> jemfinch|lambda: Determines what prefix <supybot> jemfinch|lambda: Determines what prefix
@ -126,12 +126,11 @@ Now, check this out:
<jemfinch|lambda> $config supybot.prefixChars <jemfinch|lambda> $config supybot.prefixChars
<supybot> jemfinch|lambda: '@$' <supybot> jemfinch|lambda: '@$'
Note that we used $ as our prefix character, and that the value of Note that we used $ as our prefix character, and that the value of the
the configuration variable changed. If I were to use the "flush" configuration variable changed. If I were to use the "flush" command
command now, this change would be flushed to the registry file on now, this change would be flushed to the registry file on disk (this
disk (this would also happen if I made the bot quit, or pressed would also happen if I made the bot quit, or pressed Ctrl-C in the
Ctrl-C in the terminal the bot was running in). Instead, I'll terminal the bot was running in). Instead, I'll revert the change:
revert the change:
<jemfinch|lambda> $config supybot.prefixChars @ <jemfinch|lambda> $config supybot.prefixChars @
<supybot> jemfinch|lambda: The operation succeeded. <supybot> jemfinch|lambda: The operation succeeded.
@ -153,9 +152,9 @@ simply say:
Simple, eh? Simple, eh?
Now, let's say you want to find all configuration variables that Now, let's say you want to find all configuration variables that might
might be even remotely related to opping. For that, you'll want the be even remotely related to opping. For that, you'll want the "config
"config search" command. Check this out: search" command. Check this out:
<jemfinch|lambda> @config search op <jemfinch|lambda> @config search op
<supybot> jemfinch|lambda: <supybot> jemfinch|lambda:
@ -174,16 +173,16 @@ there's no way for the bot to know what configuration variables it
registers. registers.
Some people might like editing their registry file directly rather Some people might like editing their registry file directly rather
than manipulating all these things through the bot. For those than manipulating all these things through the bot. For those people,
people, we offer the "config reload" command, which reloads both we offer the "config reload" command, which reloads both registry
registry configuration and user/channel/ignore database configuration and user/channel/ignore database configuration. Just
configuration. Just edit the interesting files and then give the bot edit the interesting files and then give the bot the "config reload"
the "config reload" command and it'll work as expected. Do note, command and it'll work as expected. Do note, however, that Supybot
however, that Supybot flushes his configuration files and databases flushes his configuration files and databases to disk every hour or
to disk every hour or so, and if this happens after you've edited so, and if this happens after you've edited your configuration files
your configuration files but before you reload your changes, you but before you reload your changes, you could lose the changes you
could lose the changes you made. To prevent this, set the made. To prevent this, set the supybot.flush value to Off, and no
supybot.flush value to Off, and no automatic flushing will occur. automatic flushing will occur.
Anyway, that's about it for configuration. Have fun, and enjoy your Anyway, that's about it for configuration. Have fun, and enjoy your
configurable bot! configurable bot!

View File

@ -111,10 +111,10 @@ You'll probably want to change the copyright notice to be your name.
It wouldn't stick even if you kept my name, so you might as well :) It wouldn't stick even if you kept my name, so you might as well :)
Describe what you want the plugin to do in the docstring. This is Describe what you want the plugin to do in the docstring. This is
used in supybot-wizard in order to explain to the user the purpose used in supybot-wizard in order to explain to the user the purpose of
of the module. It's also returned when someone asks the bot for help the module. It's also returned when someone asks the bot for help for
for a given module (instead of help for a certain command). We'll a given module (instead of help for a certain command). We'll change
change this one to "Lots of stuff relating to random numbers." this one to "Lots of stuff relating to random numbers."
Then there are the imports. The callbacks module is used (the class Then there are the imports. The callbacks module is used (the class
you're given subclasses callbacks.Privmsg) but the privmsgs module you're given subclasses callbacks.Privmsg) but the privmsgs module
@ -176,8 +176,8 @@ And we save two lines of code and make our code a little more clear :)
Now that we have an RNG, we need some way to get random numbers. So Now that we have an RNG, we need some way to get random numbers. So
first, we'll add a command that simply gets the next random number and first, we'll add a command that simply gets the next random number and
gives it back to the user. It takes no arguments, of course (what gives it back to the user. It takes no arguments, of course (what
would you give it?). Here's the command, and I'll follow that with the would you give it?). Here's the command, and I'll follow that with
explanation of what each part means. the explanation of what each part means.
def random(self, irc, msg, args): def random(self, irc, msg, args):
"""takes no arguments """takes no arguments
@ -342,9 +342,9 @@ checking for missing arguments and whatnot so we don't have to.
The Random object we're using offers us a "sample" method that takes a The Random object we're using offers us a "sample" method that takes a
sequence and a number (we'll call it N) and returns a list of N items sequence and a number (we'll call it N) and returns a list of N items
taken randomly from the sequence. So I'll show you an example that taken randomly from the sequence. So I'll show you an example that
takes advantage of multiple arguments but doesn't use takes advantage of multiple arguments but doesn't use privmsgs.getArgs
privmsgs.getArgs (and thus has to handle its own errors if the number (and thus has to handle its own errors if the number of arguments
of arguments isn't right). Here's the code: isn't right). Here's the code:
def sample(self, irc, msg, args): def sample(self, irc, msg, args):
"""<number of items> [<text> ...] """<number of items> [<text> ...]
@ -373,12 +373,11 @@ than through getArgs. Since we already have the arguments in a list,
it doesn't make any sense to have privmsgs.getArgs smush them all it doesn't make any sense to have privmsgs.getArgs smush them all
together into a big long string that we'll just have to re-split. But together into a big long string that we'll just have to re-split. But
we still want the nice error handling of privmsgs.getArgs. So what do we still want the nice error handling of privmsgs.getArgs. So what do
we do? We raise callbacks.ArgumentError! That's the secret juju we do? We raise callbacks.ArgumentError! That's the secret juju that
that privmsgs.getArgs is doing; now we're just doing it ourself. privmsgs.getArgs is doing; now we're just doing it ourself. Someone
Someone up our callchain knows how to handle it so a neat error up our callchain knows how to handle it so a neat error message is
message is returned. So in this function, if .pop(0) fails, we returned. So in this function, if .pop(0) fails, we weren't given
weren't given enough arguments and thus need to tell the user how to enough arguments and thus need to tell the user how to call us.
call us.
So we have the args, we have the number, we do a simple call to So we have the args, we have the number, we do a simple call to
random.sample and then we do this funky utils.commaAndify to it. random.sample and then we do this funky utils.commaAndify to it.
@ -417,8 +416,8 @@ important, though, is the first thing you'll notice that's different:
the privmsg.getArgs call. Here we're offering a default argument in the privmsg.getArgs call. Here we're offering a default argument in
case the user is too lazy to supply one (or just wants a nice, case the user is too lazy to supply one (or just wants a nice,
standard six-sided die :)) privmsgs.getArgs supports that; we'll just standard six-sided die :)) privmsgs.getArgs supports that; we'll just
tell it that we don't *need* any arguments (via required=0) and that we tell it that we don't *need* any arguments (via required=0) and that
*might like* one argument (optional=1). If the user provides an we *might like* one argument (optional=1). If the user provides an
argument, we'll get it -- if they don't, we'll just get an empty argument, we'll get it -- if they don't, we'll just get an empty
string. Hence the "if not n: n = 6", where we provide the default. string. Hence the "if not n: n = 6", where we provide the default.

View File

@ -4,13 +4,12 @@ Q: Why does my bot not recognize me or tell me that I don't have the
A: Because you're not given it anything to recognize you from! A: Because you're not given it anything to recognize you from!
You'll need to identify with the bot ("help identify" to see how You'll need to identify with the bot ("help identify" to see how
that works) or add your hostmask to your user record ("help that works) or add your hostmask to your user record ("help
addhostmask" to see how that works) for it to know that you're addhostmask" to see how that works) for it to know that you're you.
you. You may wish to note that addhostmask can accept a password; You may wish to note that addhostmask can accept a password; rather
rather than identify, you can send the command "addhostmask than identify, you can send the command "addhostmask myOwnerUser
myOwnerUser [hostmask] myOwnerUserPassword" and the bot will add [hostmask] myOwnerUserPassword" and the bot will add your current
your current hostmask to your owner user (of course, you should hostmask to your owner user (of course, you should change
change myOwnerUser and myOwnerUserPassword appropriately for your myOwnerUser and myOwnerUserPassword appropriately for your bot).
bot).
Q: How do I make my Supybot op my users? Q: How do I make my Supybot op my users?
@ -21,10 +20,10 @@ A: First, you'll have to make sure that your users register with the
Use the "channel addcapability" command to do this. After that, Use the "channel addcapability" command to do this. After that,
your users should be able to use the "op" command to get ops. your users should be able to use the "op" command to get ops.
If you want your users to be auto-opped when they join the If you want your users to be auto-opped when they join the channel,
channel, you'll need to load the Enforcer plugin and turn its you'll need to load the Enforcer plugin and turn its autoOp
autoOp configuration variable on. Use the "config" command to do configuration variable on. Use the "config" command to do so.
so. Here's an example of how to do these steps: Here's an example of how to do these steps:
<jemfinch|lambda> I'm going to make an example session for giving <jemfinch|lambda> I'm going to make an example session for giving
you auto-ops, for our FAQ. you auto-ops, for our FAQ.
@ -60,12 +59,11 @@ Q: Can users with the "admin" capability change configuration
A: Currently, no. Since this is the first release of Supybot that A: Currently, no. Since this is the first release of Supybot that
uses the registry, we wanted to stay on the conservative side and uses the registry, we wanted to stay on the conservative side and
require the "owner" capability for changing all require the "owner" capability for changing all non-channel-related
non-channel-related configuration variables. Feel free to make configuration variables. Feel free to make your case to us as to
your case to us as to why a certain configuration variable should why a certain configuration variable should only require the
only require the "admin" capability instead of the "owner" "admin" capability instead of the "owner" capability, and if we
capability, and if we agree with you, we'll change it for the next agree with you, we'll change it for the next release.
release.
Q: How do I make my Supybot connect to multiple servers? Q: How do I make my Supybot connect to multiple servers?
@ -75,10 +73,10 @@ A: You'll need to use the Relay plugin. As long as you don't call
channels (even if the bot is on the same channel on different channels (even if the bot is on the same channel on different
networks). In order to use the Relay plugin, you'll want to first networks). In order to use the Relay plugin, you'll want to first
call the "relay start" command, followed by the "relay connect" call the "relay start" command, followed by the "relay connect"
command. These commands are (unfortunately) not persistent at command. These commands are (unfortunately) not persistent at this
this time, so you'll need to give them to the bot anytime you time, so you'll need to give them to the bot anytime you start it
start it up. We'll probably have this lack of persistence up. We'll probably have this lack of persistence rectified before
rectified before the next release. the next release.
Q: Can Supybot do factoids? Q: Can Supybot do factoids?
@ -89,10 +87,10 @@ A: Supybot most certainly can! In fact, we offer two full-fledged
Factoids (written by jemfinch) is Supybot's original Factoids (written by jemfinch) is Supybot's original
factoids-related plugin. It offers full integration with Supybot's factoids-related plugin. It offers full integration with Supybot's
nested commands as well as a complete 1:n key to factoid ratio, nested commands as well as a complete 1:n key to factoid ratio,
with lookup by individual number. Factoids also uses with lookup by individual number. Factoids also uses a
a channel-specific database instead of a global database, although channel-specific database instead of a global database, although in
in the future it will likely be a configuration option whether to the future it will likely be a configuration option whether to use
use channel-specific or global databases for such plugins. channel-specific or global databases for such plugins.
MoobotFactoids (written by Strike) is much more full-featured, MoobotFactoids (written by Strike) is much more full-featured,
offering users the ability to define factoids in a slightly more offering users the ability to define factoids in a slightly more
@ -103,11 +101,10 @@ A: Supybot most certainly can! In fact, we offer two full-fledged
beginning)). If you're accustomed to Moobot's factoids or beginning)). If you're accustomed to Moobot's factoids or
Blootbot's factoids, then this is the Factoids plugin for you. Blootbot's factoids, then this is the Factoids plugin for you.
Unfortunately, due to the more natural definition syntax (required Unfortunately, due to the more natural definition syntax (required
to be compatible with Moobot) you can't define Factoids with to be compatible with Moobot) you can't define Factoids with nested
nested commands; you'll have to evaluate the command first and commands; you'll have to evaluate the command first and then copy
then copy the result into your factoid definition. MoobotFactoids the result into your factoid definition. MoobotFactoids uses a
uses a global database, so the factoids are the same for all global database, so the factoids are the same for all channels.
channels.
In the future, we plan to have a compatibility plugin for Infobot, In the future, we plan to have a compatibility plugin for Infobot,
but as of present we've not yet written one. but as of present we've not yet written one.
@ -119,9 +116,9 @@ A: As of present, we have no automated way to do so. Strike has
written a few scripts for importing a Moobot database into written a few scripts for importing a Moobot database into
MoobotFactoids, however, so you'll want to talk to him about MoobotFactoids, however, so you'll want to talk to him about
helping you with that. We're certainly happy to help you convert helping you with that. We're certainly happy to help you convert
such databases; if you can provide us with such a database such databases; if you can provide us with such a database exported
exported to a flat file, we can probably do the rest of the work to a flat file, we can probably do the rest of the work to write a
to write a script that imports it into a database for one of our script that imports it into a database for one of our
factoids-related plugins. factoids-related plugins.
@ -142,8 +139,8 @@ A: Submit it on Sourceforge through our Sourceforge project page:
log entry). We'd also like to see the commands that caused the log entry). We'd also like to see the commands that caused the
bug, or happened around the time you saw the bug. If the bug bug, or happened around the time you saw the bug. If the bug
involved a database, we'd love to see the database. Remember, it's involved a database, we'd love to see the database. Remember, it's
always worse to send us too much information in a bug report than always worse to send us too little information in a bug report than
too little. too much.
Q: Karma doesn't seem to work for me. Q: Karma doesn't seem to work for me.

View File

@ -28,15 +28,15 @@ like being asked many questions, just run supybot with no arguments
and it'll ask you only the questions necessary to run a bot. and it'll ask you only the questions necessary to run a bot.
So after running either of those two programs, you've got a nice So after running either of those two programs, you've got a nice
registry file handy. If you're not satisfied with your answers to registry file handy. If you're not satisfied with your answers to any
any of the questions you were asked, feel free to run the program of the questions you were asked, feel free to run the program again
again until you're satisfied with all your answers. Once you're until you're satisfied with all your answers. Once you're satisfied,
satisfied, though, run the "supybot" program with the registry file though, run the "supybot" program with the registry file you created
you created as an argument. This will start the bot; unless you as an argument. This will start the bot; unless you turned off
turned off logging to stdout, you'll see some nice log messages logging to stdout, you'll see some nice log messages describing what
describing what the bot is doing at any particular moment; it may the bot is doing at any particular moment; it may pause for a
pause for a significant amount of time after saying "Connecting significant amount of time after saying "Connecting to ..." while the
to ..." while the server tries to check its ident. server tries to check its ident.
Ok, so let's assume your bot connected to the server fine and joined Ok, so let's assume your bot connected to the server fine and joined
the channels you told it to join. For now we'll assume you named your the channels you told it to join. For now we'll assume you named your
@ -60,16 +60,14 @@ supybot: list Misc
Will list all the commands in the Misc plugin. If you want to see the Will list all the commands in the Misc plugin. If you want to see the
help for any command, just use the help command: help for any command, just use the help command:
supybot: help help supybot: help help supybot: help list supybot: help load
supybot: help list
supybot: help load
Sometimes more than one plugin will have a given command; for Sometimes more than one plugin will have a given command; for
instance, the "list" command exists in both the Misc and Config instance, the "list" command exists in both the Misc and Config
plugins (both loaded by default). List, in this case, defaults to plugins (both loaded by default). List, in this case, defaults to the
the Misc plugin, but you may want to get the help for the list Misc plugin, but you may want to get the help for the list command in
command in the Config plugin. In that case, you'll want to give your the Config plugin. In that case, you'll want to give your command
command like this: like this:
supybot: help config list supybot: help config list
@ -149,12 +147,12 @@ try is to look at the listing of configuration groups for the bot
chunk. When you invoke this command, you should see output like: chunk. When you invoke this command, you should see output like:
<supybot> nick, ident, user, server, password, channels, prefixChars, <supybot> nick, ident, user, server, password, channels, prefixChars,
defaultCapabilities, defaultAllow, defaultIgnore, defaultCapabilities, defaultAllow, defaultIgnore,
humanTimestampFormat, externalIP, bracketSyntax, pipeSyntax, humanTimestampFormat, externalIP, bracketSyntax, pipeSyntax,
followIdentificationThroughNickChanges, alwaysJoinOnInvite, followIdentificationThroughNickChanges, alwaysJoinOnInvite,
showSimpleSyntax, maxHistoryLength, nickmods, throttleTime, showSimpleSyntax, maxHistoryLength, nickmods, throttleTime,
snarfThrottle, threadAllCommands, pingServer, pingInterval, snarfThrottle, threadAllCommands, pingServer, pingInterval,
upkeepInterval, flush, (1 more message) upkeepInterval, flush, (1 more message)
Now, to see the rest of the output, simply give the command "more", Now, to see the rest of the output, simply give the command "more",
and it will show you the rest: and it will show you the rest:

View File

@ -1,26 +1,26 @@
So here's a general *programming* introduction to what the different modules do So here's a general *programming* introduction to what the different
and what services they provide. It is, however, only an introduction. Read modules do and what services they provide. It is, however, only an
the modules themselves for a much more detailed explanation :) introduction. Read the modules themselves for a much more detailed
explanation :)
fix.py: Stuff that Python should (but doesn't) include by default. fix.py: Stuff that Python should (but doesn't) include by default.
cdb.py: A constant database library, translated from C (and my O'Caml version) cdb.py: A constant database library, translated from C (and my O'Caml
More information available at http://cr.yp.to/cdb.html. Not version) More information available at
currently used since we switched to PySQLite. http://cr.yp.to/cdb.html. Not currently used since we
switched to PySQLite.
ansi.py: Contains different ANSI color sequences. ansi.py: Contains different ANSI color sequences.
Mostly used by the debug module. Mostly used by the debug module.
debug.py: Functions for handling bugs and errors in a consistent manner conf.py: The configuration file for the bot -- it sets a lot of
throughout the bot. variables that other modules check for when they have
questions about what they need to do.
conf.py: The configuration file for the bot -- it sets a lot of variables that world.py: Just a dropping off place for some globals that need to be
other modules check for when they have questions about what they need shared among all the modules. It's obviously not used *a
to do. lot*, but some things seem to fit better here than anywhere
else.
world.py: Just a dropping off place for some globals that need to be shared
among all the modules. It's obviously not used *a lot*, but some
things seem to fit better here than anywhere else.
template.py: A template used by setup.py to create customized runnable template.py: A template used by setup.py to create customized runnable
Python scripts for individual bots. Python scripts for individual bots.
@ -28,21 +28,22 @@ template.py: A template used by setup.py to create customized runnable
privmsgs.py: Basic stuff relating to callbacks.Privmsg, the base class privmsgs.py: Basic stuff relating to callbacks.Privmsg, the base class
for most plugins. for most plugins.
callbacks.py: A few basic callbacks providing significant functionality. callbacks.py: A few basic callbacks providing significant
You'll likely be inheriting from Privmsg quite a bit. functionality. You'll likely be inheriting from Privmsg
quite a bit.
ircdb.py: The users and channels databases are here, as well as the ircdb.py: The users and channels databases are here, as well as the
IrcUser and IrcChannel classes. Look here when you want to IrcUser and IrcChannel classes. Look here when you want to
restrict a command to only certain users. restrict a command to only certain users.
irclib.py: Provides the most important class in the irclib, Irc. It represents irclib.py: Provides the most important class in the irclib, Irc. It
a connection to an IRC server, but it's entirely separate from the represents a connection to an IRC server, but it's entirely
network implementation. It's connected to the network (or whatever separate from the network implementation. It's connected
else drives it) by a "driver" module which uses its feedMsg/takeMsg to the network (or whatever else drives it) by a "driver"
functions. module which uses its feedMsg/takeMsg functions.
drivers.py: The baseclass (IrcDriver) for various drivers to drive irclib.Irc drivers.py: The baseclass (IrcDriver) for various drivers to drive
classes. Also, the driving mechanism. irclib.Irc classes. Also, the driving mechanism.
asyncoreDrivers.py: The asyncore-based drivers for use with the bot. asyncoreDrivers.py: The asyncore-based drivers for use with the bot.
@ -52,12 +53,12 @@ socketDrivers.py: The plain old socket-based drivers for use with the
twistedDrivers.py: The Twisted <http://www.twistedmatrix.com/> drivers twistedDrivers.py: The Twisted <http://www.twistedmatrix.com/> drivers
for use with the bot. for use with the bot.
ircmsgs.py: The IrcMsg class (get to know it :)) and various functions for ircmsgs.py: The IrcMsg class (get to know it :)) and various functions
making the creation of IrcMsgs easier. for making the creation of IrcMsgs easier.
ircutils.py: Various utility functions for Irc -- read the module to see what ircutils.py: Various utility functions for Irc -- read the module to
goodies are there :) see what goodies are there :)
schedule.py: A schedule driver (which is automatically registered with the schedule.py: A schedule driver (which is automatically registered with
drivers module) to run things at a particular time, or at the drivers module) to run things at a particular time,
specified periods of time. or at specified periods of time.

View File

@ -1,5 +1,5 @@
Read PEP 8 (Guido's Style Guide) and know that we use almost all the same style Read PEP 8 (Guido's Style Guide) and know that we use almost all the
guidelines. same style guidelines.
Maximum line length is 79 characters. 78 is a safer bet, though. Maximum line length is 79 characters. 78 is a safer bet, though.
@ -10,29 +10,31 @@ They're just easier to type.
Triple double quotes (""") are always used for docstrings. Triple double quotes (""") are always used for docstrings.
Spaces go around all operators (except around '=' in default arguments to Spaces go around all operators (except around '=' in default arguments
functions) and after all commas (unless doing so keeps a line within the 79 to functions) and after all commas (unless doing so keeps a line
character limit). within the 79 character limit).
Class names are StudlyCaps. Method and function names are camelCaps Class names are StudlyCaps. Method and function names are camelCaps
(StudlyCaps with an initial lowercase letter). If variable and attribute (StudlyCaps with an initial lowercase letter). If variable and
names can maintain readability without being camelCaps, then they should be attribute names can maintain readability without being camelCaps, then
entirely in lowercase, otherwise they should also use camelCaps. Plugin names they should be entirely in lowercase, otherwise they should also use
are StudlyCaps. camelCaps. Plugin names are StudlyCaps.
Imports should always happen at the top of the module, one import per line Imports should always happen at the top of the module, one import per
(so if imports need to be added or removed later, it can be done easily). line (so if imports need to be added or removed later, it can be done
easily).
A blank line should be between all consecutive method declarations in a A blank line should be between all consecutive method declarations in
class definition. Two blank lines should be between all consecutive class a class definition. Two blank lines should be between all consecutive
definitions in a file. Comments are even better than blank lines for class definitions in a file. Comments are even better than blank
separating classes. lines for separating classes.
Database filenames should generally begin with the name of the plugin and the Database filenames should generally begin with the name of the plugin
extension should be 'db'. baseplugin.DBHandler does this already. and the extension should be 'db'. baseplugin.DBHandler does this
already.
Whenever creating a file descriptor or socket, keep a reference around and be Whenever creating a file descriptor or socket, keep a reference around
sure to close it. There should be no code like this: and be sure to close it. There should be no code like this:
s = urllib2.urlopen('url').read() s = urllib2.urlopen('url').read()
Instead, do this: Instead, do this:
fd = urllib2.urlopen('url') fd = urllib2.urlopen('url')
@ -44,31 +46,33 @@ All plugins should include a docstring decsribing what the plugin
does. This docstring will be returned when the user wants help on a does. This docstring will be returned when the user wants help on a
plugin. plugin.
Method docstrings in classes deriving from callbacks.Privmsg should include an Method docstrings in classes deriving from callbacks.Privmsg should
argument list as their first line, and after that a blank line followed by include an argument list as their first line, and after that a blank
a longer description of what the command does. The argument list is used by line followed by a longer description of what the command does. The
the 'syntax' command, and the longer description is used by the 'help' command. argument list is used by the 'syntax' command, and the longer
description is used by the 'help' command.
Whenever joining more than two strings, use string interpolation, not addition: Whenever joining more than two strings, use string interpolation, not
addition:
s = x + y + z # Bad. s = x + y + z # Bad.
s = '%s%s%s' % (x, y, z) # Good. s = '%s%s%s' % (x, y, z) # Good.
s = ''.join([x, y, z]) # Best, but not as general. s = ''.join([x, y, z]) # Best, but not as general.
This has to do with efficiency; the intermediate string x+y is made (and thus This has to do with efficiency; the intermediate string x+y is made
copied) before x+y+z is made, so it's less efficient. (and thus copied) before x+y+z is made, so it's less efficient.
When writing strings that have formatting characters in them, don't When writing strings that have formatting characters in them, don't
use anything but %s unless you absolutely must. In particular, %d use anything but %s unless you absolutely must. In particular, %d
should never be used, it's less general than %s and serves no useful should never be used, it's less general than %s and serves no useful
purpose. purpose.
Use the debug module to its fullest; when you need to print some values to Use the log module to its fullest; when you need to print some values
debug, use debug.printf to do so, and leave those print statements in the code to debug, use self.log.debug to do so, and leave those print
(commented out) so they can later be re-enabled. Remember that once statements in the code (commented out) so they can later be
code is buggy, it tends to have more bugs, and you'll probably need those print re-enabled. Remember that once code is buggy, it tends to have more
statements again. bugs, and you'll probably need those print statements again.
SQL table names should be all-lowercase and include underscores to separate SQL table names should be all-lowercase and include underscores to
words. This is because SQL itself is case-insensitive. separate words. This is because SQL itself is case-insensitive.
SQL statements in code should put SQL words in ALL CAPS: SQL statements in code should put SQL words in ALL CAPS:
SELECT quote FROM quotes ORDER BY random() LIMIT 1 SELECT quote FROM quotes ORDER BY random() LIMIT 1