mirror of
https://github.com/Mikaela/Limnoria-doc.git
synced 2024-12-29 06:42:33 +01:00
5202 lines
166 KiB
Plaintext
5202 lines
166 KiB
Plaintext
# SOME DESCRIPTIVE TITLE.
|
|
# Copyright (C) 2003-2015, the Limnoria/Gribble/Supybot contributors
|
|
# This file is distributed under the same license as the Limnoria package.
|
|
# FIRST AUTHOR <EMAIL@ADDRESS>, 2016.
|
|
#
|
|
#, fuzzy
|
|
msgid ""
|
|
msgstr ""
|
|
"Project-Id-Version: Limnoria 0.83\n"
|
|
"Report-Msgid-Bugs-To: \n"
|
|
"POT-Creation-Date: 2016-02-24 17:46+0100\n"
|
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
|
"MIME-Version: 1.0\n"
|
|
"Content-Type: text/plain; charset=utf-8\n"
|
|
"Content-Transfer-Encoding: 8bit\n"
|
|
"Generated-By: Babel 2.2.0\n"
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:3
|
|
msgid "Advanced Plugin Config"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:4
|
|
msgid ""
|
|
"This tutorial covers some of the more advanced plugin config features "
|
|
"available to Supybot plugin authors."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:8
|
|
msgid "What's This Tutorial For?"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:9
|
|
msgid "Brief overview of what this tutorial covers and the target audience."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:11
|
|
msgid ""
|
|
"Want to know the crazy advanced features available to you, the Supybot "
|
|
"plugin author? Well, this is the tutorial for you. This article assumes "
|
|
"you've read the Supybot plugin author tutorial since all the basics of "
|
|
"plugin config are handled there first."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:16
|
|
msgid "In this tutorial we'll cover:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:18
|
|
msgid ""
|
|
"Using the configure function more effectively by using the functions "
|
|
"provided in supybot.questions"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:20
|
|
msgid ""
|
|
"Creating config variable groups and config variables underneath those "
|
|
"groups."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:22
|
|
msgid ""
|
|
"The built-in config variable types (\"registry types\") for use with "
|
|
"config variables"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:24
|
|
msgid ""
|
|
"Creating custom registry types to handle config variable values more "
|
|
"effectively"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:28
|
|
msgid "Using 'configure' effectively"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:29
|
|
msgid ""
|
|
"How to use 'configure' effectively using the functions from "
|
|
"'supybot.questions'"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:32
|
|
msgid ""
|
|
"In the original Supybot plugin author tutorial you'll note that we gloss "
|
|
"over the configure portion of the config.py file for the sake of keeping "
|
|
"the tutorial to a reasonable length. Well, now we're going to cover it in"
|
|
" more detail."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:37
|
|
msgid ""
|
|
"The supybot.questions module is a nice little module coded specifically "
|
|
"to help clean up the configure section of every plugin's config.py. The "
|
|
"boilerplate config.py code imports the four most useful functions from "
|
|
"that module:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:41
|
|
msgid ""
|
|
"\"expect\" is a very general prompting mechanism which can specify "
|
|
"certain inputs that it will accept and also specify a default response. "
|
|
"It takes the following arguments:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:45
|
|
#: ../../develop/advanced_plugin_config.rst:57
|
|
#: ../../develop/advanced_plugin_config.rst:62
|
|
#: ../../develop/advanced_plugin_config.rst:69
|
|
msgid "prompt: The text to be displayed"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:46
|
|
msgid "possibilities: The list of possible responses (can be the empty list, [])"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:48
|
|
msgid ""
|
|
"default (optional): Defaults to None. Specifies the default value to use "
|
|
"if the user enters in no input."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:50
|
|
msgid ""
|
|
"acceptEmpty (optional): Defaults to False. Specifies whether or not to "
|
|
"accept no input as an answer."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:53
|
|
msgid ""
|
|
"\"anything\" is basically a special case of expect which takes anything "
|
|
"(including no input) and has no default value specified. It takes only "
|
|
"one argument:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:59
|
|
msgid ""
|
|
"\"something\" is also a special case of expect, requiring some input and "
|
|
"allowing an optional default. It takes the following arguments:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:63
|
|
msgid ""
|
|
"default (optional): Defaults to None. The default value to use if the "
|
|
"user doesn't input anything."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:66
|
|
msgid ""
|
|
"\"yn\" is for \"yes or no\" questions and basically forces the user to "
|
|
"input a \"y\" for yes, or \"n\" for no. It takes the following arguments:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:70
|
|
msgid ""
|
|
"default (optional): Defaults to None. Default value to use if the user "
|
|
"doesn't input anything."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:73
|
|
msgid ""
|
|
"All of these functions, with the exception of \"yn\", return whatever "
|
|
"string results as the answer whether it be input from the user or "
|
|
"specified as the default when the user inputs nothing. The \"yn\" "
|
|
"function returns True for \"yes\" answers and False for \"no\" answers."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:78
|
|
msgid ""
|
|
"For the most part, the latter three should be sufficient, but we expose "
|
|
"expect to anyone who needs a more specialized configuration."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:81
|
|
msgid ""
|
|
"Let's go through a quick example configure that covers all four of these "
|
|
"functions. First I'll give you the code, and then we'll go through it, "
|
|
"discussing each usage of a supybot.questions function just to make sure "
|
|
"you realize what the code is actually doing. Here it is::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:107
|
|
msgid ""
|
|
"As you can see, this is the WorldDom plugin, which I am currently working"
|
|
" on. The first thing our configure function checks is to see whether or "
|
|
"not the bot owner would like the world domination commands in this plugin"
|
|
" to be available to everyone. If they say yes, we set the "
|
|
"globalWorldDominationRequires configuration variable to the empty string,"
|
|
" signifying that no specific capabilities are necessary. If they say no, "
|
|
"we prompt them for a specific capability to check for, defaulting to the "
|
|
"\"Admin\" capability. Here they can create their own custom capability to"
|
|
" grant to folks which this plugin will check for if they want, but "
|
|
"luckily for the bot owner they don't really have to do this since "
|
|
"Supybot's capabilities system can be flexed to take care of this."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:118
|
|
msgid ""
|
|
"Lastly, we check to find out what direction they want to attack from as "
|
|
"they venture towards world domination. I prefer \"death from above!\", so"
|
|
" I made that the default response, but the more boring cardinal "
|
|
"directions are available as choices as well."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:124
|
|
msgid "Using Config Groups"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:125
|
|
msgid "A brief overview of how to use config groups to organize config variables"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:128
|
|
msgid "Supybot's Hierarchical Configuration"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:130
|
|
msgid ""
|
|
"Supybot's configuration is inherently hierarchical, as you've probably "
|
|
"already figured out in your use of the bot. Naturally, it makes sense to "
|
|
"allow plugin authors to create their own hierarchies to organize their "
|
|
"configuration variables for plugins that have a lot of plugin options. If"
|
|
" you've taken a look at the plugins that Supybot comes with, you've "
|
|
"probably noticed that several of them take advantage of this. In this "
|
|
"section of this tutorial we'll go over how to make your own config "
|
|
"hierarchy for your plugin."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:138
|
|
msgid ""
|
|
"Here's the brilliant part about Supybot config values which makes "
|
|
"hierarchical structuring all that much easier - values are groups. That "
|
|
"is, any config value you may already defined in your plugins can already "
|
|
"be treated as a group, you simply need to know how to add items to that "
|
|
"group."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:143
|
|
msgid ""
|
|
"Now, if you want to just create a group that doesn't have an inherent "
|
|
"value you can do that as well, but you'd be surprised at how rarely you "
|
|
"have to do that. In fact if you look at most of the plugins that Supybot "
|
|
"comes with, you'll only find that we do this in a handful of spots yet we"
|
|
" use the \"values as groups\" feature quite a bit."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:150
|
|
msgid "Creating a Config Group"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:152
|
|
msgid ""
|
|
"As stated before, config variables themselves are groups, so you can "
|
|
"create a group simply by creating a configuration variable::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:159
|
|
msgid ""
|
|
"As you probably know by now this creates the config variable "
|
|
"supybot.plugins.WorldDom.globalWorldDominationRequires which you can "
|
|
"access/set using the Config plugin directly on the running bot. What you "
|
|
"may not have known prior to this tutorial is that that variable is also a"
|
|
" group. Specifically, it is now the "
|
|
"WorldDom.globalWorldDominationRequires group, and we can add config "
|
|
"variables to it! Unfortunately, this particular bit of configuration "
|
|
"doesn't really require anything underneath it, so let's create a new "
|
|
"group which does using the \"create only a group, not a value\" command."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:168
|
|
msgid ""
|
|
"Let's create a configurable list of targets for different types of "
|
|
"attacks (land, sea, air, etc.). We'll call the group attackTargets. "
|
|
"Here's how you create just a config group alone with no value assigned::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:174
|
|
msgid ""
|
|
"The first argument is just the group under which you want to create your "
|
|
"new group (and we got WorldDom from conf.registerPlugin which was in our "
|
|
"boilerplate code from the plugin creation wizard). The second argument "
|
|
"is, of course, the group name. So now we have WorldDom.attackTargets (or,"
|
|
" fully, supybot.plugins.WorldDom.attackTargets)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:181
|
|
msgid "Adding Values to a Group"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:183
|
|
msgid ""
|
|
"Actually, you've already done this several times, just never to a custom "
|
|
"group of your own. You've always added config values to your plugin's "
|
|
"config group. With that in mind, the only slight modification needed is "
|
|
"to simply point to the new group::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:192
|
|
msgid ""
|
|
"And now we have a nice list of air targets! You'll notice that the first "
|
|
"argument is WorldDom.attackTargets, our new group. Make sure that the "
|
|
"conf.registerGroup call is made before this one or else you'll get a "
|
|
"nasty AttributeError."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:198
|
|
msgid "Variations"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:201
|
|
msgid "Channel-specific values"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:203
|
|
msgid ""
|
|
"A very handy feature is channel-specific variables, which allows bot "
|
|
"administrators to set a global value (as for non-channel-specific values "
|
|
"AND another value for specific channels)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:207
|
|
msgid ""
|
|
"The syntax is pretty much like the previous one, except we use "
|
|
"`registerChannelValue` instead of `registerGlobalValue`::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:215
|
|
msgid "Private values"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:217
|
|
msgid ""
|
|
"Variable type also take an optional argument, for setting a configuration"
|
|
" variable to private (useful for passwords, authentication tokens, api "
|
|
"keys, …)::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:227
|
|
msgid "Accessing the configuration registry"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:229
|
|
msgid "Of course, you can access the variables in your plugins."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:231
|
|
msgid ""
|
|
"If it is a variable created by your plugin, you can do it like this (if "
|
|
"the configuration variable's name is `air`)::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:236
|
|
msgid ""
|
|
"and it will return data of the right type (in this case, a list of "
|
|
"string, as we declarated it above as a "
|
|
"`registry.SpaceSeparatedListOfStrings`)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:239
|
|
msgid ""
|
|
"If it is a channel-specific variable, you can get the value on `#channel`"
|
|
" like this (if the variable is not defined on this channel, it defaults "
|
|
"to the global one)::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:246
|
|
msgid ""
|
|
"You can also set configuration variables (either globally or for a single"
|
|
" channel)::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:254
|
|
msgid ""
|
|
"You can also access other configuration variables (or your own if you "
|
|
"want) via the ``supybot.conf`` module::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:264
|
|
msgid "The Built-in Registry Types"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:265
|
|
msgid ""
|
|
"A rundown of all of the built-in registry types available for use with "
|
|
"config variables."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:268
|
|
msgid ""
|
|
"The \"registry\" module defines the following config variable types for "
|
|
"your use (I'll include the 'registry.' on each one since that's how "
|
|
"you'll refer to it in code most often). Most of them are fairly self-"
|
|
"explanatory, so excuse the boring descriptions:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:273
|
|
msgid ""
|
|
"registry.Boolean - A simple true or false value. Also accepts the "
|
|
"following for true: \"true\", \"on\" \"enable\", \"enabled\", \"1\", and "
|
|
"the following for false: \"false\", \"off\", \"disable\", \"disabled\", "
|
|
"\"0\","
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:277
|
|
msgid "registry.Integer - Accepts any integer value, positive or negative."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:279
|
|
msgid "registry.NonNegativeInteger - Will hold any non-negative integer value."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:281
|
|
msgid ""
|
|
"registry.PositiveInteger - Same as above, except that it doesn't accept 0"
|
|
" as a value."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:284
|
|
msgid "registry.Float - Accepts any floating point number."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:286
|
|
msgid "registry.PositiveFloat - Accepts any positive floating point number."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:288
|
|
msgid ""
|
|
"registry.Probability - Accepts any floating point number between 0 and 1 "
|
|
"(inclusive, meaning 0 and 1 are also valid)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:291
|
|
msgid "registry.String - Accepts any string that is not a valid Python command"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:293
|
|
msgid ""
|
|
"registry.NormalizedString - Accepts any string (with the same exception "
|
|
"above) but will normalize sequential whitespace to a single space.."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:296
|
|
msgid ""
|
|
"registry.StringSurroundedBySpaces - Accepts any string but assures that "
|
|
"it has a space preceding and following it. Useful for configuring a "
|
|
"string that goes in the middle of a response."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:300
|
|
msgid ""
|
|
"registry.StringWithSpaceOnRight - Also accepts any string but assures "
|
|
"that it has a space after it. Useful for configuring a string that begins"
|
|
" a response."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:304
|
|
msgid "registry.Regexp - Accepts only valid (Perl or Python) regular expressions"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:306
|
|
msgid ""
|
|
"registry.SpaceSeparatedListOfStrings - Accepts a space-separated list of "
|
|
"strings."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:309
|
|
msgid ""
|
|
"There are a few other built-in registry types that are available but are "
|
|
"not usable in their current state, only by creating custom registry "
|
|
"types, which we'll go over in the next section."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:314
|
|
msgid "Custom Registry Types"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:315
|
|
msgid ""
|
|
"How to create and use your own custom registry types for use in "
|
|
"customizing plugin config variables."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:319
|
|
msgid "Why Create Custom Registry Types?"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:321
|
|
msgid ""
|
|
"For most configuration, the provided types in the registry module are "
|
|
"sufficient. However, for some configuration variables it's not only "
|
|
"convenient to use custom registry types, it's actually recommended. "
|
|
"Customizing registry types allows for tighter restrictions on the values "
|
|
"that get set and for greater error-checking than is possible with the "
|
|
"provided types."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:328
|
|
msgid "What Defines a Registry Type?"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:330
|
|
msgid ""
|
|
"First and foremost, it needs to subclass one of the existing registry "
|
|
"types from the registry module, whether it be one of the ones in the "
|
|
"previous section or one of the other classes in registry specifically "
|
|
"designed to be subclassed."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:334
|
|
msgid ""
|
|
"Also it defines a number of other nice things: a custom error message for"
|
|
" your type, customized value-setting (transforming the data you get into "
|
|
"something else if wanted), etc."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:339
|
|
msgid "Creating Your First Custom Registry Type"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:341
|
|
msgid ""
|
|
"As stated above, priority number one is that you subclass one of the "
|
|
"types in the registry module. Basically, you just subclass one of those "
|
|
"and then customize whatever you want. Then you can use it all you want in"
|
|
" your own plugins. We'll do a quick example to demonstrate."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:346
|
|
msgid ""
|
|
"We already have registry.Integer and registry.PositiveInteger, but let's "
|
|
"say we want to accept only negative integers. We can create our own "
|
|
"NegativeInteger registry type like so::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:357
|
|
msgid ""
|
|
"All we need to do is define a new error message for our custom registry "
|
|
"type (specified by the docstring for the class), and customize the "
|
|
"setValue function. Note that all you have to do when you want to signify "
|
|
"that you've gotten an invalid value is to call self.error(). Finally, we "
|
|
"call the parent class's setValue to actually set the value."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:364
|
|
msgid "What Else Can I Customize?"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:366
|
|
msgid ""
|
|
"Well, the error string and the setValue function are the most useful "
|
|
"things that are available for customization, but there are other things. "
|
|
"For examples, look at the actual built-in registry types defined in "
|
|
"registry.py (in the src directory distributed with the bot)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:372
|
|
msgid "What Subclasses Can I Use?"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:374
|
|
msgid ""
|
|
"Chances are one of the built-in types in the previous section will be "
|
|
"sufficient, but there are a few others of note which deserve mention:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:377
|
|
msgid ""
|
|
"registry.Value - Provides all the core functionality of registry types "
|
|
"(including acting as a group for other config variables to reside "
|
|
"underneath), but nothing more."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:381
|
|
msgid ""
|
|
"registry.OnlySomeStrings - Allows you to specify only a certain set of "
|
|
"strings as valid values. Simply override validStrings in the inheriting "
|
|
"class and you're ready to go."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:385
|
|
msgid ""
|
|
"registry.SeparatedListOf - The generic class which is the parent class to"
|
|
" registry.SpaceSeparatedListOfStrings. Allows you to customize four "
|
|
"things: the type of sequence it is (list, set, tuple, etc.), what each "
|
|
"item must be (String, Boolean, etc.), what separates each item in the "
|
|
"sequence (using custom splitter/joiner functions), and whether or not the"
|
|
" sequence is to be sorted. Look at the definitions of "
|
|
"registry.SpaceSeparatedListOfStrings and "
|
|
"registry.CommaSeparatedListOfStrings at the bottom of registry.py for "
|
|
"more information. Also, there will be an example using this in the "
|
|
"section below."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:397
|
|
msgid "Using My Custom Registry Type"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:399
|
|
msgid ""
|
|
"Using your new registry type is relatively straightforward. Instead of "
|
|
"using whatever registry built-in you might have used before, now use your"
|
|
" own custom class. Let's say we define a registry type to handle a comma-"
|
|
"separated list of probabilities::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:410
|
|
msgid ""
|
|
"Now, to use that type we simply have to specify it whenever we create a "
|
|
"config variable using it::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:417
|
|
msgid ""
|
|
"Note that we initialize it just the same as we do any other registry "
|
|
"type, with two arguments: the default value, and then the description of "
|
|
"the config variable."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:424
|
|
msgid "Configuration hooks"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:427
|
|
msgid ""
|
|
"Until stock Supybot or Gribble merge this feature, this section only "
|
|
"applies to Limnoria."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:430
|
|
msgid ""
|
|
"It is possible to get a function called when a configuration variable is "
|
|
"changed. While this is usually not useful (you get the value whenever you"
|
|
" need it), some plugins do use it, for instance for caching results or "
|
|
"for pre-fetching data."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:435
|
|
msgid ""
|
|
"Let's say you want to write a plugin that prints `nick changed` in the "
|
|
"logs when `supybot.nick` is edited. You can do it like this::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:449
|
|
msgid ""
|
|
"As not all Supybot versions support it (yet), it can be a good idea to "
|
|
"show a warning instead of crashing on those versions::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_config.rst:469
|
|
msgid ""
|
|
"For the moment, the `name` parameter is never given when the callback is "
|
|
"called. However, in the future, it will be set to the name of the "
|
|
"variable that has been changed (useful if you want to use the same "
|
|
"callback for multiple variable), so it is better to allow this parameter."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:3
|
|
msgid "Advanced Plugin Testing"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:4
|
|
msgid "The complete guide to writing tests for your plugins."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:7
|
|
msgid "Why Write Tests?"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:8
|
|
msgid "Why should I write tests for my plugin? Here's why."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:10
|
|
msgid ""
|
|
"For those of you asking \"Why should I write tests for my plugin? I tried"
|
|
" it out, and it works!\", read on. For those of you who already realize "
|
|
"that Testing is Good (TM), skip to the next section."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:14
|
|
msgid "Here are a few quick reasons why to test your Supybot plugins."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:16
|
|
msgid ""
|
|
"When/if we rewrite or change certain features in Supybot, tests make sure"
|
|
" your plugin will work with these changes. It's much easier to run "
|
|
"supybot-test MyPlugin after upgrading the code and before even reloading "
|
|
"the bot with the new code than it is to load the bot with new code and "
|
|
"then load the plugin only to realize certain things don't work. You may "
|
|
"even ultimately decide you want to stick with an older version for a "
|
|
"while as you patch your custom plugin. This way you don't have to rush a "
|
|
"patch while restless users complain since you're now using a newer "
|
|
"version that doesn't have the plugin they really like."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:26
|
|
msgid ""
|
|
"Running the automated tests takes a few seconds, testing plugins in IRC "
|
|
"on a live bot generally takes quite a bit longer. We make it so that "
|
|
"writing tests generally doesn't take much time, so a small initial "
|
|
"investment adds up to lots of long-term gains."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:31
|
|
msgid ""
|
|
"If you want your plugin to be included in any of our releases (the core "
|
|
"Supybot if you think it's worthy, or our supybot-plugins package), it has"
|
|
" to have tests. Period."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:35
|
|
msgid "For a bigger list of why to write unit tests, check out this article:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:37
|
|
msgid "http://www.onjava.com/pub/a/onjava/2003/04/02/javaxpckbk.html"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:39
|
|
msgid ""
|
|
"and also check out what the Extreme Programming folks have to say about "
|
|
"unit tests:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:42
|
|
msgid "http://www.extremeprogramming.org/rules/unittests.html"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:45
|
|
msgid "Plugin Tests"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:46
|
|
msgid "How to write tests for commands in your plugins."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:49
|
|
#: ../../develop/advanced_plugin_testing.rst:239 ../../develop/httpserver.rst:8
|
|
#: ../../develop/plugin_tutorial.rst:6 ../../develop/using_wrap.rst:8
|
|
msgid "Introduction"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:51
|
|
msgid ""
|
|
"This tutorial assumes you've read through the plugin author tutorial, and"
|
|
" that you used supybot-plugin-create to create your plugin (as everyone "
|
|
"should). So, you should already have all the necessary imports and all "
|
|
"that boilerplate stuff in test.py already, and you have already seen what"
|
|
" a basic plugin test looks like from the plugin author tutorial. Now "
|
|
"we'll go into more depth about what plugin tests are available to Supybot"
|
|
" plugin authors."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:59
|
|
msgid "Plugin Test Case Classes"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:61
|
|
msgid ""
|
|
"Supybot comes with two plugin test case classes, PluginTestCase and "
|
|
"ChannelPluginTestCase. The former is used when it doesn't matter whether "
|
|
"or not the commands are issued in a channel, and the latter is used for "
|
|
"when it does. For the most part their API is the same, so unless there's "
|
|
"a distinction between the two we'll treat them as one and the same when "
|
|
"discussing their functionality."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:69
|
|
msgid "The Most Basic Plugin Test Case"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:71
|
|
msgid "At the most basic level, a plugin test case requires three things:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:73
|
|
msgid ""
|
|
"the class declaration (subclassing PluginTestCase or "
|
|
"ChannelPluginTestCase)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:75
|
|
msgid ""
|
|
"a list of plugins that need to be loaded for these tests (does not "
|
|
"include Owner, Misc, or Config, those are always automatically loaded) - "
|
|
"often this is just the name of the plugin that you are writing tests for"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:78
|
|
msgid "some test methods"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:80
|
|
msgid ""
|
|
"Here's what the most basic plugin test case class looks like (for a "
|
|
"plugin named MyPlugin)::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:89
|
|
msgid ""
|
|
"Your plugin test case should be named TestCase as you see above, though "
|
|
"it doesn't necessarily have to be named that way (supybot-plugin-create "
|
|
"puts that in place for you anyway). As you can see we elected to subclass"
|
|
" PluginTestCase because this hypothetical plugin apparently doesn't do "
|
|
"anything channel-specific."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:95
|
|
msgid ""
|
|
"As you probably noticed, the plugins attribute of the class is where the "
|
|
"list of necessary plugins goes, and in this case just contains the plugin"
|
|
" that we are testing. This will be the case for probably the majority of "
|
|
"plugins. A lot of the time test writers will use a bot function that "
|
|
"performs some function that they don't want to write code for and they "
|
|
"will just use command nesting to feed the bot what they need by using "
|
|
"that plugin's functionality. If you choose to do this, only do so with "
|
|
"core bot plugins as this makes distribution of your plugin simpler. After"
|
|
" all, we want people to be able to run your plugin tests without having "
|
|
"to have all of your plugins!"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:105
|
|
msgid ""
|
|
"One last thing to note before moving along is that each of the test "
|
|
"methods should describe what they are testing. If you want to test that "
|
|
"your plugin only responds to registered users, don't be afraid to name "
|
|
"your test method testOnlyRespondingToRegisteredUsers or "
|
|
"testNotRespondingToUnregisteredUsers. You may have noticed some rather "
|
|
"long and seemingly unwieldy test method names in our code, but that's "
|
|
"okay because they help us know exactly what's failing when we run our "
|
|
"tests. With an ambiguously named test method we may have to crack open "
|
|
"test.py after running the tests just to see what it is that failed. For "
|
|
"this reason you should also test only one thing per test method. Don't "
|
|
"write a test method named testFoobarAndBaz. Just write two test methods, "
|
|
"testFoobar and testBaz. Also, it is important to note that test methods "
|
|
"must begin with test and that any method within the class that does begin"
|
|
" with test will be run as a test by the supybot-test program. If you want"
|
|
" to write utility functions in your test class that's fine, but don't "
|
|
"name them something that begins with test or they will be executed as "
|
|
"tests."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:122
|
|
msgid "Including Extra Setup"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:124
|
|
msgid ""
|
|
"Some tests you write may require a little bit of setup. For the most part"
|
|
" it's okay just to include that in the individual test method itself, but"
|
|
" if you're duplicating a lot of setup code across all or most of your "
|
|
"test methods it's best to use the setUp method to perform whatever needs "
|
|
"to be done prior to each test method."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:130
|
|
msgid ""
|
|
"The setUp method is inherited from the whichever plugin test case class "
|
|
"you chose for your tests, and you can add whatever functionality you want"
|
|
" to it. Note the important distinction, however: you should be adding to "
|
|
"it and not overriding it. Just define setUp in your own plugin test case "
|
|
"class and it will be run before all the test methods are invoked."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:136
|
|
msgid ""
|
|
"Let's do a quick example of one. Let's write a setUp method which "
|
|
"registers a test user for our test bot::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:146
|
|
msgid ""
|
|
"Now notice how the first line calls the parent class's setUp method "
|
|
"first? This must be done first. Otherwise several problems are likely to "
|
|
"arise. For one, you wouldn't have an irc object at self.irc that we use "
|
|
"later on nor would self.nick be set."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:151
|
|
msgid ""
|
|
"As for the rest of the method, you'll notice a few things that are "
|
|
"available to the plugin test author. self.prefix refers to the hostmask "
|
|
"of the hypothetical test user which will be \"talking\" to the bot, "
|
|
"issuing commands. We set it to some generically fake hostmask, and then "
|
|
"we use feedMsg to send a private message (using the bot's nick, "
|
|
"accessible via self.nick) to the bot registering the username \"tester\" "
|
|
"with the password \"moo\". We have to do it this way (rather than what "
|
|
"you'll find out is the standard way of issuing commands to the bot in "
|
|
"test cases a little later) because registration must be done in private. "
|
|
"And lastly, since feedMsg doesn't dequeue any messages from the bot after"
|
|
" being fed a message, we perform a getMsg to get the response. You're not"
|
|
" expected to know all this yet, but do take note of it since using these "
|
|
"methods in test-writing is not uncommon. These utility methods as well as"
|
|
" all of the available assertions are covered in the next section."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:165
|
|
msgid ""
|
|
"So, now in any of the test methods we write, we'll be able to count on "
|
|
"the fact that there will be a registered user \"tester\" with a password "
|
|
"of \"moo\", and since we changed our prefix by altering self.prefix and "
|
|
"registered after doing so, we are now identified as this user for all "
|
|
"messages we send unless we specify that they are coming from some other "
|
|
"prefix."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:172
|
|
msgid "The Opposite of Setting-up: Tearing Down"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:174
|
|
msgid ""
|
|
"If you did some things in your setUp that you want to clean up after, "
|
|
"then this code belongs in the tearDown method of your test case class. "
|
|
"It's essentially the same as setUp except that you probably want to wait "
|
|
"to invoke the parent class's tearDown until after you've done all of your"
|
|
" tearing down. But do note that you do still have to invoke the parent "
|
|
"class's tearDown method if you decide to add in your own tear-down stuff."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:182
|
|
msgid "Setting Config Variables for Testing"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:184
|
|
msgid ""
|
|
"Before we delve into all of the fun assertions we can use in our test "
|
|
"methods it's worth noting that each plugin test case can set custom "
|
|
"values for any Supybot config variable they want rather easily. Much like"
|
|
" how we can simply list the plugins we want loaded for our tests in the "
|
|
"plugins attribute of our test case class, we can set config variables by "
|
|
"creating a mapping of variables to values with the config attribute."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:191
|
|
msgid ""
|
|
"So if, for example, we wanted to disable nested commands within our "
|
|
"plugin testing for some reason, we could just do this::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:200
|
|
msgid ""
|
|
"And now you can be assured that supybot.commands.nested is going to be "
|
|
"off for all of your test methods in this test case class."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:204
|
|
msgid "Temporarily setting a configuration variable"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:206
|
|
msgid ""
|
|
"Sometimes we want to change a configuration variable only in a test (or "
|
|
"in a part of a test), and keep the original value for other tests. The "
|
|
"historical way to do it is::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:221
|
|
msgid "But there is a more compact syntax, using context managers::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:231
|
|
msgid ""
|
|
"Until stock Supybot or Gribble merge the second syntax, only Limnoria "
|
|
"will support it."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:235
|
|
msgid "Plugin Test Methods"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:236
|
|
msgid "The full list of test methods and how to use them."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:241
|
|
msgid ""
|
|
"You know how to make plugin test case classes and you know how to do just"
|
|
" about everything with them except to actually test stuff. Well, listed "
|
|
"below are all of the assertions used in tests. If you're unfamiliar with "
|
|
"what an assertion is in code testing, it is basically a requirement of "
|
|
"something that must be true in order for that test to pass. It's a "
|
|
"necessary condition. If any assertion within a test method fails the "
|
|
"entire test method fails and it goes on to the next one."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:250
|
|
msgid "Assertions"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:252
|
|
msgid ""
|
|
"All of these are methods of the plugin test classes themselves and hence "
|
|
"are accessed by using self.assertWhatever in your test methods. These are"
|
|
" sorted in order of relative usefulness."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:260
|
|
msgid "assertResponse(query, expectedResponse)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:257
|
|
msgid ""
|
|
"Feeds query to the bot as a message and checks to make sure the response "
|
|
"is expectedResponse. The test fails if they do not match (note that "
|
|
"prefixed nicks in the response do not need to be included in the "
|
|
"expectedResponse)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:264
|
|
msgid "assertError(query)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:263
|
|
msgid ""
|
|
"Feeds query to the bot and expects an error in return. Fails if the bot "
|
|
"doesn't return an error."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:269
|
|
msgid "assertNotError(query)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:267
|
|
msgid ""
|
|
"The opposite of assertError. It doesn't matter what the response to query"
|
|
" is, as long as it isn't an error. If it is not an error, this test "
|
|
"passes, otherwise it fails."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:275
|
|
msgid "assertRegexp(query, regexp, flags=re.I)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:272
|
|
msgid ""
|
|
"Feeds query to the bot and expects something matching the regexp (no m// "
|
|
"required) in regexp with the supplied flags. Fails if the regexp does not"
|
|
" match the bot's response."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:280
|
|
msgid "assertNotRegexp(query, regexp, flags=re.I)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:278
|
|
msgid ""
|
|
"The opposite of assertRegexp. Fails if the bot's output matches regexp "
|
|
"with the supplied flags."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:284
|
|
msgid "assertHelp(query)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:283
|
|
msgid ""
|
|
"Expects query to return the help for that command. Fails if the command "
|
|
"help is not triggered."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:289
|
|
msgid "assertAction(query, expectedResponse=None)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:287
|
|
msgid ""
|
|
"Feeds query to the bot and expects an action in response, specifically "
|
|
"expectedResponse if it is supplied. Otherwise, the test passes for any "
|
|
"action response."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:294
|
|
msgid "assertActionRegexp(query, regexp, flags=re.I)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:292
|
|
msgid ""
|
|
"Basically like assertRegexp but carries the extra requirement that the "
|
|
"response must be an action or the test will fail."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:297
|
|
msgid "Utilities"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:303
|
|
msgid "feedMsg(query, to=None, frm=None)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:300
|
|
msgid ""
|
|
"Simply feeds query to whoever is specified in to or to the bot itself if "
|
|
"no one is specified. Can also optionally specify the hostmask of the "
|
|
"sender with the frm keyword. Does not actually perform any assertions."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:306
|
|
msgid "getMsg(query)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:306
|
|
msgid "Feeds query to the bot and gets the response."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:309
|
|
msgid "Other Tests"
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:310
|
|
msgid ""
|
|
"If you had to write helper code for a plugin and want to test it, here's "
|
|
"how."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:313
|
|
msgid ""
|
|
"Previously we've only discussed how to test stuff in the plugin that is "
|
|
"intended for IRC. Well, we realize that some Supybot plugins will require"
|
|
" utility code that doesn't necessarily require all of the overhead of "
|
|
"setting up IRC stuff, and so we provide a more lightweight test case "
|
|
"class, SupyTestCase, which is a very very light wrapper around "
|
|
"unittest.TestCase (from the standard unittest module) that basically just"
|
|
" provides a little extra logging. This test case class is what you should"
|
|
" use for writing those test cases which test things that are independent "
|
|
"of IRC."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:322
|
|
msgid ""
|
|
"For example, in the MoobotFactoids plugin there is a large chunk of "
|
|
"utility code dedicating to parsing out random choices within a factoid "
|
|
"using a class called OptionList. So, we wrote the OptionListTestCase as a"
|
|
" SupyTestCase for the MoobotFactoids plugin. The setup for test methods "
|
|
"is basically the same as before, only you don't have to define plugins "
|
|
"since this is independent of IRC."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:329
|
|
msgid ""
|
|
"You still have the choice of using setUp and tearDown if you wish, since "
|
|
"those are inherited from unittest.TestCase. But, the same rules about "
|
|
"calling the setUp or tearDown method from the parent class still apply."
|
|
msgstr ""
|
|
|
|
#: ../../develop/advanced_plugin_testing.rst:333
|
|
msgid ""
|
|
"With all this in hand, now you can write great tests for your Supybot "
|
|
"plugins!"
|
|
msgstr ""
|
|
|
|
#: ../../develop/callbacks.rst:5
|
|
msgid "supybot.callbacks"
|
|
msgstr ""
|
|
|
|
#: ../../develop/callbacks.rst:10
|
|
msgid "Plugin"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.Plugin:1
|
|
msgid ""
|
|
"Bases: :class:`supybot.callbacks.PluginMixin`, "
|
|
":class:`supybot.callbacks.Commands`"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.Plugin.callPrecedence:1
|
|
#: supybot.irclib.IrcCallback.callPrecedence:1
|
|
msgid ""
|
|
"Returns a pair of (callbacks to call before me, callbacks to call after "
|
|
"me)"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.Plugin.die:1 supybot.irclib.IrcCallback.die:1
|
|
msgid "Makes the callback die. Called when the parent Irc object dies."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.Plugin.dispatchCommand:1
|
|
#: supybot.irclib.Irc.dispatchCommand:1
|
|
#: supybot.irclib.IrcState.dispatchCommand:1
|
|
#: supybot.irclib.IrcCommandDispatcher.dispatchCommand:1
|
|
msgid "Given a string 'command', dispatches to doCommand."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.Plugin.getCommandMethod:1
|
|
#: supybot.callbacks.Commands.getCommandMethod:1
|
|
msgid "Gets the given command from this plugin."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.Plugin.inFilter:1 supybot.irclib.IrcCallback.inFilter:1
|
|
msgid "Used for filtering/modifying messages as they're entering."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.Plugin.inFilter:3 supybot.irclib.IrcCallback.inFilter:3
|
|
msgid ""
|
|
"ircmsgs.IrcMsg objects are immutable, so this method is expected to "
|
|
"return another ircmsgs.IrcMsg object. Obviously the same IrcMsg can be "
|
|
"returned."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.Plugin.isCommand:1
|
|
#: supybot.callbacks.Commands.isCommand:1
|
|
msgid "Convenience, backwards-compatibility, semi-deprecated."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.Plugin.isCommandMethod:1
|
|
#: supybot.callbacks.Commands.isCommandMethod:1
|
|
msgid "Returns whether a given method name is a command in this plugin."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.Plugin.outFilter:1
|
|
#: supybot.irclib.IrcCallback.outFilter:1
|
|
msgid "Used for filtering/modifying messages as they're leaving."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.Plugin.outFilter:3
|
|
#: supybot.irclib.IrcCallback.outFilter:3
|
|
msgid "As with inFilter, an IrcMsg is returned."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.Plugin.reset:1 supybot.irclib.IrcCallback.reset:1
|
|
msgid "Resets the callback. Called when reconnecting to the server."
|
|
msgstr ""
|
|
|
|
#: ../../develop/callbacks.rst:19
|
|
msgid "PluginRegexp"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.PluginRegexp:1
|
|
msgid "Bases: :class:`supybot.callbacks.Plugin`"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.PluginRegexp:1
|
|
msgid ""
|
|
"Same as Plugin, except allows the user to also include regexp-based "
|
|
"callbacks. All regexp-based callbacks must be specified in the set (or "
|
|
"list) attribute \"regexps\", \"addressedRegexps\", or "
|
|
"\"unaddressedRegexps\" depending on whether they should always be "
|
|
"triggered, triggered only when the bot is addressed, or triggered only "
|
|
"when the bot isn't addressed."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.PluginRegexp.addressedRegexps:1
|
|
msgid ""
|
|
"'addressedRegexps' methods are called only when the message is addressed,"
|
|
" and then, only with the payload (i.e., what is returned from the "
|
|
"'addressed' function."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.PluginRegexp.regexps:1
|
|
msgid "'regexps' methods are called whether the message is addressed or not."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.PluginRegexp.unaddressedRegexps:1
|
|
msgid ""
|
|
"'unaddressedRegexps' methods are called only when the message is *not* "
|
|
"addressed."
|
|
msgstr ""
|
|
|
|
#: ../../develop/callbacks.rst:28
|
|
msgid "tokenize"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.tokenize:1
|
|
msgid "A utility function to create a Tokenizer and tokenize a string."
|
|
msgstr ""
|
|
|
|
#: ../../develop/callbacks.rst:34 ../../develop/irclib.rst:60
|
|
msgid "Other classes"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks:1
|
|
msgid "This module contains the basic callbacks for handling PRIVMSGs."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.ArgumentError:1 supybot.callbacks.SilentError:1
|
|
msgid "Bases: :class:`supybot.callbacks.Error`"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.ArgumentError:1
|
|
msgid "The bot replies with a help message when this is raised."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.BasePlugin:1 supybot.callbacks.DisabledCommands:1
|
|
#: supybot.callbacks.RichReplyMethods:1 supybot.callbacks.Tokenizer:1
|
|
#: supybot.commands.context:1 supybot.commands.Spec:1
|
|
#: supybot.irclib.IrcCommandDispatcher:1 supybot.irclib.IrcMsgQueue:1
|
|
#: supybot.ircmsgs.IrcMsg:1
|
|
msgid "Bases: :class:`object`"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.CanonicalNameDict:1
|
|
msgid "Bases: :class:`supybot.utils.gen.InsensitivePreservingDict`"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.CanonicalNameSet:1
|
|
msgid "Bases: :class:`supybot.utils.gen.NormalizingSet`"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.CanonicalString:1
|
|
msgid "Bases: :class:`supybot.registry.NormalizedString`"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.CommandProcess:1
|
|
msgid "Bases: :class:`supybot.world.SupyProcess`"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.CommandProcess:1
|
|
msgid ""
|
|
"Just does some extra logging and error-recovery for commands that need to"
|
|
" run in processes."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.CommandThread:1
|
|
msgid "Bases: :class:`supybot.world.SupyThread`"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.CommandThread:1
|
|
msgid ""
|
|
"Just does some extra logging and error-recovery for commands that need to"
|
|
" run in threads."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.Commands:1
|
|
msgid ""
|
|
"Bases: :class:`supybot.callbacks.BasePlugin`, "
|
|
":class:`supybot.utils.python.SynchronizedAndFirewalled`"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.Disabled:1
|
|
msgid "Bases: :class:`supybot.registry.SpaceSeparatedListOf`"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.Error:1
|
|
msgid "Bases: :class:`Exception`"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.Error:1
|
|
msgid "Generic class for errors in Privmsg callbacks."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.MetaSynchronizedAndFirewalled:1
|
|
msgid ""
|
|
"Bases: :class:`supybot.log.MetaFirewall`, "
|
|
":class:`supybot.utils.python.MetaSynchronized`"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.NestedCommandsIrcProxy:1
|
|
msgid "Bases: :class:`supybot.callbacks.ReplyIrcProxy`"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.NestedCommandsIrcProxy:1
|
|
msgid "A proxy object to allow proper nesting of commands (even threaded ones)."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.NestedCommandsIrcProxy.findCallbacksForArgs:1
|
|
msgid ""
|
|
"Returns a two-tuple of (command, plugins) that has the command (a list of"
|
|
" strings) and the plugins for which it was a command."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.NestedCommandsIrcProxy.reply:1
|
|
msgid "Keyword arguments:"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.NestedCommandsIrcProxy.reply:3
|
|
msgid "`noLengthCheck=False`: True if the length shouldn't be checked"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.NestedCommandsIrcProxy.reply:4
|
|
msgid "(used for 'more' handling)"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.NestedCommandsIrcProxy.reply:5
|
|
msgid "`prefixNick=True`: False if the nick shouldn't be prefixed to the"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.NestedCommandsIrcProxy.reply:6
|
|
msgid "reply."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.NestedCommandsIrcProxy.reply:7
|
|
msgid "`action=False`: True if the reply should be an action."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.NestedCommandsIrcProxy.reply:8
|
|
msgid "`private=False`: True if the reply should be in private."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.NestedCommandsIrcProxy.reply:9
|
|
msgid "`notice=False`: True if the reply should be noticed when the"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.NestedCommandsIrcProxy.reply:10
|
|
msgid "bot is configured to do so."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.NestedCommandsIrcProxy.reply:11
|
|
msgid "`to=<nick|channel>`: The nick or channel the reply should go to."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.NestedCommandsIrcProxy.reply:12
|
|
msgid "Defaults to msg.args[0] (or msg.nick if private)"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.NestedCommandsIrcProxy.reply:14
|
|
msgid "`sendImmediately=False`: True if the reply should use sendMsg() which"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.NestedCommandsIrcProxy.reply:14
|
|
msgid ""
|
|
"bypasses conf.supybot.protocols.irc.throttleTime and gets sent before any"
|
|
" queued messages"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.PluginMixin:1
|
|
msgid ""
|
|
"Bases: :class:`supybot.callbacks.BasePlugin`, "
|
|
":class:`supybot.irclib.IrcCallback`"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.ReplyIrcProxy:1
|
|
msgid "Bases: :class:`supybot.callbacks.RichReplyMethods`"
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.ReplyIrcProxy:1
|
|
msgid ""
|
|
"This class is a thin wrapper around an irclib.Irc object that gives it "
|
|
"the reply() and error() methods (as well as everything in "
|
|
"RichReplyMethods, based on those two)."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.ReplyIrcProxy.getRealIrc:1
|
|
msgid "Returns the real irclib.Irc object underlying this proxy chain."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.RichReplyMethods:1
|
|
msgid ""
|
|
"This is a mixin so these replies need only be defined once. It operates "
|
|
"under several assumptions, including the fact that 'self' is an Irc "
|
|
"object of some sort and there is a self.msg that is an IrcMsg."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.SilentError:1
|
|
msgid "An error that we should not notify the user."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.addressed:1
|
|
msgid ""
|
|
"If msg is addressed to 'name', returns the portion after the address. "
|
|
"Otherwise returns the empty string."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.canonicalName:1
|
|
msgid "Turn a command into its canonical form."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.canonicalName:3
|
|
msgid ""
|
|
"Currently, this makes everything lowercase and removes all dashes and "
|
|
"underscores."
|
|
msgstr ""
|
|
|
|
#: of supybot.callbacks.error:1
|
|
msgid "Makes an error reply to msg with the appropriate error payload."
|
|
msgstr ""
|
|
|
|
#: ../../develop/capabilities.rst:3
|
|
msgid "Capabilities"
|
|
msgstr ""
|
|
|
|
#: ../../develop/capabilities.rst:6
|
|
msgid ""
|
|
"I wrote this section with the little knowledge I have of the capabilities"
|
|
" system; I work mainly by testing different possibilities until I get "
|
|
"what I want. As for all the documentation, feel free to contact me to "
|
|
"correct/enhance it."
|
|
msgstr ""
|
|
|
|
#: ../../develop/capabilities.rst:12
|
|
msgid ""
|
|
"First, you should know how capabilities work :ref:`on the user side "
|
|
"<capabilities>`."
|
|
msgstr ""
|
|
|
|
#: ../../develop/capabilities.rst:16
|
|
msgid "Checking for a capability given its name"
|
|
msgstr ""
|
|
|
|
#: ../../develop/capabilities.rst:18
|
|
msgid "You only have to use ``ircdb.checkCapability(prefix, 'capability')``."
|
|
msgstr ""
|
|
|
|
#: ../../develop/capabilities.rst:20
|
|
msgid ""
|
|
"You can also override some behavior of the capability system. Here is the"
|
|
" complete documentation of ``ircdb.checkCapabiltiy``:"
|
|
msgstr ""
|
|
|
|
#: of supybot.ircdb.checkCapability:1
|
|
msgid "Checks that the user specified by name/hostmask has the capability given."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircdb.checkCapability:3
|
|
msgid ""
|
|
"``users`` and ``channels`` default to ``ircdb.users`` and "
|
|
"``ircdb.channels``."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircdb.checkCapability:6
|
|
msgid ""
|
|
"``ignoreOwner``, ``ignoreChannelOp``, and ``ignoreDefaultAllow`` are used"
|
|
" to override default behavior of the capability system in special cases "
|
|
"(actually, in the AutoMode plugin):"
|
|
msgstr ""
|
|
|
|
#: of supybot.ircdb.checkCapability:10
|
|
msgid "``ignoreOwner`` disables the behavior \"owners have all capabilites\""
|
|
msgstr ""
|
|
|
|
#: of supybot.ircdb.checkCapability:11
|
|
msgid ""
|
|
"``ignoreChannelOp`` disables the behavior \"channel ops have all channel "
|
|
"capabilities\""
|
|
msgstr ""
|
|
|
|
#: of supybot.ircdb.checkCapability:13
|
|
msgid ""
|
|
"``ignoreDefaultAllow`` disables the behavior \"if a user does not have a "
|
|
"capability or the associated anticapability, then they have the "
|
|
"capability\""
|
|
msgstr ""
|
|
|
|
#: ../../develop/capabilities.rst:27
|
|
msgid "Manipulating capability names"
|
|
msgstr ""
|
|
|
|
#: ../../develop/capabilities.rst:29
|
|
msgid ""
|
|
"Althrough you can manipulate capability names with string operations, "
|
|
"Supybot provides a few methods to do that “in the abstract” (could be "
|
|
"useful if we change the capability syntax one day…):"
|
|
msgstr ""
|
|
|
|
#: of supybot.ircdb.makeChannelCapability:1
|
|
msgid "Makes a channel capability given a channel and a capability."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircdb.isChannelCapability:1
|
|
msgid "Returns True if capability is a channel capability; False otherwise."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircdb.makeAntiCapability:1
|
|
msgid "Returns the anticapability of a given capability."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircdb.unAntiCapability:1
|
|
msgid "Takes an anticapability and returns the non-anti form."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircdb.invertCapability:1
|
|
msgid "Make a capability into an anticapability and vice versa."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircdb.isAntiCapability:1
|
|
msgid "Returns True if capability is an anticapability; False otherwise."
|
|
msgstr ""
|
|
|
|
#: ../../develop/commands.rst:5
|
|
msgid "supybot.commands"
|
|
msgstr ""
|
|
|
|
#: of supybot.commands:1
|
|
msgid "Includes wrappers for commands."
|
|
msgstr ""
|
|
|
|
#: of supybot.commands.any:1 supybot.commands.additional:1
|
|
#: supybot.commands.rest:1 supybot.commands.getopts:1 supybot.commands.first:1
|
|
#: supybot.commands.reverse:1 supybot.commands.commalist:1
|
|
msgid "Bases: :class:`supybot.commands.context`"
|
|
msgstr ""
|
|
|
|
#: of supybot.commands.many:1
|
|
msgid "Bases: :class:`supybot.commands.any`"
|
|
msgstr ""
|
|
|
|
#: of supybot.commands.optional:1
|
|
msgid "Bases: :class:`supybot.commands.additional`"
|
|
msgstr ""
|
|
|
|
#: of supybot.commands.getopts:1
|
|
msgid ""
|
|
"The empty string indicates that no argument is taken; None indicates that"
|
|
" there is no converter for the argument."
|
|
msgstr ""
|
|
|
|
#: of supybot.commands.urlSnarfer:1
|
|
msgid "Protects the snarfer from loops (with other bots) and whatnot."
|
|
msgstr ""
|
|
|
|
#: of supybot.commands.thread:1
|
|
msgid "Makes sure a command spawns a thread when called."
|
|
msgstr ""
|
|
|
|
#: of supybot.commands.wrap:1
|
|
msgid "Useful wrapper for plugin commands."
|
|
msgstr ""
|
|
|
|
#: of supybot.commands.wrap:3
|
|
msgid ""
|
|
"Valid converters are: admin, anything, banmask, boolean, "
|
|
"callerInGivenChannel, capability, channel, channelDb, channelOrGlobal, "
|
|
"checkCapability, checkCapabilityButIgnoreOwner, checkChannelCapability, "
|
|
"color, commandName, email, expiry, filename, float, glob, halfop, "
|
|
"haveHalfop, haveHalfop+, haveOp, haveOp+, haveVoice, haveVoice+, "
|
|
"hostmask, httpUrl, id, inChannel, index, int, ip, isGranted, letter, "
|
|
"literal, long, lowered, matches, networkIrc, nick, nickInChannel, nonInt,"
|
|
" nonNegativeInt, now, onlyInChannel, op, otherUser, owner, plugin, "
|
|
"positiveInt, private, public, regexpMatcher, regexpMatcherMany, "
|
|
"regexpReplacer, seenNick, something, somethingWithoutSpaces, text, to, "
|
|
"url, user, validChannel, voice."
|
|
msgstr ""
|
|
|
|
#: of supybot.commands.wrap:5
|
|
msgid "A command, taking (self, irc, msg, args, ...) as arguments"
|
|
msgstr ""
|
|
|
|
#: of supybot.commands.wrap:6
|
|
msgid "A list of converters and contexts"
|
|
msgstr ""
|
|
|
|
#: of supybot.commands.process:1
|
|
msgid "Runs a function <f> in a subprocess."
|
|
msgstr ""
|
|
|
|
#: of supybot.commands.process:3
|
|
msgid ""
|
|
"Several extra keyword arguments can be supplied. <pn>, the pluginname, "
|
|
"and <cn>, the command name, are strings used to create the process name, "
|
|
"for identification purposes. <timeout>, if supplied, limits the length of"
|
|
" execution of target function to <timeout> seconds."
|
|
msgstr ""
|
|
|
|
#: of supybot.commands.regexp_wrapper:1
|
|
msgid "A convenient wrapper to stuff regexp search queries through a subprocess."
|
|
msgstr ""
|
|
|
|
#: of supybot.commands.regexp_wrapper:3
|
|
msgid ""
|
|
"This is used because specially-crafted regexps can use exponential time "
|
|
"and hang the bot."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:3
|
|
msgid "Special methods and catching events"
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:5
|
|
msgid ""
|
|
"This page is a non-exhaustive list of special plugin method names and "
|
|
"events catchable via those methods (other events include "
|
|
":ref:`configuration hooks <configuration-hooks>` and :ref:`HTTP server "
|
|
"callbacks <http_plugins>`)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:10
|
|
msgid ""
|
|
"All methods here are defined in ``supybot-callbacks-plugin``. You may "
|
|
"override them if you need, but make sure you call the parent's one unless"
|
|
" you actually don't want to do it."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:14
|
|
msgid ""
|
|
"In case multiple plugins implement the same special methods, the order "
|
|
"they are called depends on the ``callAfter`` and ``callBefore`` (lists of"
|
|
" plugin names) attributes of the plugins, if they are set."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:19
|
|
msgid "Loading and unloading"
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:21
|
|
msgid ""
|
|
"The ``__init__`` method gets called with an Irc object as a parameter "
|
|
"when a plugin is loaded (or has just been reloaded). Make sure you always"
|
|
" call the parent's ``__init__``."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:25
|
|
msgid ""
|
|
"When a plugin is unloaded (or is to be reloaded), the ``die`` method is "
|
|
"called (with no parameter). Also make sure you always call the parent's "
|
|
"``die``."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:30
|
|
msgid "Commands and numerics"
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:32
|
|
msgid ""
|
|
"You can catch commands directly with “do-methods”: when the bot receives "
|
|
"a ``PRIVMSG``, all ``doPrivmsg`` methods are called; when it gets a "
|
|
"``437`` message, all ``do437`` methods are called, etc."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:36
|
|
msgid ""
|
|
"Those command take two commands: an :ref:`Irc object <supybot-irclib-"
|
|
"irc>` and a :ref:`IrcMsg object <supybot-ircmsgs>`."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:39
|
|
msgid "To get a list of all possible messages, check IRC RFCs."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:42
|
|
msgid "Filters"
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:44
|
|
msgid ""
|
|
"The ``inFilter`` and ``outFilter`` methods allow you to “intercept” "
|
|
"messages between the bot and the network and to alter them."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:47
|
|
msgid ""
|
|
"``inFilter`` gets messages just after they are parsed from network; and "
|
|
"its return value is fed to the bot. ``outFilter`` does the opposite: it "
|
|
"get any message the bot is about the send, and returns a message (which "
|
|
"may be the same) that will be sent instead."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:57
|
|
msgid "Commands handling"
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:60
|
|
msgid "Command dispatching"
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:63
|
|
msgid ""
|
|
"I wrote this subsection with the little knowledge I have of the commands "
|
|
"handling (all I know comes from hacks I made to write the Aka plugin), so"
|
|
" keep in mind some informations might be wrong. As for all the "
|
|
"documentation, feel free to contact me to correct/enhance it."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:70
|
|
msgid ""
|
|
"``isCommandMethod`` takes a command name as a string (which may contain "
|
|
"spaces) and returns a boolean telling if the plugin provides this "
|
|
"command."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:72
|
|
msgid ""
|
|
"``listCommands`` returns a list of command names as strings (which may "
|
|
"contain spaces)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:74
|
|
msgid ""
|
|
"``getCommand`` takes a potential command name as a list of strings, and "
|
|
"returns a truncated list corresponding to the name of a command provided "
|
|
"by the plugin. If no command match, it returns an empty list."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:77
|
|
msgid ""
|
|
"``getCommandMethod`` takes a command name as a list of strings and "
|
|
"returns the corresponding method/function."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:79
|
|
msgid ""
|
|
"``callCommand`` gets a command name as a list of strings, an irc object, "
|
|
"an msg object, and extra arguments (with `*args` and `**kwargs`), calls "
|
|
"``getCommandMethod`` to get the command method, and calls it with the "
|
|
"arguments. It also calls the functions in ``pre_command_callback``."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:86
|
|
msgid "Pre-command-call callbacks"
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:89
|
|
msgid ""
|
|
"Until stock Supybot and Gribble merge this feature, this section only "
|
|
"applies to Limnoria"
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:92
|
|
msgid ""
|
|
"If you want a function of your plugin to be called before every command "
|
|
"call, you can add it to the ``pre_command_callback`` attribute of your "
|
|
"plugin (actually, it is a static class attribute, so make sure you *add* "
|
|
"it to the list and don't touch other items of the list)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:97
|
|
msgid ""
|
|
"At every command call, *all* callbacks are called, and if *any* of them "
|
|
"returns ``True``, the command is not called."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:101
|
|
msgid "Other command-related events"
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:103
|
|
msgid ""
|
|
"all ``invalidCommand`` methods get called (with an Irc object, an IrcMsg "
|
|
"objet, and a list of token) when a user calls a command that no plugin "
|
|
"provides."
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:109
|
|
msgid "Regular expression triggered events"
|
|
msgstr ""
|
|
|
|
#: ../../develop/events.rst:111
|
|
msgid ""
|
|
"The :class:`supybot.callbacks.PluginRegexp` class provides some utilities"
|
|
" for creating plugins that act on regular expression matching."
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:5
|
|
msgid "Using Limnoria's HTTP server in your plugins"
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:11
|
|
msgid ""
|
|
"Until stock Supybot or Gribble merge the HTTP server, this documentation "
|
|
"only applies to Limnoria."
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:14
|
|
msgid ""
|
|
"Limnoria provides an HTTP server to plugins. This is not relevant for "
|
|
"most plugins, but some of them have to start a server (either for serving"
|
|
" a website or for being remotely called). The HTTP server provided by "
|
|
"Limnoria aims at starting a single server for all of them, which means "
|
|
"less used port and less resources usage."
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:20
|
|
msgid ""
|
|
"Some example plugins are `Factoids`_, `WebStats`_, `GitHub`_, UfrPaste, "
|
|
"and `WebDoc`_"
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:30
|
|
msgid "Using the HTTP server in a plugin"
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:32
|
|
msgid ""
|
|
"Let's try to make a basic dictionary about Supybot! We'll call it "
|
|
"Supystory."
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:34
|
|
msgid ""
|
|
"We want to get plain text information about Supybot, Gribble, and "
|
|
"Limnoria when accessing http://localhost:8080/supystory/supybot, "
|
|
"http://localhost:8080/supystory/gribble, and "
|
|
"http://localhost:8080/supystory/limnoria, an index page, and an HTML "
|
|
"error page if the page is not found"
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:41
|
|
msgid "Importing the HTTP server"
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:43
|
|
msgid "On only have to add this line::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:48
|
|
msgid "Creating a callback"
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:50
|
|
msgid ""
|
|
"If you are familiar with `BaseHTTPServer`, you will recognize the design,"
|
|
" except you don't need to subclass BaseHTTPServer, because I already did "
|
|
"it in *supybot.httpserver*."
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:54
|
|
msgid ""
|
|
"Now, you have to subclass `httpserver.SupyHTTPServerCallback`. A callback"
|
|
" is pretty much like an handler, but this is not an handler, because a "
|
|
"callback is called by the handler."
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:58
|
|
msgid "Here is how to do it::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:63
|
|
msgid ""
|
|
"Now, you have to register the callback, because the HTTP server does not "
|
|
"know what subdirectory it should assign to your callback. Do it with "
|
|
"adding a *__init__* to your plugin (read Supybot's docs about it, for "
|
|
"more informations)::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:78
|
|
msgid ""
|
|
"By the way, don't forget to unhook your callback when unloading your "
|
|
"plugin, unless what it will be impossible to reload the plugin! Append "
|
|
"this code to the following::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:89
|
|
msgid ""
|
|
"Now, you can load your plugin, and you'll see on the server a beautiful "
|
|
"link to `/supystory` called `Supystory`."
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:93
|
|
msgid "Overriding the default error message"
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:95
|
|
msgid ""
|
|
"But our plugin does not do anything for the moment. If click the link, "
|
|
"you'll get this message::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:102
|
|
msgid ""
|
|
"That mean your browser sent a GET request, but you didn't teach your "
|
|
"plugin how to handle it. First, we'll change this error message. Here is "
|
|
"a new code for your callback::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:111
|
|
msgid ""
|
|
"Now, you'll get your customized message. But your plugin still doesn't "
|
|
"work. You need to implement the GET request."
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:115
|
|
msgid "Implementing the GET request"
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:117
|
|
msgid ""
|
|
"As I said before, callbacks are pretty much like handlers. In order to "
|
|
"handle GET requests, you have to implement a method called... `doGet` (if"
|
|
" you used BaseHTTPServer, you will notice this is not do_GET, because "
|
|
"doGet is more homogeneous with Supybot naming style: `doPrivmsg`, "
|
|
"`doPing`, and so on)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:122
|
|
msgid ""
|
|
"You will get the handler and the URI as arguments. The handler is a "
|
|
"`BaseHTTPRequestHandler`_, and the URI is a string."
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:127
|
|
msgid "Here is the code of the callback... pretty much simple, as ever::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:192
|
|
msgid "Using templates"
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:194
|
|
msgid ""
|
|
"You may also want to allow your plugin's users to customize the web pages"
|
|
" without editing the source code of the plugin itself."
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:197
|
|
msgid ""
|
|
"Limnoria provides a template facility, which takes a file name, returns "
|
|
"the content of a file from the file system if it exists (the user-defined"
|
|
" template), and a default one otherwise (the developer's default "
|
|
"template). does not exist."
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:202
|
|
msgid ""
|
|
"In our case, we will do it only for the home page and the error page "
|
|
"(which are the only 'big' pages), like this::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/httpserver.rst:275
|
|
msgid ""
|
|
"Then, the user can change the template by copying "
|
|
"`data/web/supystory/index.html.example` to "
|
|
"`data/web/supystory/index.html` and editing it. (Same for `error.html`.)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/index.rst:5
|
|
msgid "Developing plugins for Limnoria"
|
|
msgstr ""
|
|
|
|
#: ../../develop/index.rst:8
|
|
msgid "Generic documentation"
|
|
msgstr ""
|
|
|
|
#: ../../develop/index.rst:20
|
|
msgid "Specific documentation"
|
|
msgstr ""
|
|
|
|
#: ../../develop/index.rst:31
|
|
msgid "Library reference"
|
|
msgstr ""
|
|
|
|
#: ../../develop/irclib.rst:5
|
|
msgid "supybot.irclib"
|
|
msgstr ""
|
|
|
|
#: ../../develop/irclib.rst:10
|
|
msgid "Irc"
|
|
msgstr ""
|
|
|
|
#: ../../develop/irclib.rst:12
|
|
msgid "It is usually the `irc` object given to plugin commands."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc:1 supybot.irclib.IrcState:1
|
|
#: supybot.irclib.IrcCallback:1
|
|
msgid ""
|
|
"Bases: :class:`supybot.irclib.IrcCommandDispatcher`, "
|
|
":class:`supybot.log.Firewalled`"
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc:1
|
|
msgid "The base class for an IRC connection."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc:3
|
|
msgid "Handles PING commands already."
|
|
msgstr ""
|
|
|
|
#: ../../develop/irclib.rst:20
|
|
msgid "Whether or not this object represents a living IRC connection."
|
|
msgstr ""
|
|
|
|
#: ../../develop/irclib.rst:22
|
|
msgid "bool"
|
|
msgstr ""
|
|
|
|
#: ../../develop/irclib.rst:26
|
|
msgid "The name of the network this object is connected to."
|
|
msgstr ""
|
|
|
|
#: ../../develop/irclib.rst:28
|
|
msgid "str"
|
|
msgstr ""
|
|
|
|
#: ../../develop/irclib.rst:32
|
|
msgid "float"
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.addCallback:1
|
|
msgid "Adds a callback to the callbacks list."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.addCallback:3
|
|
msgid "A callback object"
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.die:1
|
|
msgid ""
|
|
"Makes the Irc object *promise* to die -- but it won't die (of its own "
|
|
"volition) until all its queues are clear. Isn't that cool?"
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.do002:1
|
|
msgid "Logs the ircd version."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.do670:1
|
|
msgid "STARTTLS accepted."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.do691:1
|
|
msgid "STARTTLS refused."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.doError:1
|
|
msgid "Handles ERROR messages."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.doNick:1
|
|
msgid "Handles NICK messages."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.doPing:1
|
|
msgid "Handles PING messages."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.doPong:1
|
|
msgid "Handles PONG messages."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.feedMsg:1
|
|
msgid "Called by the IrcDriver; feeds a message received."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.getCallback:1
|
|
msgid "Gets a given callback by name."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.isChannel:1
|
|
msgid ""
|
|
"Helper function to check whether a given string is a channel on the "
|
|
"network this Irc object is connected to."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.monitor:1
|
|
msgid ""
|
|
"Increment a counter of how many callbacks monitor each target; and send a"
|
|
" MONITOR + to the server if the target is not yet monitored."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.queueMsg:1
|
|
msgid "Queues a message to be sent to the server."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.removeCallback:1
|
|
msgid "Removes a callback from the callback list."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.reset:1
|
|
msgid "Resets the Irc object. Called when the driver reconnects."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.sendMsg:1
|
|
msgid "Queues a message to be sent to the server *immediately*"
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.takeMsg:1
|
|
msgid "Called by the IrcDriver; takes a message to be sent."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Irc.unmonitor:1
|
|
msgid ""
|
|
"Decrements a counter of how many callbacks monitor each target; and send "
|
|
"a MONITOR - to the server if the counter drops to 0."
|
|
msgstr ""
|
|
|
|
#: ../../develop/irclib.rst:38
|
|
msgid "IrcState"
|
|
msgstr ""
|
|
|
|
#: ../../develop/irclib.rst:40
|
|
msgid ""
|
|
"Used mainly as the `state` attribute of :py:class:`supybot.irclib.Irc` "
|
|
"objects."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.IrcState:1
|
|
msgid "Maintains state of the Irc connection. Should also become smarter."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.IrcState.addMsg:1
|
|
msgid "Updates the state based on the irc object and the message."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.IrcState.do004:1
|
|
msgid "Handles parsing the 004 reply"
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.IrcState.do004:3
|
|
msgid "Supported user and channel modes are cached"
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.IrcState.getTopic:1
|
|
msgid "Returns the topic for a given channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.IrcState.nickToHostmask:1
|
|
msgid "Returns the hostmask for a given nick."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.IrcState.reset:1
|
|
msgid "Resets the state to normal, unconnected state."
|
|
msgstr ""
|
|
|
|
#: ../../develop/irclib.rst:50
|
|
msgid "ChannelState"
|
|
msgstr ""
|
|
|
|
#: ../../develop/irclib.rst:52
|
|
msgid ""
|
|
"Used mainly as the `channels['#chan']` attribute of "
|
|
":py:class:`supybot.irclib.Irc` objects."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.ChannelState:1
|
|
msgid "Bases: :class:`supybot.utils.python.Object`"
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.ChannelState.addUser:1
|
|
msgid "Adds a given user to the ChannelState. Power prefixes are handled."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.ChannelState.removeUser:1
|
|
msgid "Removes a given user from the channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.ChannelState.replaceUser:1
|
|
msgid "Changes the user oldNick to newNick; used for NICK changes."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Batch:1
|
|
msgid "Bases: :class:`tuple`"
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Batch.arguments:1
|
|
msgid "Alias for field number 1"
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Batch.messages:1
|
|
msgid "Alias for field number 2"
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.Batch.type:1
|
|
msgid "Alias for field number 0"
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.IrcCallback:1
|
|
msgid "Base class for standard callbacks."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.IrcCallback:3
|
|
msgid ""
|
|
"Callbacks derived from this class should have methods of the form "
|
|
"\"doCommand\" -- doPrivmsg, doNick, do433, etc. These will be called on "
|
|
"matching messages."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.IrcCallback.name:1
|
|
msgid "Returns the name of the callback."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.IrcCommandDispatcher:1
|
|
msgid "Base class for classes that must dispatch on a command."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.IrcMsgQueue:1
|
|
msgid "Class for a queue of IrcMsgs. Eventually, it should be smart."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.IrcMsgQueue:3
|
|
msgid ""
|
|
"Probably smarter than it is now, though it's gotten quite a bit smarter "
|
|
"than it originally was. A method to \"score\" methods, and a heapq to "
|
|
"maintain a priority queue of the messages would be the ideal way to do "
|
|
"intelligent queuing."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.IrcMsgQueue:8
|
|
msgid ""
|
|
"As it stands, however, we simply keep track of 'high priority' messages, "
|
|
"'low priority' messages, and normal messages, and just make sure to "
|
|
"return the 'high priority' ones before the normal ones before the 'low "
|
|
"priority' ones."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.IrcMsgQueue.dequeue:1
|
|
msgid "Dequeues a given message."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.IrcMsgQueue.enqueue:1
|
|
msgid "Enqueues a given message."
|
|
msgstr ""
|
|
|
|
#: of supybot.irclib.IrcMsgQueue.reset:1
|
|
msgid "Clears the queue."
|
|
msgstr ""
|
|
|
|
#: ../../develop/ircmsgs.rst:5
|
|
msgid "supybot.ircmsgs"
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs:1
|
|
msgid ""
|
|
"This module provides the basic IrcMsg object used throughout the bot to "
|
|
"represent the actual messages. It also provides several helper functions"
|
|
" to construct such messages in an easier way than the constructor for the"
|
|
" IrcMsg object (which, as you'll read later, is quite...full-featured :))"
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.IrcMsg:1
|
|
msgid "Class to represent an IRC message."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.IrcMsg:3
|
|
msgid ""
|
|
"As usual, ignore attributes that begin with an underscore. They simply "
|
|
"don't exist. Instances of this class are *not* to be modified, since "
|
|
"they are hashable. Public attributes of this class are .prefix, "
|
|
".command, .args, .nick, .user, and .host."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.IrcMsg:8
|
|
msgid ""
|
|
"The constructor for this class is pretty intricate. It's designed to "
|
|
"take any of three major (sets of) arguments."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.IrcMsg:11
|
|
msgid ""
|
|
"Called with no keyword arguments, it takes a single string that is a raw "
|
|
"IRC message (such as one taken straight from the network)."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.IrcMsg:14
|
|
msgid ""
|
|
"Called with keyword arguments, it *requires* a command parameter. Args "
|
|
"is optional, but with most commands will be necessary. Prefix is "
|
|
"obviously optional, since clients aren't allowed (well, technically, they"
|
|
" are, but only in a completely useless way) to send prefixes to the "
|
|
"server."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.IrcMsg:19
|
|
msgid ""
|
|
"Since this class isn't to be modified, the constructor also accepts a "
|
|
"'msg' keyword argument representing a message from which to take all the "
|
|
"attributes not provided otherwise as keyword arguments. So, for "
|
|
"instance, if a programmer wanted to take a PRIVMSG they'd gotten and "
|
|
"simply redirect it to a different source, they could do this:"
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.IrcMsg:25
|
|
msgid "IrcMsg(prefix='', args=(newSource, otherMsg.args[1]), msg=otherMsg)"
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.IrcMsg.tag:1
|
|
msgid "Affect a key:value pair to this message."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.IrcMsg.tagged:1
|
|
msgid "Get the value affected to a tag."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.action:1
|
|
msgid "Returns a PRIVMSG ACTION to recipient with the message msg."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.ban:1
|
|
msgid "Returns a MODE to ban nick on channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.bans:1
|
|
msgid "Returns a MODE to ban each of nicks on channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.dehalfop:1
|
|
msgid "Returns a MODE to dehalfop nick on channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.dehalfops:1
|
|
msgid "Returns a MODE to dehalfop each of nicks on channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.deop:1
|
|
msgid "Returns a MODE to deop nick on channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.deops:1
|
|
msgid "Returns a MODE to deop each of nicks on channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.devoice:1
|
|
msgid "Returns a MODE to devoice nick on channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.devoices:1
|
|
msgid "Returns a MODE to devoice each of nicks on channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.halfop:1
|
|
msgid "Returns a MODE to halfop nick on channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.halfops:1
|
|
msgid "Returns a MODE to halfop each of nicks on channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.invite:1
|
|
msgid "Returns an INVITE for nick."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.isAction:1
|
|
msgid "A predicate returning true if the PRIVMSG in question is an ACTION"
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.isCtcp:1
|
|
msgid "Returns whether or not msg is a CTCP message."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.join:1
|
|
msgid "Returns a JOIN to a channel"
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.joins:1
|
|
msgid "Returns a JOIN to each of channels."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.kick:1
|
|
msgid "Returns a KICK to kick nick from channel with the message msg."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.kicks:1
|
|
msgid "Returns a KICK to kick each of nicks from channel with the message msg."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.modes:1
|
|
msgid "Returns a MODE to quiet each of nicks on channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.nick:1
|
|
msgid "Returns a NICK with nick nick."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.notice:1
|
|
msgid "Returns a NOTICE to recipient with the message msg."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.op:1
|
|
msgid "Returns a MODE to op nick on channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.ops:1
|
|
msgid "Returns a MODE to op each of nicks on channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.part:1
|
|
msgid "Returns a PART from channel with the message msg."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.parts:1
|
|
msgid "Returns a PART from each of channels with the message msg."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.password:1
|
|
msgid "Returns a PASS command for accessing a server."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.ping:1
|
|
msgid "Takes a payload and returns the proper PING IrcMsg."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.pong:1
|
|
msgid "Takes a payload and returns the proper PONG IrcMsg."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.prettyPrint:1
|
|
msgid "Provides a client-friendly string form for messages."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.prettyPrint:3
|
|
msgid "IIRC, I copied BitchX's (or was it XChat's?) format for messages."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.privmsg:1
|
|
msgid "Returns a PRIVMSG to recipient with the message msg."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.quit:1
|
|
msgid "Returns a QUIT with the message msg."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.topic:1
|
|
msgid "Returns a TOPIC for channel with the topic topic."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.unAction:1
|
|
msgid "Returns the payload (i.e., non-ACTION text) of an ACTION msg."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.unban:1
|
|
msgid "Returns a MODE to unban nick on channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.unbans:1
|
|
msgid "Returns a MODE to unban each of nicks on channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.user:1
|
|
msgid "Returns a USER with ident ident and user user."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.voice:1
|
|
msgid "Returns a MODE to voice nick on channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.voices:1
|
|
msgid "Returns a MODE to voice each of nicks on channel."
|
|
msgstr ""
|
|
|
|
#: of supybot.ircmsgs.who:1
|
|
msgid "Returns a WHO for the hostmask or channel hostmaskOrChannel."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:3
|
|
msgid "Writing Your First Supybot Plugin"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:7
|
|
msgid ""
|
|
"Ok, so you want to write a plugin for Supybot. Good, then this is the "
|
|
"place to be. We're going to start from the top (the highest level, where "
|
|
"Supybot code does the most work for you) and move lower after that."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:11
|
|
msgid ""
|
|
"So have you used Supybot? If not, you need to go use it. This will help "
|
|
"you understand crucial things like the way the various commands work and "
|
|
"it is essential prior to embarking upon the plugin-development excursion "
|
|
"detailed in the following pages. If you haven't used Supybot, come back "
|
|
"to this document after you've used it for a while and gotten a feel for "
|
|
"it."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:17
|
|
msgid ""
|
|
"So, now that we know you've used Supybot, we'll start getting into "
|
|
"details. We'll go through this tutorial by actually writing a new plugin,"
|
|
" named Random with just a few simple commands."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:21
|
|
msgid ""
|
|
"Caveat: you'll need to have Supybot installed on the machine you intend "
|
|
"to develop plugins on. This will not only allow you to test the plugins "
|
|
"with a live bot, but it will also provide you with several nice scripts "
|
|
"which aid the development of plugins. Most notably, it provides you with "
|
|
"the supybot-plugin-create script which we will use in the next section..."
|
|
" Creating a minimal plugin This section describes using the 'supybot-"
|
|
"plugin-create' script to create a minimal plugin which we will enhance in"
|
|
" later sections."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:30
|
|
msgid ""
|
|
"The recommended way to start writing a plugin is to use the wizard "
|
|
"provided, :command:`supybot-plugin-create`. Run this from within your "
|
|
"local plugins directory, so we will be able to load the plugin and test "
|
|
"it out."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:34
|
|
msgid ""
|
|
"It's very easy to follow, because basically all you have to do is answer "
|
|
"three questions. Here's an example session::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:52
|
|
msgid ""
|
|
"It's that simple! Well, that part of making the minimal plugin is that "
|
|
"simple. You should now have a directory with a few files in it, so let's "
|
|
"take a look at each of those files and see what they're used for."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:57
|
|
msgid "README.md"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:58
|
|
msgid ""
|
|
"Open the file with notepad just as if it was a .txt file. In `README.md` "
|
|
"you put exactly what the boilerplate text says to put in there:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:61
|
|
msgid ""
|
|
"Insert a description of your plugin here, with any notes, etc. about "
|
|
"using it."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:64
|
|
msgid ""
|
|
"A brief overview of exactly what the purpose of the plugin is supposed to"
|
|
" do is really all that is needed here. Also, if this plugin requires any "
|
|
"third-party Python modules, you should definitely mention those here. You"
|
|
" don't have to describe individual commands or anything like that, as "
|
|
"those are defined within the plugin code itself as you'll see later. You "
|
|
"also don't need to acknowledge any of the developers of the plugin as "
|
|
"those too are handled elsewhere."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:71
|
|
msgid "For our Random plugin, let's make :file:`README.md` say this:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:73
|
|
msgid ""
|
|
"This plugin contains commands relating to random numbers, and includes: a"
|
|
" simple random number generator, the ability to pick a random number from"
|
|
" within a range, a command for returning a random sampling from a list of"
|
|
" items, and a simple dice roller."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:78
|
|
msgid ""
|
|
"And now you know what's in store for the rest of this tutorial, we'll be "
|
|
"writing all of that in one Supybot plugin, and you'll be surprised at "
|
|
"just how simple it is!"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:83
|
|
msgid "__init__.py"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:84
|
|
msgid ""
|
|
"The next file we'll look at is :file:`__init__.py`. If you're familiar "
|
|
"with the Python import mechanism, you'll know what this file is for. If "
|
|
"you're not, think of it as sort of the \"glue\" file that pulls all the "
|
|
"files in this directory together when you load the plugin. It's also "
|
|
"where there are a few administrative items live that you really need to "
|
|
"maintain."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:90
|
|
msgid ""
|
|
"Let's go through the file. For the first 30 lines or so, you'll see the "
|
|
"copyright notice that we use for our plugins, only with your name in "
|
|
"place (as prompted in :command:`supybot-plugin-create`). Feel free to use"
|
|
" whatever license you choose, we don't feel particularly attached to the "
|
|
"boilerplate code so it's yours to license as you see fit even if you "
|
|
"don't modify it. For our example, we'll leave it as is."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:97
|
|
msgid ""
|
|
"The plugin docstring immediately follows the copyright notice and it "
|
|
"(like :file:`README.txt`) tells you precisely what it should contain:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:100
|
|
msgid ""
|
|
"Add a description of the plugin (to be presented to the user inside the "
|
|
"wizard) here. This should describe *what* the plugin does."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:103
|
|
msgid ""
|
|
"The \"wizard\" that it speaks of is the :command:`supybot-wizard` script "
|
|
"that is used to create working Supybot config file. I imagine that in "
|
|
"meeting the prerequisite of \"using a Supybot\" first, most readers will "
|
|
"have already encountered this script. Basically, if the user selects to "
|
|
"look at this plugin from the list of plugins to load, it prints out that "
|
|
"description to let the user know what it does, so make sure to be clear "
|
|
"on what the purpose of the plugin is. This should be an abbreviated "
|
|
"version of what we put in our :file:`README.txt`, so let's put this::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:114
|
|
msgid ""
|
|
"Next in :file:`__init__.py` you see a few imports which are necessary, "
|
|
"and then four attributes that you need to modify for your bot and "
|
|
"preferably keep up with as you develop it: ``__version__``, "
|
|
"``__author__``, ``__contributors__``, ``__url__``."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:119
|
|
msgid ""
|
|
"``__version__`` is just a version string representing the current working"
|
|
" version of the plugin, and can be anything you want. If you use some "
|
|
"sort of RCS, this would be a good place to have it automatically "
|
|
"increment the version string for any time you edit any of the files in "
|
|
"this directory. We'll just make ours \"0.1\"."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:125
|
|
msgid ""
|
|
"``__author__`` should be an instance of the :class:`supybot.Author` "
|
|
"class. A :class:`supybot.Author` is simply created by giving it a full "
|
|
"name, a short name (preferably IRC nick), and an e-mail address (all of "
|
|
"these are optional, though at least the second one is expected). So, for "
|
|
"example, to create my Author user (though I get to cheat and use "
|
|
"supybot.authors.strike since I'm a main dev, muahaha), I would do::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:135
|
|
msgid "Keep this in mind as we get to the next item..."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:137
|
|
msgid ""
|
|
"``__contributors__`` is a dictionary mapping supybot.Author instances to "
|
|
"lists of things they contributed. If someone adds a command named foo to "
|
|
"your plugin, the list for that author should be ``[\"foo\"]``, or perhaps"
|
|
" even ``[\"added foo command\"]``. The main author shouldn't be "
|
|
"referenced here, as it is assumed that everything that wasn't contributed"
|
|
" by someone else was done by the main author. For now we have no "
|
|
"contributors, so we'll leave it blank."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:144
|
|
msgid ""
|
|
"Lastly, the ``__url__`` attribute should just reference the download URL "
|
|
"for the plugin. Since this is just an example, we'll leave this blank."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:147
|
|
msgid ""
|
|
"The rest of :file:`__init__.py` really shouldn't be touched unless you "
|
|
"are using third-party modules in your plugin. If you are, then you need "
|
|
"to take special note of the section that looks like this::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:158
|
|
msgid ""
|
|
"As the comment says, this is one place where you need to make sure you "
|
|
"import the third-party modules, and that you call :func:`reload` on them "
|
|
"as well. That way, if we are reloading a plugin on a running bot it will "
|
|
"actually reload the latest code. We aren't using any third-party modules,"
|
|
" so we can just leave this bit alone."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:164
|
|
msgid ""
|
|
"We're almost through the \"boring\" part and into the guts of writing "
|
|
"Supybot plugins, let's take a look at the next file."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:168
|
|
msgid "config.py"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:169
|
|
msgid ""
|
|
":file:`config.py` is, unsurprisingly, where all the configuration stuff "
|
|
"related to your plugin goes. If you're not familiar with Supybot's "
|
|
"configuration system, I recommend reading the config tutorial before "
|
|
"going any further with this section."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:174
|
|
msgid "So, let's plow through config.py line-by-line like we did the other files."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:176
|
|
msgid ""
|
|
"Once again, at the top is the standard copyright notice. Again, change it"
|
|
" to how you see fit."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:179
|
|
msgid "Then, some standard imports which are necessary."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:181
|
|
msgid ""
|
|
"Now, the first peculiar thing we get to is the configure function. This "
|
|
"function is what is called by the supybot-wizard whenever a plugin is "
|
|
"selected to be loaded. Since you've used the bot by now (as stated on the"
|
|
" first page of this tutorial as a prerequisite), you've seen what this "
|
|
"script does to configure plugins. The wizard allows the bot owner to "
|
|
"choose something different from the default plugin config values without "
|
|
"having to do it through the bot (which is still not difficult, but not as"
|
|
" easy as this). Also, note that the advanced argument allows you to "
|
|
"differentiate whether or not the person configuring this plugin considers"
|
|
" himself an advanced Supybot user. Our plugin has no advanced features, "
|
|
"so we won't be using it."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:192
|
|
msgid ""
|
|
"So, what exactly do we do in this configure function for our plugin? "
|
|
"Well, for the most part we ask questions and we set configuration values."
|
|
" You'll notice the import line with supybot.questions in it. That "
|
|
"provides some nice convenience functions which are used to (you guessed "
|
|
"it) ask questions. The other line in there is the conf.registerPlugin "
|
|
"line which registers our plugin with the config and allows us to create "
|
|
"configuration values for the plugin. You should leave these two lines in "
|
|
"even if you don't have anything else to put in here. For the vast "
|
|
"majority of plugins, you can leave this part as is, so we won't go over "
|
|
"how to write plugin configuration functions here (that will be handled in"
|
|
" a separate article). Our plugin won't be using much configuration, so "
|
|
"we'll leave this as is."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:204
|
|
msgid ""
|
|
"Next, you'll see a line that looks very similar to the one in the "
|
|
"configure function. This line is used not only to register the plugin "
|
|
"prior to being called in configure, but also to store a bit of an alias "
|
|
"to the plugin's config group to make things shorter later on. So, this "
|
|
"line should read::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:211
|
|
msgid ""
|
|
"Now we get to the part where we define all the configuration groups and "
|
|
"variables that our plugin is to have. Again, many plugins won't require "
|
|
"any configuration so we won't go over it here, but in a separate article "
|
|
"dedicated to sprucing up your config.py for more advanced plugins. Our "
|
|
"plugin doesn't require any config variables, so we actually don't need to"
|
|
" make any changes to this file at all."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:218
|
|
msgid ""
|
|
"Configuration of plugins is handled in depth at the Advanced Plugin "
|
|
"Config Tutorial"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:222
|
|
msgid "plugin.py"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:223
|
|
msgid ""
|
|
"Here's the moment you've been waiting for, the overview of plugin.py and "
|
|
"how to make our plugin actually do stuff."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:226
|
|
msgid ""
|
|
"At the top, same as always, is the standard copyright block to be used "
|
|
"and abused at your leisure."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:229
|
|
msgid ""
|
|
"Next, some standard imports. Not all of them are used at the moment, but "
|
|
"you probably will use many (if not most) of them, so just let them be. "
|
|
"Since we'll be making use of Python's standard 'random' module, you'll "
|
|
"need to add the following line to the list of imports::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:236
|
|
msgid ""
|
|
"Now, the plugin class itself. What you're given is a skeleton: a simple "
|
|
"subclass of callbacks.Plugin for you to start with. The only real content"
|
|
" it has is the boilerplate docstring, which you should modify to reflect "
|
|
"what the boilerplate text says - it should be useful so that when someone"
|
|
" uses the plugin help command to determine how to use this plugin, "
|
|
"they'll know what they need to do. Ours will read something like::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:251
|
|
msgid ""
|
|
"It's basically a \"guide to getting started\" for the plugin. Now, to "
|
|
"make the plugin do something. First of all, to get any random numbers "
|
|
"we're going to need a random number generator (RNG). Pretty much "
|
|
"everything in our plugin is going to use it, so we'll define it in the "
|
|
"constructor of our plugin, __init__. Here we'll also seed it with the "
|
|
"current time (standard practice for RNGs). Here's what our __init__ looks"
|
|
" like::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:264
|
|
msgid ""
|
|
"Now, the first two lines may look a little daunting, but it's just "
|
|
"administrative stuff required if you want to use a custom __init__. If we"
|
|
" didn't want to do so, we wouldn't have to, but it's not uncommon so I "
|
|
"decided to use an example plugin that did. For the most part you can just"
|
|
" copy/paste those lines into any plugin you override the __init__ for and"
|
|
" just change them to use the plugin name that you are working on instead."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:271
|
|
msgid ""
|
|
"So, now we have a RNG in our plugin, let's write a command to get a "
|
|
"random number. We'll start with a simple command named random that just "
|
|
"returns a random number from our RNG and takes no arguments. Here's what "
|
|
"that looks like::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:284
|
|
msgid "And that's it. Now here are the important points."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:286
|
|
msgid ""
|
|
"First and foremost, all plugin commands must have all-lowercase function "
|
|
"names. If they aren't all lowercase they won't show up in a plugin's list"
|
|
" of commands (nor will they be useable in general). If you look through a"
|
|
" plugin and see a function that's not in all lowercase, it is not a "
|
|
"plugin command. Chances are it is a helper function of some sort, and in "
|
|
"fact using capital letters is a good way of assuring that you don't "
|
|
"accidentally expose helper functions to users as commands."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:294
|
|
msgid ""
|
|
"You'll note the arguments to this class method are (self, irc, msg, "
|
|
"args). This is what the argument list for all methods that are to be used"
|
|
" as commands must start with. If you wanted additional arguments, you'd "
|
|
"append them onto the end, but since we take no arguments we just stop "
|
|
"there. I'll explain this in more detail with our next command, but it is "
|
|
"very important that all plugin commands are class methods that start with"
|
|
" those four arguments exactly as named."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:301
|
|
msgid ""
|
|
"Next, in the docstring there are two major components. First, the very "
|
|
"first line dictates the argument list to be displayed when someone calls "
|
|
"the help command for this command (i.e., help random). Then you leave a "
|
|
"blank line and start the actual help string for the function. Don't worry"
|
|
" about the fact that it's tabbed in or anything like that, as the help "
|
|
"command normalizes it to make it look nice. This part should be fairly "
|
|
"brief but sufficient to explain the function and what (if any) arguments "
|
|
"it requires. Remember that this should fit in one IRC message which is "
|
|
"typically around a 450 character limit."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:310
|
|
msgid ""
|
|
"Then we have the actual code body of the plugin, which consists of a "
|
|
"single line: irc.reply(str(self.rng.random())). The irc.reply function "
|
|
"issues a reply to wherever the PRIVMSG it received the command from with "
|
|
"whatever text is provided. If you're not sure what I mean when I say "
|
|
"\"wherever the PRIVMSG it received the command from\", basically it "
|
|
"means: if the command is issued in a channel the response is sent in the "
|
|
"channel, and if the command is issued in a private dialog the response is"
|
|
" sent in a private dialog. The text we want to display is simply the next"
|
|
" number from our RNG (self.rng). We get that number by calling the random"
|
|
" function, and then we str it just to make sure it is a nice printable "
|
|
"string."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:321
|
|
msgid ""
|
|
"Lastly, all plugin commands must be 'wrap'ed. What the wrap function does"
|
|
" is handle argument parsing for plugin commands in a very nice and very "
|
|
"powerful way. With no arguments, we simply need to just wrap it. For more"
|
|
" in-depth information on using wrap check out the wrap tutorial (The "
|
|
"astute Python programmer may note that this is very much like a "
|
|
"decorator, and that's precisely what it is. However, we developed this "
|
|
"before decorators existed and haven't changed the syntax due to our "
|
|
"earlier requirement to stay compatible with Python 2.3. As we now "
|
|
"require Python 2.4 or greater, this may eventually change to support work"
|
|
" via decorators.)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:331
|
|
msgid ""
|
|
"Now let's create a command with some arguments and see how we use those "
|
|
"in our plugin commands. Let's allow the user to seed our RNG with their "
|
|
"own seed value. We'll call the command seed and take just the seed value "
|
|
"as the argument (which we'll require be a floating point value of some "
|
|
"sort, though technically it can be any hashable object). Here's what this"
|
|
" command looks like::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:347
|
|
msgid ""
|
|
"You'll notice first that argument list now includes an extra argument, "
|
|
"seed. If you read the wrap tutorial mentioned above, you should "
|
|
"understand how this arg list gets populated with values. Thanks to wrap "
|
|
"we don't have to worry about type-checking or value-checking or anything "
|
|
"like that. We just specify that it must be a float in the wrap portion "
|
|
"and we can use it in the body of the function."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:354
|
|
msgid ""
|
|
"Of course, we modify the docstring to document this function. Note the "
|
|
"syntax on the first line. Arguments go in <> and optional arguments "
|
|
"should be surrounded by [] (we'll demonstrate this later as well)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:358
|
|
msgid ""
|
|
"The body of the function should be fairly straightforward to figure out, "
|
|
"but it introduces a new function - irc.replySuccess. This is just a "
|
|
"generic \"I succeeded\" command which responds with whatever the bot "
|
|
"owner has configured to be the success response (configured in "
|
|
"supybot.replies.success). Note that we don't do any error-checking in the"
|
|
" plugin, and that's because we simply don't have to. We are guaranteed "
|
|
"that seed will be a float and so the call to our RNG's seed is guaranteed"
|
|
" to work."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:366
|
|
msgid ""
|
|
"Lastly, of course, the wrap call. Again, read the wrap tutorial for "
|
|
"fuller coverage of its use, but the basic premise is that the second "
|
|
"argument to wrap is a list of converters that handles argument validation"
|
|
" and conversion and it then assigns values to each argument in the arg "
|
|
"list after the first four (required) arguments. So, our seed argument "
|
|
"gets a float, guaranteed."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:372
|
|
msgid ""
|
|
"With this alone you'd be able to make some pretty usable plugin commands,"
|
|
" but we'll go through two more commands to introduce a few more useful "
|
|
"ideas. The next command we'll make is a sample command which gets a "
|
|
"random sample of items from a list provided by the user::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:393
|
|
msgid ""
|
|
"This plugin command introduces a few new things, but the general "
|
|
"structure should look fairly familiar by now. You may wonder why we only "
|
|
"have two extra arguments when obviously this plugin can accept any number"
|
|
" of arguments. Well, using wrap we collect all of the remaining arguments"
|
|
" after the first one into the items argument. If you haven't caught on "
|
|
"yet, wrap is really cool and extremely useful."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:400
|
|
msgid ""
|
|
"Next of course is the updated docstring. Note the use of [] to denote the"
|
|
" optional items after the first item."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:403
|
|
msgid ""
|
|
"The body of the plugin should be relatively easy to read. First we check "
|
|
"and make sure that n (the number of items the user wants to sample) is "
|
|
"not larger than the actual number of items they gave. If it does, we call"
|
|
" irc.error with the error message you see. irc.error is kind of like "
|
|
"irc.replySuccess only it gives an error message using the configured "
|
|
"error format (in supybot.replies.error). Otherwise, we use the sample "
|
|
"function from our RNG to get a sample, then we sort it, and we reply with"
|
|
" the 'utils.str.commaAndify'ed version. The utils.str.commaAndify "
|
|
"function basically takes a list of strings and turns it into \"item1, "
|
|
"item2, item3, item4, and item5\" for an arbitrary length. More details on"
|
|
" using the utils module can be found in the utils tutorial."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:415
|
|
msgid ""
|
|
"Now for the last command that we will add to our plugin.py. This last "
|
|
"command will allow the bot users to roll an arbitrary n-sided die, with "
|
|
"as many sides as they so choose. Here's the code for this command::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:429
|
|
msgid ""
|
|
"The only new thing learned here really is that the irc.reply method "
|
|
"accepts an optional argument action, which if set to True makes the reply"
|
|
" an action instead. So instead of just crudely responding with the "
|
|
"number, instead you should see something like * supybot rolls a 5. You'll"
|
|
" also note that it uses a more advanced wrap line than we have used to "
|
|
"this point, but to learn more about wrap, you should refer to the wrap "
|
|
"tutorial"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:436
|
|
msgid ""
|
|
"And now that we're done adding plugin commands you should see the "
|
|
"boilerplate stuff at the bottom, which just consists of::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:441
|
|
msgid ""
|
|
"And also some vim modeline stuff. Leave these as is, and we're finally "
|
|
"done with plugin.py!"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:445
|
|
msgid "test.py"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:446
|
|
msgid ""
|
|
"Now that we've gotten our plugin written, we want to make sure it works. "
|
|
"Sure, an easy way to do a somewhat quick check is to start up a bot, load"
|
|
" the plugin, and run a few commands on it. If all goes well there, "
|
|
"everything's probably okay. But, we can do better than \"probably okay\"."
|
|
" This is where written plugin tests come in. We can write tests that not "
|
|
"only assure that the plugin loads and runs the commands fine, but also "
|
|
"that it produces the expected output for given inputs. And not only that,"
|
|
" we can use the nifty supybot-test script to test the plugin without even"
|
|
" having to have a network connection to connect to IRC with and most "
|
|
"certainly without running a local IRC server."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:456
|
|
msgid ""
|
|
"The boilerplate code for test.py is a good start. It imports everything "
|
|
"you need and sets up RandomTestCase which will contain all of our tests. "
|
|
"Now we just need to write some test methods. I'll be moving fairly "
|
|
"quickly here just going over very basic concepts and glossing over "
|
|
"details, but the full plugin test authoring tutorial has much more detail"
|
|
" to it and is recommended reading after finishing this tutorial."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:463
|
|
msgid ""
|
|
"Since we have four commands we should have at least four test methods in "
|
|
"our test case class. Typically you name the test methods that simply "
|
|
"checks that a given command works by just appending the command name to "
|
|
"test. So, we'll have testRandom, testSeed, testSample, and testDiceRoll. "
|
|
"Any other methods you want to add are more free-form and should describe "
|
|
"what you're testing (don't be afraid to use long names)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:470
|
|
msgid "First we'll write the testRandom method::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:476
|
|
msgid ""
|
|
"Since we can't predict what the output of our random number generator is "
|
|
"going to be, it's hard to specify a response we want. So instead, we just"
|
|
" make sure we don't get an error by calling the random command, and "
|
|
"that's about all we can do."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:481
|
|
msgid ""
|
|
"Next, testSeed. In this method we're just going to check that the command"
|
|
" itself functions. In another test method later on we will check and make"
|
|
" sure that the seed produces reproducible random numbers like we would "
|
|
"hope it would, but for now we just test it like we did random in "
|
|
"'testRandom'::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:490
|
|
msgid ""
|
|
"Now for testSample. Since this one takes more arguments it makes sense "
|
|
"that we test more scenarios in this one. Also this time we have to make "
|
|
"sure that we hit the error that we coded in there given the right "
|
|
"conditions::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:500
|
|
msgid ""
|
|
"So first we check and make sure trying to take a 20-element sample of a "
|
|
"1-element list gives us an error. Next we just check and make sure we get"
|
|
" the right number of elements and that they are formatted correctly when "
|
|
"we give 1, 2, or 3 element lists."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:505
|
|
msgid ""
|
|
"And for the last of our basic \"check to see that it works\" functions, "
|
|
"testDiceRoll::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:511
|
|
msgid ""
|
|
"We know that diceroll should return an action, and that with no arguments"
|
|
" it should roll a single-digit number. And that's about all we can test "
|
|
"reliably here, so that's all we do."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:515
|
|
msgid ""
|
|
"Lastly, we wanted to check and make sure that seeding the RNG with seed "
|
|
"actually took effect like it's supposed to. So, we write another test "
|
|
"method::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:528
|
|
msgid ""
|
|
"So we seed the RNG with 20, store the message, and then seed it at 20 "
|
|
"again. We grab that message, and unless they are the same number when we "
|
|
"compare the two, we fail. And then just to make sure our RNG is producing"
|
|
" random numbers, we get another random number and make sure it is "
|
|
"distinct from the prior one."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:534
|
|
msgid "Conclusion"
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:535
|
|
msgid ""
|
|
"You are now very well-prepared to write Supybot plugins. Now for a few "
|
|
"words of wisdom with regards to Supybot plugin-writing."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:538
|
|
msgid ""
|
|
"Read other people's plugins, especially the included plugins and ones by "
|
|
"the core developers. We (the Supybot dev team) can't possibly document "
|
|
"all the awesome things that Supybot plugins can do, but we try. "
|
|
"Nevertheless there are some really cool things that can be done that "
|
|
"aren't very well-documented."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:544
|
|
msgid ""
|
|
"Hack new functionality into existing plugins first if writing a new "
|
|
"plugin is too daunting."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:547
|
|
msgid ""
|
|
"Come ask us questions in #supybot on Freenode or OFTC. Going back to the "
|
|
"first point above, the developers themselves can help you even more than "
|
|
"the docs can (though we prefer you read the docs first)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:551
|
|
msgid ""
|
|
"Share your plugins with the world and make Supybot all that more "
|
|
"attractive for other users so they will want to write their plugins for "
|
|
"Supybot as well."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:555
|
|
msgid "Read, read, read all the documentation."
|
|
msgstr ""
|
|
|
|
#: ../../develop/plugin_tutorial.rst:557
|
|
msgid "And of course, have fun writing your plugins."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:3
|
|
msgid "Style Guidelines"
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:5
|
|
msgid ""
|
|
"**Note:** Code not following these style guidelines fastidiously is "
|
|
"likely (*very* likely) not to be accepted into the Supybot core."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:8
|
|
msgid ""
|
|
"Read :pep:`8` (Guido's Style Guide) and know that we use almost all the "
|
|
"same style guidelines."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:11
|
|
msgid ""
|
|
"Maximum line length is 79 characters. 78 is a safer bet, though. This is"
|
|
" **NON-NEGOTIABLE**. Your code will not be accepted while you are "
|
|
"violating this guidline."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:15
|
|
msgid ""
|
|
"Identation is 4 spaces per level. No tabs. This also is **NON-"
|
|
"NEGOTIABLE**. Your code, again, will *never* be accepted while you have "
|
|
"literal tabs in it."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:19
|
|
msgid ""
|
|
"Single quotes are used for all string literals that aren't docstrings. "
|
|
"They're just easier to type."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:22
|
|
msgid "Triple double quotes (``\"\"\"``) are always used for docstrings."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:24
|
|
msgid "Raw strings (``r''`` or ``r\"\"``) should be used for regular expressions."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:26
|
|
msgid ""
|
|
"Spaces go around all operators (except around ``=`` in default arguments "
|
|
"to functions) and after all commas (unless doing so keeps a line within "
|
|
"the 79 character limit)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:30
|
|
msgid ""
|
|
"Functions calls should look like ``foo(bar(baz(x), y))``. They should "
|
|
"not look like ``foo (bar (baz (x), y))``, or like ``foo(bar(baz(x), y) "
|
|
")`` or like anything else. I hate extraneous spaces."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:34
|
|
msgid ""
|
|
"Class names are StudlyCaps. Method and function names are camelCaps "
|
|
"(StudlyCaps with an initial lowercase letter). If variable and attribute"
|
|
" names can maintain readability without being camelCaps, then they should"
|
|
" be entirely in lowercase, otherwise they should also use camelCaps. "
|
|
"Plugin names are StudlyCaps."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:40
|
|
msgid ""
|
|
"Imports should always happen at the top of the module, one import per "
|
|
"line (so if imports need to be added or removed later, it can be done "
|
|
"easily)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:43
|
|
msgid ""
|
|
"Unless absolutely required by some external force, imports should be "
|
|
"ordered by the string length of the module imported. I just think it "
|
|
"looks prettier."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:47
|
|
msgid ""
|
|
"A blank line should be between all consecutive method declarations in a "
|
|
"class definition. Two blank lines should be between all consecutive "
|
|
"class definitions in a file. Comments are even better than blank lines "
|
|
"for separating classes."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:52
|
|
msgid ""
|
|
"Database filenames should generally begin with the name of the plugin and"
|
|
" the extension should be 'db'. plugins.DBHandler does this already."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:55
|
|
msgid ""
|
|
"Whenever creating a file descriptor or socket, keep a reference around "
|
|
"and be sure to close it. There should be no code like this::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:60
|
|
msgid "Instead, do this::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:68
|
|
msgid "This is to be sure the bot doesn't leak file descriptors."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:70
|
|
msgid ""
|
|
"All plugin files should include a docstring decsribing what the plugin "
|
|
"does. This docstring will be returned when the user is configuring the "
|
|
"plugin. All plugin classes should also include a docstring describing how"
|
|
" to do things with the plugin; this docstring will be returned when the "
|
|
"user requests help on a plugin name."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:76
|
|
msgid ""
|
|
"Method docstrings in classes deriving from callbacks.Privmsg should "
|
|
"include an argument list as their first line, and after that a blank line"
|
|
" followed by a longer description of what the command does. The argument"
|
|
" list is used by the ``syntax`` command, and the longer description is "
|
|
"used by the ``help`` command."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:82
|
|
msgid ""
|
|
"Whenever joining more than two strings, use string interpolation, not "
|
|
"addition::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:89
|
|
msgid ""
|
|
"This has to do with efficiency; the intermediate string x+y is made (and "
|
|
"thus copied) before x+y+z is made, so it's less efficient. People who "
|
|
"use string concatenation in a for loop will be swiftly kicked in the "
|
|
"head."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:93
|
|
#, python-format
|
|
msgid ""
|
|
"When writing strings that have formatting characters in them, don't 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 "
|
|
"purpose. If you got the ``%d`` wrong, you'll get an exception that says,"
|
|
" \"foo instance can't be converted to an integer.\" But if you use "
|
|
"``%s``, you'll get to see your nice little foo instance, if it doesn't "
|
|
"convert to a string cleanly, and if it does convert cleanly, you'll get "
|
|
"to see what you expect to see. Basically, ``%d`` just sucks."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:102
|
|
#, python-format
|
|
msgid ""
|
|
"As a corrolary to the above, note that sometimes ``%f`` is used, but on "
|
|
"when floats need to be formatted, e.g., ``%.2f``."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:105
|
|
msgid ""
|
|
"Use the log module to its fullest; when you need to print some values to "
|
|
"debug, use self.log.debug to do so, and leave those statements in the "
|
|
"code (commented out) so they can later be re-enabled. Remember that once"
|
|
" code is buggy, it tends to have more bugs, and you'll probably need "
|
|
"those print statements again."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:111
|
|
msgid ""
|
|
"While on the topic of logs, note that we do not use % (i.e., str.__mod__)"
|
|
" with logged strings; we simple pass the format parameters as additional "
|
|
"arguments. The reason is simple: the logging module supports it, and "
|
|
"it's cleaner (fewer tokens/glyphs) to read."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:116
|
|
msgid ""
|
|
"While still on the topic of logs, it's also important to pick the "
|
|
"appropriate log level for given information."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:119
|
|
msgid ""
|
|
"DEBUG: Appropriate to tell a programmer *how* we're doing something "
|
|
"(i.e., debugging printfs, basically). If you're trying to figure out why"
|
|
" your code doesn't work, DEBUG is the new printf -- use that, and leave "
|
|
"the statements in your code."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:124
|
|
msgid ""
|
|
"INFO: Appropriate to tell a user *what* we're doing, when what we're "
|
|
"doing isn't important for the user to pay attention to. A user who likes"
|
|
" to keep up with things should enjoy watching our logging at the INFO "
|
|
"level; it shouldn't be too low-level, but it should give enough "
|
|
"information that it keeps them relatively interested at peak times."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:130
|
|
msgid ""
|
|
"WARNING: Appropriate to tell a user when we're doing something that they"
|
|
" really ought to pay attention to. Users should see WARNING and think, "
|
|
"\"Hmm, should I tell the Supybot developers about this?\" Later, they "
|
|
"should decide not to, but it should give the user a moment to pause and "
|
|
"think about what's actually happening with their bot."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:136
|
|
msgid ""
|
|
"ERROR: Appropriate to tell a user when something has gone wrong. "
|
|
"Uncaught exceptions are ERRORs. Conditions that we absolutely want to "
|
|
"hear about should be errors. Things that should *scare* the user should "
|
|
"be errors."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:141
|
|
msgid ""
|
|
"CRITICAL: Not really appropriate. I can think of no absolutely critical "
|
|
"issue yet encountered in Supybot; the only possible thing I can imagine "
|
|
"is to notify the user that the partition on which Supybot is running has "
|
|
"filled up. That would be a CRITICAL condition, but it would also be hard"
|
|
" to log :)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:148
|
|
msgid ""
|
|
"All plugins should have test cases written for them. Even if it doesn't "
|
|
"actually test anything but just exists, it's good to have the test there "
|
|
"so there's a place to add more tests later (and so we can be sure that "
|
|
"all plugins are adequately documented; PluginTestCase checks that every "
|
|
"command has documentation)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:154
|
|
msgid ""
|
|
"All uses of eval() that expect to get integrated in Supybot must be "
|
|
"approved by jemfinch, no exceptions. Chances are, it won't be accepted."
|
|
" Have you looked at utils.safeEval?"
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:158
|
|
msgid ""
|
|
"SQL table names should be all-lowercase and include underscores to "
|
|
"separate words. This is because SQL itself is case-insensitive. This "
|
|
"doesn't change, however the fact that variable/member names should be "
|
|
"camel case."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:162
|
|
msgid "SQL statements in code should put SQL words in ALL CAPS::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:166
|
|
msgid "This makes SQL significantly easier to read."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:168
|
|
msgid "Common variable names"
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:170
|
|
msgid "L => an arbitrary list."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:172
|
|
msgid "t => an arbitrary tuple."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:174
|
|
msgid "x => an arbitrary float."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:176
|
|
msgid "s => an arbitrary string."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:178
|
|
msgid "f => an arbitrary function."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:180
|
|
msgid "p => an arbitrary predicate."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:182
|
|
msgid "i,n => an arbitrary integer."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:184
|
|
msgid "cb => an arbitrary callback."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:186
|
|
msgid "db => a database handle."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:188
|
|
msgid "fd => a file-like object."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:190
|
|
msgid "msg => an ircmsgs.IrcMsg object."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:192
|
|
msgid "irc => an irclib.Irc object (or proxy)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:194
|
|
msgid "nick => a string that is an IRC nick."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:196
|
|
msgid "channel => a string that is an IRC channel."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:198
|
|
msgid "hostmask => a string that is a user's IRC prefix."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:200
|
|
msgid ""
|
|
"When the semantic functionality (that is, the \"meaning\" of a variable "
|
|
"is obvious from context), one of these names should be used. This just "
|
|
"makes it easier for people reading our code to know what a variable "
|
|
"represents without scouring the surrounding code."
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:205
|
|
msgid ""
|
|
"Multiple variable assignments should always be surrounded with "
|
|
"parentheses -- i.e., if you're using the partition function, then your "
|
|
"assignment statement should look like::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/style.rst:211
|
|
msgid ""
|
|
"The parentheses make it obvious that you're doing a multiple assignment, "
|
|
"and that's important because I hate reading code and wondering where a "
|
|
"variable came from."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:3
|
|
msgid "Using Supybot's utils module"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:4
|
|
msgid ""
|
|
"Supybot provides a wealth of utilities for plugin writers in the "
|
|
"supybot.utils module, this tutorial describes these utilities and shows "
|
|
"you how to use them."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:8
|
|
msgid "str.py"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:10
|
|
msgid "The Format Function"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:12
|
|
#, python-format
|
|
msgid ""
|
|
"The supybot.utils.str module provides a bunch of utility functions for "
|
|
"handling string values. This section contains a quick rundown of all of "
|
|
"the functions available, along with descriptions of the arguments they "
|
|
"take. First and foremost is the format function, which provides a lot of "
|
|
"capability in just one function that uses string-formatting style to "
|
|
"accomplish a lot. So much so that it gets its own section in this "
|
|
"tutorial. All other functions will be in other sections. format takes "
|
|
"several arguments - first, the format string (using the format characters"
|
|
" described below), and then after that, each individual item to be "
|
|
"formatted. Do not attempt to use the % operator to do the formatting "
|
|
"because that will fall back on the normal string formatting operator. The"
|
|
" format function uses the following string formatting characters."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:24
|
|
msgid "% - literal ``%``"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:25
|
|
msgid "i - integer"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:26
|
|
msgid "s - string"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:27
|
|
msgid "f - float"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:28
|
|
msgid "r - repr"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:29
|
|
msgid "b - form of the verb ``to be`` (takes an int)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:30
|
|
msgid "h - form of the verb ``to have`` (takes an int)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:31
|
|
msgid "L - commaAndify (takes a list of strings or a tuple of ([strings], and))"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:32
|
|
msgid "p - pluralize (takes a string)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:33
|
|
msgid "q - quoted (takes a string)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:34
|
|
msgid ""
|
|
"n - n items (takes a 2-tuple of (n, item) or a 3-tuple of (n, between, "
|
|
"item))"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:35
|
|
msgid "S - a human-readable size (takes an int)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:36
|
|
msgid "t - time, formatted (takes an int)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:37
|
|
msgid "T - time delta, formatted (takes an int)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:38
|
|
msgid "u - url, wrapped in braces"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:39
|
|
msgid ""
|
|
"v - void, takes one or many arguments, but doesn't display it (useful for"
|
|
" translation)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:43
|
|
msgid ""
|
|
"Until stock Supybot and Gribble merge them, %S, %T, and %v are only "
|
|
"available in Limnoria."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:47
|
|
msgid "Here are a few examples to help elaborate on the above descriptions::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:72
|
|
msgid ""
|
|
"As you can see, you can combine all sorts of combinations of formatting "
|
|
"strings into one. In fact, that was the major motivation behind format. "
|
|
"We have specific functions that you can use individually for each of "
|
|
"those formatting types, but it became much easier just to use special "
|
|
"formatting chars and the format function than concatenating a bunch of "
|
|
"strings that were the result of other utils.str functions."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:80
|
|
msgid "The Other Functions"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:82
|
|
msgid ""
|
|
"These are the functions that can't be handled by format. They are sorted "
|
|
"in what I perceive to be the general order of usefulness (and I'm leaving"
|
|
" the ones covered by format for the next section)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:86
|
|
msgid ""
|
|
"ellipsisify(s, n) - Returns a shortened version of a string. Produces up "
|
|
"to the first n chars at the nearest word boundary."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:89
|
|
msgid "s: the string to be shortened"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:90
|
|
msgid "n: the number of characters to shorten it to"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:92
|
|
msgid ""
|
|
"perlReToPythonRe(s) - Converts a Perl-style regexp (e.g., \"/abcd/i\" or "
|
|
"\"m/abcd/i\") to an actual Python regexp (an re object)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:95 ../../develop/using_utils.rst:100
|
|
msgid "s: the regexp string"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:97
|
|
msgid ""
|
|
"perlReToReplacer(s) - converts a perl-style replacement regexp (eg, "
|
|
"\"s/foo/bar/g\") to a Python function that performs such a replacement"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:102
|
|
msgid ""
|
|
"dqrepr(s) - Returns a repr() of s guaranteed to be in double quotes. "
|
|
"(Double Quote Repr)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:105
|
|
msgid "s: the string to be double-quote repr()'ed"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:107
|
|
msgid ""
|
|
"toBool(s) - Determines whether or not a string means True or False and "
|
|
"returns the appropriate boolean value. True is any of \"true\", \"on\", "
|
|
"\"enable\", \"enabled\", or \"1\". False is any of \"false\", \"off\", "
|
|
"\"disable\", \"disabled\", or \"0\"."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:112
|
|
msgid "s: the string to determine the boolean value for"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:114
|
|
msgid ""
|
|
"rsplit(s, sep=None, maxsplit=-1) - functionally the same as str.split in "
|
|
"the Python standard library except splitting from the right instead of "
|
|
"the left. Python 2.4 has str.rsplit (which this function defers to for "
|
|
"those versions >= 2.4), but Python 2.3 did not."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:119
|
|
msgid "s: the string to be split"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:120
|
|
msgid "sep: the separator to split on, defaults to whitespace"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:121
|
|
msgid ""
|
|
"maxsplit: the maximum number of splits to perform, -1 splits all possible"
|
|
" splits."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:124
|
|
msgid ""
|
|
"normalizeWhitespace(s) - reduces all multi-spaces in a string to a single"
|
|
" space"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:127
|
|
msgid "s: the string to normalize"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:129
|
|
msgid "depluralize(s) - the opposite of pluralize"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:131
|
|
msgid "s: the string to depluralize"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:133
|
|
msgid ""
|
|
"unCommaThe(s) - Takes a string of the form \"foo, the\" and turns it into"
|
|
" \"the foo\""
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:136
|
|
msgid "s: string, the"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:138
|
|
msgid ""
|
|
"distance(s, t) - computes the levenshtein distance (or \"edit distance\")"
|
|
" between two strings"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:141
|
|
msgid "s: the first string"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:142
|
|
msgid "t: the second string"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:144
|
|
msgid "soundex(s, length=4) - computes the soundex for a given string"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:146
|
|
msgid "s: the string to compute the soundex for"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:147
|
|
msgid "length: the length of the soundex to generate"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:149
|
|
msgid ""
|
|
"matchCase(s1, s2) - Matches the case of the first string in the second "
|
|
"string."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:152
|
|
msgid "s1: the first string"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:153
|
|
msgid "s2: the string which will be made to match the case of the first"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:156
|
|
msgid "The Commands Format Already Covers"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:158
|
|
msgid ""
|
|
"These commands aren't necessary because you can achieve them more easily "
|
|
"by using the format command, but they exist if you decide you want to use"
|
|
" them anyway though it is greatly discouraged for general use."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:162
|
|
msgid ""
|
|
"commaAndify(seq, comma=\",\", And=\"and\") - transforms a list of items "
|
|
"into a comma separated list with an \"and\" preceding the last element. "
|
|
"For example, [\"foo\", \"bar\", \"baz\"] becomes \"foo, bar, and baz\". "
|
|
"Is smart enough to convert two-element lists to just \"item1 and item2\" "
|
|
"as well."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:167
|
|
msgid ""
|
|
"seq: the sequence of items (don't have to be strings, but need to be "
|
|
"'str()'-able)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:169
|
|
msgid "comma: the character to use to separate the list"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:170
|
|
msgid "And: the word to use before the last element"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:172
|
|
msgid ""
|
|
"pluralize(s) - Returns the plural of a string. Put any exceptions to the "
|
|
"general English rules of pluralization in the plurals dictionary in "
|
|
"supybot.utils.str."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:176
|
|
msgid "s: the string to pluralize"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:178
|
|
msgid ""
|
|
"nItems(n, item, between=None) - returns a string that describes a given "
|
|
"number of an item (with any string between the actual number and the item"
|
|
" itself), handles pluralization with the pluralize function above. Note "
|
|
"that the arguments here are in a different order since between is "
|
|
"optional."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:183
|
|
msgid "n: the number of items"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:184
|
|
msgid "item: the type of item"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:185
|
|
msgid ""
|
|
"between: the optional string that goes between the number and the type of"
|
|
" item"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:188
|
|
msgid "quoted(s) - Returns the string surrounded by double-quotes."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:190
|
|
msgid "s: the string to quote"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:192
|
|
msgid ""
|
|
"be(i) - Returns the proper form of the verb \"to be\" based on the number"
|
|
" provided (be(1) is \"is\", be(anything else) is \"are\")"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:195
|
|
msgid "i: the number of things that \"be\""
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:197
|
|
msgid ""
|
|
"has(i) - Returns the proper form of the verb \"to have\" based on the "
|
|
"number provided (has(1) is \"has\", has(anything else) is \"have\")"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:200
|
|
msgid "i: the number of things that \"has\""
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:203
|
|
msgid "structures.py"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:205 ../../develop/using_utils.rst:341
|
|
msgid "Intro"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:207
|
|
msgid ""
|
|
"This module provides a number of useful data structures that aren't found"
|
|
" in the standard Python library. For the most part they were created as "
|
|
"needed for the bot and plugins themselves, but they were created in such "
|
|
"a way as to be of general use for anyone who needs a data structure that "
|
|
"performs a like duty. As usual in this document, I'll try and order these"
|
|
" in order of usefulness, starting with the most useful."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:215
|
|
msgid "The queue classes"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:217
|
|
msgid ""
|
|
"The structures module provides two general-purpose queue classes for you "
|
|
"to use. The \"queue\" class is a robust full-featured queue that scales "
|
|
"up to larger sized queues. The \"smallqueue\" class is for queues that "
|
|
"will contain fewer (less than 1000 or so) items. Both offer the same "
|
|
"common interface, which consists of:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:223
|
|
msgid ""
|
|
"a constructor which will optionally accept a sequence to start the queue "
|
|
"off with"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:225
|
|
msgid "enqueue(item) - adds an item to the back of the queue"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:226
|
|
msgid "dequeue() - removes (and returns) the item from the front of the queue"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:227
|
|
msgid "peek() - returns the item from the front of the queue without removing it"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:228
|
|
msgid "reset() - empties the queue entirely"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:230
|
|
msgid ""
|
|
"In addition to these general-use queue classes, there are two other more "
|
|
"specialized queue classes as well. The first is the \"TimeoutQueue\" "
|
|
"which holds a queue of items until they reach a certain age and then they"
|
|
" are removed from the queue. It features the following:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:235
|
|
msgid ""
|
|
"TimeoutQueue(timeout, queue=None) - you must specify the timeout (in "
|
|
"seconds) in the constructor. Note that you can also optionally pass it a "
|
|
"queue which uses any implementation you wish to use whether it be one of "
|
|
"the above (queue or smallqueue) or if it's some custom queue you create "
|
|
"that implements the same interface. If you don't pass it a queue instance"
|
|
" to use, it will build its own using smallqueue."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:242
|
|
msgid "reset(), enqueue(item), dequeue() - all same as above queue classes"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:243
|
|
msgid "setTimeout(secs) - allows you to change the timeout value"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:245
|
|
msgid ""
|
|
"And for the final queue class, there's the \"MaxLengthQueue\" class. As "
|
|
"you may have guessed, it's a queue that is capped at a certain specified "
|
|
"length. It features the following:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:249
|
|
msgid ""
|
|
"MaxLengthQueue(length, seq=()) - the constructor naturally requires that "
|
|
"you set the max length and it allows you to optionally pass in a sequence"
|
|
" to be used as the starting queue. The underlying implementation is "
|
|
"actually the queue from before."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:254
|
|
msgid ""
|
|
"enqueue(item) - adds an item onto the back of the queue and if it would "
|
|
"push it over the max length, it dequeues the item on the front (it does "
|
|
"not return this item to you)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:257
|
|
msgid "all the standard methods from the queue class are inherited for this class"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:260
|
|
msgid "The Other Structures"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:262
|
|
msgid ""
|
|
"The most useful of the other structures is actually very similar to the "
|
|
"\"MaxLengthQueue\". It's the \"RingBuffer\", which is essentially a "
|
|
"MaxLengthQueue which fills up to its maximum size and then circularly "
|
|
"replaces the old contents as new entries are added instead of dequeuing."
|
|
" It features the following:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:268
|
|
msgid ""
|
|
"RingBuffer(size, seq=()) - as with the MaxLengthQueue you specify the "
|
|
"size of the RingBuffer and optionally give it a sequence."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:271
|
|
msgid ""
|
|
"append(item) - adds item to the end of the buffer, pushing out an item "
|
|
"from the front if necessary"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:273
|
|
msgid "reset() - empties out the buffer entirely"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:274
|
|
msgid "resize(i) - shrinks/expands the RingBuffer to the size provided"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:275
|
|
msgid ""
|
|
"extend(seq) - append the items from the provided sequence onto the end of"
|
|
" the RingBuffer"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:278
|
|
msgid ""
|
|
"The next data structure is the TwoWayDictionary, which as the name "
|
|
"implies is a dictionary in which key-value pairs have mappings going both"
|
|
" directions. It features the following:"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:282
|
|
msgid ""
|
|
"TwoWayDictionary(seq=(), \\**kwargs) - Takes an optional sequence of "
|
|
"(key, value) pairs as well as any key=value pairs specified in the "
|
|
"constructor as initial values for the two-way dict."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:286
|
|
msgid ""
|
|
"other than that, no extra features that a normal Python dict doesn't "
|
|
"already offer with the exception that any (key, val) pair added to the "
|
|
"dict is also added as (val, key) as well, so the mapping goes both ways. "
|
|
"Elements are still accessed the same way you always do with Python "
|
|
"'dict's."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:292
|
|
msgid ""
|
|
"There is also a MultiSet class available, but it's very unlikely that it "
|
|
"will serve your purpose, so I won't go into it here. The curious coder "
|
|
"can go check the source and see what it's all about if they wish (it's "
|
|
"only used once in our code, in the Relay plugin)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:298
|
|
msgid "web.py"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:299
|
|
msgid ""
|
|
"The web portion of Supybot's utils module is mainly used for retrieving "
|
|
"data from websites but it also has some utility functions pertaining to "
|
|
"HTML and email text as well. The functions in web are listed below, once "
|
|
"again in order of usefulness."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:304
|
|
msgid ""
|
|
"getUrl(url, size=None, headers=None) - gets the data at the URL provided "
|
|
"and returns it as one large string"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:307 ../../develop/using_utils.rst:315
|
|
msgid ""
|
|
"url: the location of the data to be retrieved or a urllib2.Request object"
|
|
" to be used in the retrieval"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:309
|
|
msgid ""
|
|
"size: the maximum number of bytes to retrieve, defaults to None, meaning "
|
|
"that it is to try to retrieve all data"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:311 ../../develop/using_utils.rst:317
|
|
msgid "headers: a dictionary mapping header types to header data"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:313
|
|
msgid "getUrlFd(url, headers=None) - returns a file-like object for a url"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:319
|
|
msgid ""
|
|
"htmlToText(s, tagReplace=\" \") - strips out all tags in a string of "
|
|
"HTML, replacing them with the specified character"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:322
|
|
msgid "s: the HTML text to strip the tags out of"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:323
|
|
msgid "tagReplace: the string to replace tags with"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:325
|
|
msgid ""
|
|
"strError(e) - pretty-printer for web exceptions, returns a descriptive "
|
|
"string given a web-related exception"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:328
|
|
msgid "e: the exception to pretty-print"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:330
|
|
msgid ""
|
|
"mungeEmail(s) - a naive e-mail obfuscation function, replaces \"@\" with "
|
|
"\"AT\" and \".\" with \"DOT\""
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:333
|
|
msgid "s: the e-mail address to obfuscate"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:335
|
|
msgid "getDomain(url) - returns the domain of a URL - url: the URL in question"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:339
|
|
msgid "The Best of the Rest"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:343
|
|
msgid ""
|
|
"Rather than document each of the remaining portions of the supybot.utils "
|
|
"module, I've elected to just pick out the choice bits from specific parts"
|
|
" and document those instead. Here they are, broken out by module name."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:348
|
|
msgid "supybot.utils.file - file utilities"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:350
|
|
msgid ""
|
|
"touch(filename) - updates the access time of a file by opening it for "
|
|
"writing and immediately closing it"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:353
|
|
msgid ""
|
|
"mktemp(suffix=\"\") - creates a decent random string, suitable for a "
|
|
"temporary filename with the given suffix, if provided"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:356
|
|
msgid ""
|
|
"the AtomicFile class - used for files that need to be atomically written,"
|
|
" i.e., if there's a failure the original file remains unmodified. For "
|
|
"more info consult file.py in src/utils"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:361
|
|
msgid "supybot.utils.gen - general utilities"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:363
|
|
msgid ""
|
|
"timeElapsed(elapsed, [lots of optional args]) - given the number of "
|
|
"seconds elapsed, returns a string with the English description of the "
|
|
"amount of time passed, consult gen.py in src/utils for the exact argument"
|
|
" list and documentation if you feel you could use this function."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:368
|
|
msgid ""
|
|
"exnToString(e) - improved exception-to-string function. Provides nicer "
|
|
"output than a simple str(e)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:371
|
|
msgid ""
|
|
"InsensitivePreservingDict class - a dict class that is case-insensitive "
|
|
"when accessing keys"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:375
|
|
msgid "supybot.utils.iter - iterable utilities"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:377
|
|
msgid "len(iterable) - returns the length of a given iterable"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:379
|
|
msgid ""
|
|
"groupby(key, iterable) - equivalent to the itertools.groupby function "
|
|
"available as of Python 2.4. Provided for backwards compatibility."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:382
|
|
msgid ""
|
|
"any(p, iterable) - Returns true if any element in the iterable satisfies "
|
|
"the predicate p"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:385
|
|
msgid ""
|
|
"all(p, iterable) - Returns true if all elements in the iterable satisfy "
|
|
"the predicate p"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:388
|
|
msgid "choice(iterable) - Returns a random element from the iterable"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:392
|
|
msgid "supybot.dynamicScope / dynamic - accessing variables in the stack"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:394
|
|
msgid ""
|
|
"This feature is not in `supybot.utils` but still deserves to be "
|
|
"documented as a utility."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:397
|
|
msgid ""
|
|
"Althrough you should avoid using this feature as long as you can, it is "
|
|
"sometimes necessary to access variables the Supybot API does not provide "
|
|
"you."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:400
|
|
msgid ""
|
|
"For instance, the `Aka` plugin provides per-channel aliases by overriding"
|
|
" :ref:`getCommandMethod <commands_handling>`. However, the channel where "
|
|
"the command is called is not passed to this functions, so when writing "
|
|
"`Aka` I could either add this parameter (and thus break all plugins all "
|
|
"plugins already overriding this method) or use this hack. I choosed this "
|
|
"hack."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:406
|
|
msgid ""
|
|
"How does it work? This is quite simple: ``dynamic.channel`` is a shortcut"
|
|
" for ``supybot.dynamicScope.DynamicScope.__getattr__('channel')``, which "
|
|
"browse the call stack backwards, looking for a variable named "
|
|
"``channel``, and then returns is as far as it finds it (and returns "
|
|
"``None`` if there is no such variale)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_utils.rst:412
|
|
msgid ""
|
|
"Note that you don't have to import ``dynamicScope``, the ``dynamic`` "
|
|
"object is automatically set as a global variable when Supybot starts."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:3
|
|
msgid "Using commands.wrap to parse your command's arguments"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst
|
|
msgid "Contents"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:9
|
|
msgid ""
|
|
"To plugin developers for older (pre-0.80) versions of Supybot, one of the"
|
|
" more annoying aspects of writing commands was handling the arguments "
|
|
"that were passed in. In fact, many commands often had to duplicate "
|
|
"parsing and verification code, resulting in lots of duplicated code for "
|
|
"not a whole lot of action. So, instead of forcing plugin writers to come "
|
|
"up with their own ways of cleaning it up, we wrote up the wrap function "
|
|
"to handle all of it."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:16
|
|
msgid ""
|
|
"It allows a much simpler and more flexible way of checking things than "
|
|
"before and it doesn't require that you know the bot internals to do "
|
|
"things like check and see if a user exists, or check if a command name "
|
|
"exists and whatnot."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:20
|
|
msgid ""
|
|
"If you are a plugin author this document is absolutely required reading, "
|
|
"as it will massively ease the task of writing commands."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:24
|
|
msgid "Using Wrap"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:25
|
|
msgid ""
|
|
"First off, to get the wrap function, it is recommended (strongly) that "
|
|
"you use the following import line::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:30
|
|
msgid ""
|
|
"This will allow you to access the wrap command (and it allows you to do "
|
|
"it without the commands prefix). Note that this line is added to the "
|
|
"imports of plugin templates generated by the supybot-plugin-create "
|
|
"script."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:34
|
|
msgid ""
|
|
"Let's write a quickie command that uses wrap to get a feel for how it "
|
|
"makes our lives better. Let's write a command that repeats a string of "
|
|
"text a given number of times. So you could say \"repeat 3 foo\" and it "
|
|
"would say \"foofoofoo\". Not a very useful command, but it will serve our"
|
|
" purpose just fine. Here's how it would be done without wrap::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:52
|
|
msgid ""
|
|
"Note that all of the argument validation and parsing takes up 5 of the 6 "
|
|
"lines (and you should have seen it before we had privmsg.getArgs!). Now, "
|
|
"here's what our command will look like with wrap applied::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:64
|
|
msgid ""
|
|
"Pretty short, eh? With wrap all of the argument parsing and validation is"
|
|
" handled for us and we get the arguments we want, formatted how we want "
|
|
"them, and converted into whatever types we want them to be - all in one "
|
|
"simple function call that is used to wrap the function! So now the code "
|
|
"inside each command really deals with how to execute the command and not "
|
|
"how to deal with the input."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:71
|
|
msgid ""
|
|
"So, now that you see the benefits of wrap, let's figure out what stuff we"
|
|
" have to do to use it."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:75
|
|
msgid "Syntax Changes"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:76
|
|
msgid ""
|
|
"There are two syntax changes to the old style that are implemented. "
|
|
"First, the definition of the command function must be changed. The basic "
|
|
"syntax for the new definition is::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:82
|
|
msgid ""
|
|
"Where arg1 and arg2 (up through as many as you want) are the variables "
|
|
"that will store the parsed arguments. \"Now where do these parsed "
|
|
"arguments come from?\" you ask. Well, that's where the second syntax "
|
|
"change comes in. The second syntax change is the actual use of the wrap "
|
|
"function itself to decorate our command names. The basic decoration "
|
|
"syntax is::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:92
|
|
msgid ""
|
|
"This should go on the line immediately following the body of the "
|
|
"command's definition, so it can easily be located (and it obviously must "
|
|
"go after the command's definition so that commandname is defined)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:96
|
|
msgid ""
|
|
"Each of the converters in the above listing should be one of the "
|
|
"converters in commands.py (I will describe each of them in detail later.)"
|
|
" The converters are applied in order to the arguments given to the "
|
|
"command, generally taking arguments off of the front of the argument list"
|
|
" as they go. Note that each of the arguments is actually a string "
|
|
"containing the NAME of the converter to use and not a reference to the "
|
|
"actual converter itself. This way we can have converters with names like "
|
|
"int and not have to worry about polluting the builtin namespace by "
|
|
"overriding the builtin int."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:105
|
|
msgid ""
|
|
"As you will find out when you look through the list of converters below, "
|
|
"some of the converters actually take arguments. The syntax for supplying "
|
|
"them (since we aren't actually calling the converters, but simply "
|
|
"specifying them), is to wrap the converter name and args list into a "
|
|
"tuple. For example::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:113
|
|
msgid ""
|
|
"For the most part you won't need to use an argument with the converters "
|
|
"you use either because the defaults are satisfactory or because it "
|
|
"doesn't even take any."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:118
|
|
msgid "Customizing Wrap"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:119
|
|
msgid ""
|
|
"Converters alone are a pretty powerful tool, but for even more advanced "
|
|
"(yet simpler!) argument handling you may want to use contexts. Contexts "
|
|
"describe how the converters are applied to the arguments, while the "
|
|
"converters themselves do the actual parsing and validation."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:124
|
|
msgid ""
|
|
"For example, one of the contexts is \"optional\". By using this context, "
|
|
"you're saying that a given argument is not required, and if the supplied "
|
|
"converter doesn't find anything it likes, we should use some default. Yet"
|
|
" another example is the \"reverse\" context. This context tells the "
|
|
"supplied converter to look at the last argument and work backwards "
|
|
"instead of the normal first-to-last way of looking at arguments."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:131
|
|
msgid ""
|
|
"So, that should give you a feel for the role that contexts play. They are"
|
|
" not by any means necessary to use wrap. All of the stuff we've done to "
|
|
"this point will work as-is. However, contexts let you do some very "
|
|
"powerful things in very easy ways, and are a good thing to know how to "
|
|
"use."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:136
|
|
msgid ""
|
|
"Now, how do you use them? Well, they are in the global namespace of "
|
|
"src/commands.py, so your previous import line will import them all; you "
|
|
"can call them just as you call wrap. In fact, the way you use them is you"
|
|
" simply call the context function you want to use, with the converter "
|
|
"(and its arguments) as arguments. It's quite simple. Here's an example::"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:144
|
|
msgid ""
|
|
"In this example, our command is looking for an optional integer argument "
|
|
"first. Then, after that, any number of arguments which can be anything "
|
|
"(as long as they are something, of course)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:148
|
|
msgid ""
|
|
"Do note, however, that the type of the arguments that are returned can be"
|
|
" changed if you apply a context to it. So, optional(\"int\") may very "
|
|
"well return None as well as something that passes the \"int\" converter, "
|
|
"because after all it's an optional argument and if it is None, that "
|
|
"signifies that nothing was there. Also, for another example, "
|
|
"many(\"something\") doesn't return the same thing that just \"something\""
|
|
" would return, but rather a list of \"something\"s."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:156
|
|
msgid "Converter List"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:157
|
|
msgid ""
|
|
"Below is a list of all the available converters to use with wrap. If the "
|
|
"converter accepts any arguments, they are listed after it and if they are"
|
|
" optional, the default value is shown."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:162
|
|
msgid "Numbers and time"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:164
|
|
msgid "expiry"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:166
|
|
msgid ""
|
|
"Takes a number of seconds and adds it to the current time to create an "
|
|
"expiration timestamp."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:169
|
|
msgid "id, kind=\"integer\""
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:171
|
|
msgid ""
|
|
"Returns something that looks like an integer ID number. Takes an optional"
|
|
" \"kind\" argument for you to state what kind of ID you are looking for, "
|
|
"though this doesn't affect the integrity-checking. Basically requires "
|
|
"that the argument be an integer, does no other integrity-checking, and "
|
|
"provides a nice error message with the kind in it."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:177
|
|
msgid "index"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:179
|
|
msgid ""
|
|
"Basically (\"int\", \"index\"), but with a twist. This will take a "
|
|
"1-based index and turn it into a 0-based index (which is more useful in "
|
|
"code). It doesn't transform 0, and it maintains negative indices as is "
|
|
"(note that it does allow them!)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:184
|
|
msgid "int, type=\"integer\", p=None"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:186
|
|
msgid ""
|
|
"Gets an integer. The \"type\" text can be used to customize the error "
|
|
"message received when the argument is not an integer. \"p\" is an "
|
|
"optional predicate to test the integer with. If p(i) fails (where i is "
|
|
"the integer arg parsed out of the argument string), the arg will not be "
|
|
"accepted."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:191
|
|
msgid "now"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:193
|
|
msgid ""
|
|
"Simply returns the current timestamp as an arg, does not reference or "
|
|
"modify the argument list."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:196
|
|
msgid "long, type=\"long\""
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:198
|
|
msgid ""
|
|
"Basically the same as int minus the predicate, except that it converts "
|
|
"the argument to a long integer regardless of the size of the int."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:201
|
|
msgid "float, type=\"floating point number\""
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:203
|
|
msgid ""
|
|
"Basically the same as int minus the predicate, except that it converts "
|
|
"the argument to a float."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:206
|
|
msgid "nonInt, type=\"non-integer value\""
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:208
|
|
msgid ""
|
|
"Accepts everything but integers, and returns them unchanged. The \"type\""
|
|
" value, as always, can be used to customize the error message that is "
|
|
"displayed upon failure."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:212
|
|
msgid "positiveInt"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:214
|
|
msgid "Accepts only positive integers."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:216
|
|
msgid "nonNegativeInt"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:218
|
|
msgid "Accepts only non-negative integers."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:221
|
|
msgid "Channel"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:223
|
|
msgid "channelDb"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:225
|
|
msgid ""
|
|
"Sets the channel appropriately in order to get to the databases for that "
|
|
"channel (handles whether or not a given channel uses channel-specific "
|
|
"databases and whatnot)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:229
|
|
msgid "channel"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:231
|
|
msgid ""
|
|
"Gets a channel to use the command in. If the channel isn't supplied, uses"
|
|
" the channel the message was sent in. If using a different channel, does "
|
|
"sanity-checking to make sure the channel exists on the current IRC "
|
|
"network."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:235
|
|
msgid "inChannel"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:237
|
|
msgid ""
|
|
"Requires that the command be called from within any channel that the bot "
|
|
"is currently in or with one of those channels used as an argument to the "
|
|
"command."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:241
|
|
msgid "onlyInChannel"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:243
|
|
msgid ""
|
|
"Requires that the command be called from within any channel that the bot "
|
|
"is currently in."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:246
|
|
msgid "callerInGivenChannel"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:248
|
|
msgid ""
|
|
"Takes the given argument as a channel and makes sure that the caller is "
|
|
"in that channel."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:251
|
|
msgid "public"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:253
|
|
msgid ""
|
|
"Requires that the command be sent in a channel instead of a private "
|
|
"message."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:256
|
|
msgid "private"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:258
|
|
msgid ""
|
|
"Requires that the command be sent in a private message instead of a "
|
|
"channel."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:261
|
|
msgid "validChannel"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:263
|
|
msgid "Gets a channel argument once it makes sure it's a valid channel."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:266
|
|
msgid "Words"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:268
|
|
msgid "color"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:270
|
|
msgid ""
|
|
"Accepts arguments that describe a text color code (e.g., \"black\", "
|
|
"\"light blue\") and returns the mIRC color code for that color. (Note "
|
|
"that many other IRC clients support the mIRC color code scheme, not just "
|
|
"mIRC)"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:274
|
|
msgid "letter"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:276
|
|
msgid ""
|
|
"Looks for a single letter. (Technically, it looks for any one-element "
|
|
"sequence)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:279
|
|
msgid "literal, literals, errmsg=None"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:281
|
|
msgid ""
|
|
"Takes a required sequence or string (literals) and any argument that "
|
|
"uniquely matches the starting substring of one of the literals is "
|
|
"transformed into the full literal. For example, with ``(\"literal\", "
|
|
"(\"bar\", \"baz\", \"qux\"))``, you'd get \"bar\" for \"bar\", \"baz\" "
|
|
"for \"baz\", and \"qux\" for any of \"q\", \"qu\", or \"qux\". \"b\" and "
|
|
"\"ba\" would raise errors because they don't uniquely identify one of the"
|
|
" literals in the list. You can override errmsg to provide a specific "
|
|
"(full) error message, otherwise the default argument error message is "
|
|
"displayed."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:290
|
|
msgid "lowered"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:292
|
|
msgid ""
|
|
"Returns the argument lowered (NOTE: it is lowered according to IRC "
|
|
"conventions, which does strange mapping with some punctuation "
|
|
"characters)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:295
|
|
msgid "to"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:297
|
|
msgid ""
|
|
"Returns the string \"to\" if the arg is any form of \"to\" (case-"
|
|
"insensitive)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:300
|
|
msgid "Network"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:302
|
|
msgid "ip"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:304
|
|
msgid ""
|
|
"Checks and makes sure the argument looks like a valid IP and then returns"
|
|
" it."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:307
|
|
msgid "url"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:309
|
|
msgid "Checks for a valid URL."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:311
|
|
msgid "httpUrl"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:313
|
|
msgid "Checks for a valid HTTP URL."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:316
|
|
msgid "Users, nicks, and permissions"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:318
|
|
msgid "haveOp, action=\"do that\""
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:320
|
|
msgid ""
|
|
"Simply requires that the bot have ops in the channel that the command is "
|
|
"called in. The action parameter completes the error message: \"I need to "
|
|
"be opped to ...\"."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:324
|
|
msgid "nick"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:326
|
|
msgid "Checks that the arg is a valid nick on the current IRC server."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:328
|
|
msgid "seenNick"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:330
|
|
msgid ""
|
|
"Checks that the arg is a nick that the bot has seen (NOTE: this is "
|
|
"limited by the size of the history buffer that the bot has)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:333
|
|
msgid "nickInChannel"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:335
|
|
msgid ""
|
|
"Requires that the argument be a nick that is in the current channel, and "
|
|
"returns that nick."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:338
|
|
msgid "capability"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:340
|
|
msgid "Used to retrieve an argument that describes a capability."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:342
|
|
msgid "hostmask"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:344
|
|
msgid "Returns the hostmask of any provided nick or hostmask argument."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:346
|
|
msgid "banmask"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:348
|
|
msgid "Returns a generic banmask of the provided nick or hostmask argument."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:350
|
|
msgid "user"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:352
|
|
msgid "Requires that the caller be a registered user."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:354
|
|
msgid "otherUser"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:356
|
|
msgid "Returns the user specified by the username or hostmask in the argument."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:358
|
|
msgid "owner"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:360
|
|
msgid "Requires that the command caller has the \"owner\" capability."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:362
|
|
msgid "admin"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:364
|
|
msgid "Requires that the command caller has the \"admin\" capability."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:366
|
|
msgid "checkCapability, capability"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:368
|
|
msgid "Checks to make sure that the caller has the specified capability."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:372
|
|
msgid "checkChannelCapability, capability"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:371
|
|
msgid ""
|
|
"Checks to make sure that the caller has the specified capability on the "
|
|
"channel the command is called in."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:375
|
|
msgid "Matching"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:377
|
|
msgid "anything"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:379
|
|
msgid "Returns anything as is."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:381
|
|
msgid "something, errorMsg=None, p=None"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:383
|
|
msgid ""
|
|
"Takes anything but the empty string. errorMsg can be used to customize "
|
|
"the error message. p is any predicate function that can be used to test "
|
|
"the validity of the input."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:387
|
|
msgid "somethingWithoutSpaces"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:389
|
|
msgid ""
|
|
"Same as something, only with the exception of disallowing spaces of "
|
|
"course."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:391
|
|
msgid "matches, regexp, errmsg"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:393
|
|
msgid ""
|
|
"Searches the args with the given regexp and returns the matches. If no "
|
|
"match is found, errmsg is given."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:396
|
|
msgid "regexpMatcher"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:398
|
|
msgid "Gets a matching regexp argument (m// or //)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:400
|
|
msgid "glob"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:402
|
|
msgid ""
|
|
"Gets a glob string. Basically, if there are no wildcards (``*``, ``?``) "
|
|
"in the argument, returns ``*string*``, making a glob string that matches "
|
|
"anything containing the given argument."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:406
|
|
msgid "regexpReplacer"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:408
|
|
msgid "Gets a replacing regexp argument (s//)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:411 ../../develop/using_wrap.rst:489
|
|
msgid "Other"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:413
|
|
msgid "networkIrc, errorIfNoMatch=False"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:415
|
|
msgid ""
|
|
"Returns the IRC object of the specified IRC network. If one isn't "
|
|
"specified, the IRC object of the IRC network the command was called on is"
|
|
" returned."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:419
|
|
msgid "plugin, require=True"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:421
|
|
msgid ""
|
|
"Returns the plugin specified by the arg or None. If require is True, an "
|
|
"error is raised if the plugin cannot be retrieved."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:424
|
|
msgid "boolean"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:426
|
|
msgid ""
|
|
"Converts the text string to a boolean value. Acceptable true values are: "
|
|
"\"1\", \"true\", \"on\", \"enable\", or \"enabled\" (case-insensitive). "
|
|
"Acceptable false values are: \"0\", false\", \"off\", \"disable\", or "
|
|
"\"disabled\" (case-insensitive)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:431
|
|
msgid "filename"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:433
|
|
msgid "Used to get a filename argument."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:435
|
|
msgid "commandName"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:437
|
|
msgid ""
|
|
"Returns the canonical command name version of the given string (ie, the "
|
|
"string is lowercased and dashes and underscores are removed)."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:440
|
|
msgid "text"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:442
|
|
msgid ""
|
|
"Takes the rest of the arguments as one big string. Note that this differs"
|
|
" from the \"anything\" context in that it clobbers the arg string when "
|
|
"it's done. Using any converters after this is most likely incorrect."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:447
|
|
msgid "Contexts List"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:448
|
|
msgid "What contexts are available for me to use?"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:450
|
|
msgid ""
|
|
"The list of available contexts is below. Unless specified otherwise, it "
|
|
"can be assumed that the type returned by the context itself matches the "
|
|
"type of the converter it is applied to."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:455
|
|
msgid "Options"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:460
|
|
msgid "optional"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:458
|
|
msgid ""
|
|
"Look for an argument that satisfies the supplied converter, but if it's "
|
|
"not the type I'm expecting or there are no arguments for us to check, "
|
|
"then use the default value. Will return the converted argument as is or "
|
|
"None."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:465
|
|
msgid "additional"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:463
|
|
msgid ""
|
|
"Look for an argument that satisfies the supplied converter, making sure "
|
|
"that it's the right type. If there aren't any arguments to check, then "
|
|
"use the default value. Will return the converted argument as is or None."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:469
|
|
msgid "first"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:468
|
|
msgid ""
|
|
"Tries each of the supplied converters in order and returns the result of "
|
|
"the first successfully applied converter."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:472
|
|
msgid "Multiplicity"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:476
|
|
msgid "any"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:475
|
|
msgid ""
|
|
"Looks for any number of arguments matching the supplied converter. Will "
|
|
"return a sequence of converted arguments or None."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:481
|
|
msgid "many"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:479
|
|
msgid ""
|
|
"Looks for multiple arguments matching the supplied converter. Expects at "
|
|
"least one to work, otherwise it will fail. Will return the sequence of "
|
|
"converted arguments."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:486
|
|
msgid "commalist"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:484
|
|
msgid ""
|
|
"Looks for a comma separated list of arguments that match the supplied "
|
|
"converter. Returns a list of the successfully converted arguments. If any"
|
|
" of the arguments fail, this whole context fails."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:493
|
|
msgid "rest"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:492
|
|
msgid ""
|
|
"Treat the rest of the arguments as one big string, and then convert. If "
|
|
"the conversion is unsuccessful, restores the arguments."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:500
|
|
msgid "getopts"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:496
|
|
msgid ""
|
|
"Handles --option style arguments. Each option should be a key in a "
|
|
"dictionary that maps to the name of the converter that is to be used on "
|
|
"that argument. To make the option take no argument, use \"\" as the "
|
|
"converter name in the dictionary. For no conversion, use None as the "
|
|
"converter name in the dictionary."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:505
|
|
msgid "reverse"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:503
|
|
msgid ""
|
|
"Reverse the argument list, apply the converters, and then reverse the "
|
|
"argument list back."
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:508
|
|
msgid "Final Word"
|
|
msgstr ""
|
|
|
|
#: ../../develop/using_wrap.rst:510
|
|
msgid ""
|
|
"Now that you know how to use wrap, and you have a list of converters and "
|
|
"contexts you can use, your task of writing clean, simple, and safe plugin"
|
|
" code should become much easier. Enjoy!"
|
|
msgstr ""
|
|
|