mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-12-23 03:02:52 +01:00
More updating Docbook stuff
This commit is contained in:
parent
a4b3d66748
commit
b5d7045359
@ -36,6 +36,11 @@
|
||||
<revnumber>0.4</revnumber>
|
||||
<date>26 Feb 2004</date>
|
||||
<revremark>Converted to use Supybot DTD</revremark>
|
||||
</revision>
|
||||
<revision>
|
||||
<revnumber>0.5</revnumber>
|
||||
<date>4 Sep 2004</date>
|
||||
<revremark>Updated Docbook translation</revremark>
|
||||
</revision>
|
||||
</revhistory>
|
||||
</articleinfo>
|
||||
@ -56,7 +61,6 @@
|
||||
details.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Creating your own plugin</title>
|
||||
<sect2>
|
||||
@ -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 <function>configure</function> function. This
|
||||
the function that's called when users decide to add your
|
||||
module in <script>scripts/setup.py</script>. You'll
|
||||
note that by default it simply adds <literal>"load
|
||||
Example"</literal> (where 'Example' is the name you provided
|
||||
as the name of your plugin, so in our case it is
|
||||
<literal>"load Random"</literal>) 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.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
@ -236,8 +242,8 @@ Class = Random
|
||||
</para>
|
||||
<programlisting>
|
||||
def __init__(self):
|
||||
self.rng = random.Random()
|
||||
callbacks.Privmsg.__init__(self)
|
||||
self.rng = random.Random()
|
||||
callbacks.Privmsg.__init__(self)
|
||||
</programlisting>
|
||||
<para>
|
||||
(<varname>rng</varname>is an abbreviation for "random number
|
||||
@ -248,10 +254,8 @@ def __init__(self):
|
||||
any arguments (other than <varname>self</varname>, 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.
|
||||
</para>
|
||||
<para>
|
||||
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()))
|
||||
</programlisting>
|
||||
<para>
|
||||
And that's it! Pretty simple, huh? Anyway, you're probably
|
||||
@ -297,7 +301,7 @@ def __init__(self):
|
||||
</programlisting>
|
||||
<para>
|
||||
What that does is define a command
|
||||
<function>random</function>. You can call it by saying
|
||||
<botcommand>random</botcommand>. You can call it by saying
|
||||
"@random" (or whatever prefix character your specific bot
|
||||
uses). The arguments are a bit less obvious.
|
||||
<varname>self</varname> is self-evident (hah!).
|
||||
@ -308,22 +312,20 @@ def __init__(self):
|
||||
(with the exception of calling <function>irc.reply</function>
|
||||
or <function>irc.error</function>). What you're
|
||||
<emphasis>really</emphasis> interested in is the
|
||||
<varname>args</varname> arg. That if a list of all the
|
||||
<varname>args</varname> 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 <classname>Irc</classname> object in
|
||||
whitespace – the work has already been done for you).
|
||||
You can read about the <classname>Irc</classname> object in
|
||||
<filename>irclib.py</filename> (you won't find
|
||||
<function>.reply</function> or <function>.error</function>
|
||||
there, though, because you're actually getting an
|
||||
<classname>IrcObjectProxy</classname>, but that's beyond the
|
||||
level we want to describe here :)). You can read about the
|
||||
<varname>msg</varname> object in
|
||||
<filename>ircmsgs.py</filename>. But again, aside from
|
||||
calling <function>irc.reply</function> or
|
||||
<function>irc.error</function>, you'll very rarely be using
|
||||
these objects.
|
||||
<filename>ircmsgs.py</filename>. But again, you'll very
|
||||
rarely be using these objects.
|
||||
</para>
|
||||
<para>
|
||||
(In case you're curious, the answer is yes, you
|
||||
@ -348,39 +350,28 @@ def __init__(self):
|
||||
this is what a supybot does:
|
||||
</para>
|
||||
<ircsession>
|
||||
<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.
|
||||
</ircsession>
|
||||
<para>
|
||||
'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:
|
||||
</para>
|
||||
<programlisting>
|
||||
irc.reply(msg, str(self.rng.random()))
|
||||
</programlisting>
|
||||
<para>
|
||||
<function>irc.reply</function> takes two arguments, an
|
||||
<classname>IrcMsg</classname> (like the one passed into your
|
||||
function) and a string. The <classname>IrcMsg</classname> 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
|
||||
<function>irc.reply</function> 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
|
||||
<function>irc.reply</function> 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
|
||||
<function>irc.reply</function> a string.
|
||||
</para>
|
||||
<para>
|
||||
@ -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()
|
||||
</programlisting>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
simple. The method name is <botcommand>seed</botcommand> 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.
|
||||
</para>
|
||||
<para>
|
||||
<function>privmsgs.getArgs</function> is a function you're
|
||||
going to be seeing a lot of when you write plugins for
|
||||
@ -444,26 +436,25 @@ def __init__(self):
|
||||
<function>irc.error</function>. It has the same interface as
|
||||
we saw before in <function>irc.reply</function>, 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 <keyword>return</keyword> 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
|
||||
<function>irc.error</function>, you'll want to return
|
||||
immediately afterwards.
|
||||
(currently, that means it puts <literal>"Error: "</literal> at
|
||||
the beginning of the message). After erroring, we return.
|
||||
It's important to remember this <keyword>return</keyword>
|
||||
here; otherwise, we'll just keep going down through the
|
||||
function and try to use this <varname>seed</varname> variable
|
||||
that never got assigned. A good general rule of thumb is that
|
||||
any time you use <function>irc.error</function>, you'll want
|
||||
to return immediately afterwards.
|
||||
</para>
|
||||
<para>
|
||||
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
|
||||
<varname>conf.replySuccess</varname> says. By default, it has
|
||||
the very dry (and appropriately robot-like) "The operation
|
||||
<function>irc.replySuccess</function> says. By default, it
|
||||
has the very dry (and appropriately robot-like) "The operation
|
||||
succeeded." but you're perfectly welcome to customize it
|
||||
yourself -- <filename>conf.py</filename> was written to be
|
||||
modified!
|
||||
yourself – the registry was written to be modified!
|
||||
</para>
|
||||
<para>
|
||||
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)))
|
||||
</programlisting>
|
||||
<para>
|
||||
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)
|
||||
</programlisting>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
Later, though, you'll see something other than
|
||||
<function>irc.reply</function>. This is
|
||||
<function>irc.queueMsg</function>, the general interface for
|
||||
sending messages to the server. It's what
|
||||
<function>irc.reply</function> is using under the covers. It
|
||||
takes an <classname>IrcMsg</classname> object. Fortunately,
|
||||
that's exactly what's returned by
|
||||
<function>ircmsgs.action</function>. An action message, just
|
||||
in case you don't know, is a /me kind of message.
|
||||
<function>ircmsgs.action</function> 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 <classname>IrcMsg</classname> object.
|
||||
<function>ircutils.replyTo</function> simply takes an
|
||||
<classname>IrcMsg</classname> 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 <function>irc.reply</function> was given
|
||||
a keyword argument here, <varname>action</varname>. This
|
||||
means that the reply is to be made as an action rather than a
|
||||
normal reply.
|
||||
</para>
|
||||
<para>
|
||||
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
|
||||
<function>irc.reply</function>). That raise just makes sure
|
||||
the user finds this out if he tries to nest this like "@rot13
|
||||
[diceroll]".
|
||||
</para>
|
||||
<para>
|
||||
<para>
|
||||
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):
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Finishing touches</title>
|
||||
<para>
|
||||
Let's take a look at that <function>configure</function>
|
||||
function <script>scripts/newplugin.py</script> made
|
||||
for us. Here it is, in case you've forgotten:
|
||||
</para>
|
||||
<programlisting>
|
||||
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')
|
||||
</programlisting>
|
||||
<para>
|
||||
You remember when you first started running supybot and ran
|
||||
<script>scripts/setup.py</script> 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
|
||||
<plugin>Random</plugin> 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.
|
||||
</para>
|
||||
<programlisting>
|
||||
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)
|
||||
</programlisting>
|
||||
<para>
|
||||
As you can see, what the <module>questions</module> module
|
||||
does is fairly self-evident: <function>yn</function> returns
|
||||
either 'y' or 'n'; <function>something</function> returns
|
||||
<emphasis>something</emphasis> (but not nothing; for nothing,
|
||||
you'd want <function>anything</function>). So basically we
|
||||
ask some questions until we get a good seed. Then we do this
|
||||
"onStart.append('seed %s' % seed)" doohickey.
|
||||
<varname>onStart</varname> 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
|
||||
<script>scripts/setup.py</script> creates for the bot.
|
||||
</para>
|
||||
<para>
|
||||
We've written our own plugin from scratch (well, from the
|
||||
boilerplate that we got from
|
||||
<script>scripts/newplugin.py</script> :)) and
|
||||
survived! Now go write more plugins for supybot, and send
|
||||
them to me so I can use them too :)
|
||||
</para>
|
||||
</sect2>
|
||||
<title>Using the registry in your plugin</title>
|
||||
<para>
|
||||
TODO: Describe the registry and how to write a proper plugin
|
||||
configure function.
|
||||
</para>
|
||||
</sect2>
|
||||
<para>
|
||||
We've written our own plugin from scratch (well, from the
|
||||
boilerplate that we got from
|
||||
<script>scripts/newplugin.py</script> :)) and
|
||||
survived! Now go write more plugins for supybot, and send
|
||||
them to me so I can use them too :)
|
||||
</para>
|
||||
</sect1>
|
||||
</article>
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user