diff --git a/docs/DocBook/plugin-example.sgml b/docs/DocBook/plugin-example.sgml index 8f0987c84..310ae8b2b 100644 --- a/docs/DocBook/plugin-example.sgml +++ b/docs/DocBook/plugin-example.sgml @@ -36,6 +36,11 @@ 0.4 26 Feb 2004 Converted to use Supybot DTD + + + 0.5 + 4 Sep 2004 + Updated Docbook translation @@ -56,7 +61,6 @@ details. - Creating your own plugin @@ -105,7 +109,7 @@ functor% #!/usr/bin/env python ### -# Copyright (c) 2002, Jeremiah Fincher +# Copyright (c) 2004, Jeremiah Fincher # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -137,20 +141,24 @@ functor% Add the module docstring here. This will be used by the setup.py script. """ -from baseplugin import * +__revision__ = "$Id$" +__author__ = '' -import utils -import privmsgs -import callbacks +import supybot.plugins as plugins + +import supybot.conf as conf +import supybot.utils as utils +import supybot.privmsgs as privmsgs +import supybot.callbacks as callbacks def configure(onStart, afterConnect, advanced): - # This will be called by setup.py to configure this module. onStart and - # afterConnect are both lists. Append to onStart the commands you would - # like to be run when the bot is started; append to afterConnect the - # commands you would like to be run when the bot has finished connecting. + # This will be called by setup.py to configure this module. Advanced is + # a bool that specifies whether the user identified himself as an advanced + # user or not. You should effect your configuration by manipulating the + # registry as appropriate. from questions import expect, anything, something, yn - onStart.append('load Random') + conf.registerPlugin('Random', True) class Random(callbacks.Privmsg): pass @@ -193,13 +201,11 @@ Class = Random Then you see a configure function. This the function that's called when users decide to add your module in . You'll - note that by default it simply adds "load - Example" (where 'Example' is the name you provided - as the name of your plugin, so in our case it is - "load Random") at the bottom. For many - plugins this is all you need; for more complex plugins, you - might need to ask questions and add commands based on the - answers. + note that by default it simply registers the plugin to be + automatically loaded on startup. For many + plugins this is all you need; for more complex plugins, you + might need to ask questions and add commands based on the + answers. @@ -236,8 +242,8 @@ Class = Random def __init__(self): - self.rng = random.Random() - callbacks.Privmsg.__init__(self) + self.rng = random.Random() + callbacks.Privmsg.__init__(self) (rngis an abbreviation for "random number @@ -248,10 +254,8 @@ def __init__(self): any arguments (other than self, of course). There's no way anything will ever get to them! If you have some sort of initial values you need to get to your plugin - before it can do anything interesting, add a command that gets - those values. By convention, those commands begin with - "start" -- check out the Relay and Enforcer plugins for - examples of such commands. + before it can do anything interesting, you should get those + values from the registry. There's an easier way to get our plugin to have its own rng @@ -285,7 +289,7 @@ def __init__(self): Returns the next random number generated by the random number generator. """ - irc.reply(msg, str(self.rng.random())) + irc.reply(str(self.rng.random())) And that's it! Pretty simple, huh? Anyway, you're probably @@ -297,7 +301,7 @@ def __init__(self): What that does is define a command - random. You can call it by saying + random. You can call it by saying "@random" (or whatever prefix character your specific bot uses). The arguments are a bit less obvious. self is self-evident (hah!). @@ -308,22 +312,20 @@ def __init__(self): (with the exception of calling irc.reply or irc.error). What you're really interested in is the - args arg. That if a list of all the + args arg. That is a list of all the arguments passed to your command, pre-parsed and already evaluated (i.e., you never have to worry about nested commands, or handling double quoted strings, or splitting on - whitespace -- the work has already been done for you). You - can read about the Irc object in + whitespace – the work has already been done for you). + You can read about the Irc object in irclib.py (you won't find .reply or .error there, though, because you're actually getting an IrcObjectProxy, but that's beyond the level we want to describe here :)). You can read about the msg object in - ircmsgs.py. But again, aside from - calling irc.reply or - irc.error, you'll very rarely be using - these objects. + ircmsgs.py. But again, you'll very + rarely be using these objects. (In case you're curious, the answer is yes, you @@ -348,39 +350,28 @@ def __init__(self): this is what a supybot does: - <angryman> jemfinch: random takes no arguments (for more help - use the morehelp command) - <jemfinch> $morehelp random - <angryman> jemfinch: Returns the next random number from the - current random number generator. + <jemfinch> @help random + <angryman> jemfinch: (random takes no arguments) -- Returns the + next random number from the random number generator. - 'help <command>' replies with the command name followed - by the first line of the command's docstring; there should be - a blank line following, and then 'morehelp <command>' - will reply with the remainder of the docstring. So that - explains the docstring. Now on to the actual body of the - function: + Now on to the actual body of the function: irc.reply(msg, str(self.rng.random())) - irc.reply takes two arguments, an - IrcMsg (like the one passed into your - function) and a string. The IrcMsg is - used to determine who the reply should go to and whether or - not it should be sent in private message (commands sent in - private are replied to in private). The string is the reply - to be sent. Don't worry about length restrictions or anything - -- if the string you want to send is too big for an IRC + irc.reply simply takes one simple + argument: a string The string is the reply to be sent. Don't + worry about length restrictions or anything + – if the string you want to send is too big for an IRC message (and oftentimes that turns out to be the case :)) the - supybot framework handles that entirely transparently to you. + Supybot framework handles that entirely transparently to you. Do make sure, however, that you give irc.reply a string. It doesn't take anything else (sometimes even unicode fails!). That's why we have "str(self.rng.random())" instead of simply - "self.rng.random()" -- we had to give + "self.rng.random()" – we had to give irc.reply a string. @@ -406,15 +397,16 @@ def __init__(self): irc.error(msg, '<seed> must be a valid int or long.') return self.rng.seed(seed) - irc.reply(msg, conf.replySuccess) + irc.replySuccess() So this one's a bit more complicated. But it's still pretty - simple. The method name is "seed" so that'll be the command - name. The arguments are the same, the docstring is of the - same form, so we don't need to go over that again. The body - of the function, however, is significantly different. - + simple. The method name is seed so + that'll be the command name. The arguments are the same, the + docstring is of the same form, so we don't need to go over + that again. The body of the function, however, is + significantly different. + privmsgs.getArgs is a function you're going to be seeing a lot of when you write plugins for @@ -444,26 +436,25 @@ def __init__(self): irc.error. It has the same interface as we saw before in irc.reply, but it makes sure to remind the user that an error has been encountered - (currently, that means it puts "Error: " at the beginning of - the message). After erroring, we return. It's important to - remember this return here; otherwise, - we'll just keep going down through the function and try to use - this "seed" variable that never got assigned. A good general - rule of thumb is that any time you use - irc.error, you'll want to return - immediately afterwards. + (currently, that means it puts "Error: " at + the beginning of the message). After erroring, we return. + It's important to remember this return + here; otherwise, we'll just keep going down through the + function and try to use this seed variable + that never got assigned. A good general rule of thumb is that + any time you use irc.error, you'll want + to return immediately afterwards. - Then we set the seed -- that's a simple function on our rng - object. Assuming that succeeds (and doesn't raise an + Then we set the seed – that's a simple function on our + rng object. Assuming that succeeds (and doesn't raise an exception, which it shouldn't, because we already read the documentation and know that it should work) we reply to say that everything worked fine. That's what - conf.replySuccess says. By default, it has - the very dry (and appropriately robot-like) "The operation + irc.replySuccess says. By default, it + has the very dry (and appropriately robot-like) "The operation succeeded." but you're perfectly welcome to customize it - yourself -- conf.py was written to be - modified! + yourself – the registry was written to be modified! So that's a bit more complicated command. But we still @@ -529,14 +520,14 @@ def __init__(self): except IndexError: # raised by .pop(0) raise callbacks.ArgumentError except ValueError: - irc.error(msg, '<number of items> must be an integer.') + irc.error('<number of items> must be an integer.') return if n > len(args): - irc.error(msg, '<number of items> must be less than the number ' - 'of arguments.') + irc.error('<number of items> must be less than the number ' + 'of arguments.') return sample = self.rng.sample(args, n) - irc.reply(msg, utils.commaAndify(map(repr, sample))) + irc.reply(utils.commaAndify(map(repr, sample))) Most everything here is familiar. The difference between this @@ -592,8 +583,7 @@ def __init__(self): irc.error(msg, 'Dice have integer numbers of sides. Use one.') return s = 'rolls a %s' % self.rng.randrange(1, n+1) - irc.queueMsg(ircmsgs.action(ircutils.replyTo(msg), s)) - raise callbacks.CannotNest + irc.reply(s, action=True) There's a lot of stuff you haven't seen before in there. The @@ -611,35 +601,12 @@ def __init__(self): = 6", where we provide the default. - Later, though, you'll see something other than - irc.reply. This is - irc.queueMsg, the general interface for - sending messages to the server. It's what - irc.reply is using under the covers. It - takes an IrcMsg object. Fortunately, - that's exactly what's returned by - ircmsgs.action. An action message, just - in case you don't know, is a /me kind of message. - ircmsgs.action is a helper function that - takes a target (a place to send the message, either a channel - or a person) and a payload (the thing to /me) and returns the - appropriate IrcMsg object. - ircutils.replyTo simply takes an - IrcMsg and returns where we should - reply to; if the message was originally sent to a channel, - we'll reply to there, if it was originally sent to us - privately, we'll reply in private. + You'll also note that irc.reply was given + a keyword argument here, action. This + means that the reply is to be made as an action rather than a + normal reply. - - At the end, you might be surprised by the "raise - callbacks.CannotNest". That's used simply because at the - moment you can't nest actions (just like you can't nest - anything that doesn't go through - irc.reply). That raise just makes sure - the user finds this out if he tries to nest this like "@rot13 - [diceroll]". - - + So that's our plugin. 5 commands, each building in complexity. You should now be able to write most anything you want to do in Supybot. Except regexp-based plugins, but @@ -649,66 +616,18 @@ def __init__(self): - Finishing touches - - Let's take a look at that configure - function made - for us. Here it is, in case you've forgotten: - - -def configure(onStart, afterConnect, advanced): - # This will be called by setup.py to configure this module. onStart and - # afterConnect are both lists. Append to onStart the commands you would - # like to be run when the bot is started; append to afterConnect the - # commands you would like to be run when the bot has finished connecting. - from questions import expect, anything, something, yn - onStart.append('load Random') - - - You remember when you first started running supybot and ran - and it asked you - all those questions? Well, now's your chance to ask other - users some questions of your own. In our case, with our - Random plugin, it might be nice to offer - the user the ability to specify a seed to use whenever the - plugin is loaded. So let's ask him if he wants to do that, - and if so, let's ask him what the seed should be. - - -def configure(onStart, afterConnect, advanced): - # This will be called by setup.py to configure this module. onStart and - # afterConnect are both lists. Append to onStart the commands you would - # like to be run when the bot is started; append to afterConnect the - # commands you would like to be run when the bot has finished connecting. - from questions import expect, anything, something, yn - onStart.append('load Random') - if yn('Do you want to specify a seed to be used for the RNG')=='y': - seed = something('What seed? It must be an int or long.') - while not seed.isdigit(): - print 'That\'s not a valid seed.' - seed = something('What seed?') - onStart.append('seed %s' % seed) - - - As you can see, what the questions module - does is fairly self-evident: yn returns - either 'y' or 'n'; something returns - something (but not nothing; for nothing, - you'd want anything). So basically we - ask some questions until we get a good seed. Then we do this - "onStart.append('seed %s' % seed)" doohickey. - onStart is a list of the commands to run - when the bot starts; we're just throwing our little piece into - it. These commands will then be written into the template - creates for the bot. - - - We've written our own plugin from scratch (well, from the - boilerplate that we got from - :)) and - survived! Now go write more plugins for supybot, and send - them to me so I can use them too :) - - + Using the registry in your plugin + + TODO: Describe the registry and how to write a proper plugin + configure function. + + + + We've written our own plugin from scratch (well, from the + boilerplate that we got from + :)) and + survived! Now go write more plugins for supybot, and send + them to me so I can use them too :) + diff --git a/docs/PLUGIN-EXAMPLE b/docs/PLUGIN-EXAMPLE index 7e89eae4e..bd5e64efb 100644 --- a/docs/PLUGIN-EXAMPLE +++ b/docs/PLUGIN-EXAMPLE @@ -304,9 +304,9 @@ Then we set the seed -- that's a simple function on our rng object. Assuming that succeeds (and doesn't raise an exception, which it shouldn't, because we already read the documentation and know that it should work) we reply to say that everything worked fine. That's what -conf.replySuccess says. By default, it has the very dry (and +irc.replySuccess says. By default, it has the very dry (and appropriately robot-like) "The operation succeeded." but you're -perfectly welcome to customize it yourself -- conf.py was written to +perfectly welcome to customize it yourself -- the registry was written to be modified! So that's a bit more complicated command. But we still haven't dealt