Merge branch 'testing'

This commit is contained in:
Valentin Lorentz 2011-03-08 15:21:31 +01:00
commit 872e92eaff
141 changed files with 7100 additions and 1471 deletions

226
plugins/Admin/locale/fi.po Normal file
View File

@ -0,0 +1,226 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: Finnish translation of Admin plugin in Supybot\n"
"POT-Creation-Date: 2010-10-16 10:43+CEST\n"
"PO-Revision-Date: 2011-02-28 14:51+0200\n"
"Last-Translator: Mika Suomalainen <mika.henrik.mainio@hotmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n"
"X-Poedit-Language: Finnish\n"
"X-Poedit-Country: FINLAND\n"
#: plugin.py:54
msgid "Nick/channel temporarily unavailable."
msgstr "Nimimerkki/kanava on väliaikaisesti saavutettavissa."
#: plugin.py:72
msgid "Cannot join %s, it's full."
msgstr "Ei voida liittyä %s, se on täynnä."
#: plugin.py:80
msgid "Cannot join %s, I was not invited."
msgstr "Ei voi liittyä %s, minua ei ole kutsuttu."
#: plugin.py:88
msgid "Cannot join %s, it's banned me."
msgstr "Ei voi liittyä %s, se on bannannut minut."
#: plugin.py:96
msgid "Cannot join %s, my keyword was wrong."
msgstr "Ei voi littyä %s, minun avainsanani oli väärä."
#: plugin.py:104
msgid "Cannot join %s, I'm not identified with the NickServ."
msgstr "Ei voi liittyä %s, minä en ole tunnistautunut NickServillä."
#: plugin.py:134
msgid ""
"<channel> [<key>]\n"
"\n"
" Tell the bot to join the given channel. If <key> is given, it is used\n"
" when attempting to join the channel.\n"
" "
msgstr ""
"<kanava> [<avain>]\n"
"\n"
"Käskee botin liittyä annetulle kanavalle. Jos <avain> on annettu, sitä käytetään\n"
"yrittäessä liittyä kanavalle.\n"
" "
#: plugin.py:147
msgid "I'm already too close to maximum number of channels for this network."
msgstr "Minä olen liian lähellä kanavien maksimi määrää tällä verkolla."
#: plugin.py:156
msgid ""
"takes no arguments\n"
"\n"
" Returns the channels the bot is on. Must be given in private, in order\n"
" to protect the secrecy of secret channels.\n"
" "
msgstr ""
"Ei ota parametrejä\n"
"\n"
"Palauttaa listan kanavista, joilla botti on. Täytyy antaa yksityisviestillä salaisten kanavien\n"
"salaisuuden suojelemiseksi.\n"
" "
#: plugin.py:166
msgid "I'm not currently in any channels."
msgstr "En juuri nyt ole millään kanavalla."
#: plugin.py:172
msgid "My connection is restricted, I can't change nicks."
msgstr "Minun yhteyteni on rajoitettu. En voi vaihtaa nimimerkkiä."
#: plugin.py:179
msgid "Someone else is already using that nick."
msgstr "Joku muu käyttää jo tuota nimimerkkiä."
#: plugin.py:186
msgid "That nick is currently banned."
msgstr "Se nimimerkki on juuri nyt bannattu."
#: plugin.py:193
msgid "I can't change nicks, the server said %q."
msgstr "Minä en voi vaihtaa nimimerkkiä, palvelin sanoi %q"
#: plugin.py:207
msgid ""
"[<nick>]\n"
"\n"
" Changes the bot's nick to <nick>. If no nick is given, returns the\n"
" bot's current nick.\n"
" "
msgstr ""
"[<nimimerkki>]\n"
"\n"
"Vaihtaa botin nimimerkin <nimimerkiksi>. Jos nimimerkkiä ei ole annettu, palauttaa\n"
"botin nykyisen nimimerkin.\n"
" "
#: plugin.py:222
msgid ""
"[<channel>] [<reason>]\n"
"\n"
" Tells the bot to part the list of channels you give it. <channel> is\n"
" only necessary if you want the bot to part a channel other than the\n"
" current channel. If <reason> is specified, use it as the part\n"
" message.\n"
" "
msgstr ""
"[<kanava>] [<syy>]\n"
"\n"
"Käskee botin poistua kanavilta, jotka annat sille. <kanava> on\n"
"vaadittu jost tahdot botin poistuvat muulta, kuin \n"
"nykyiseltä kanavalta. Jos <syy> on määritetty, sitä käytetään poistumis\n"
"viestissä.\n"
" "
#: plugin.py:240
msgid "I'm not in %s."
msgstr "Minä en ole %s:ssa."
#: plugin.py:252
msgid ""
"<name|hostmask> <capability>\n"
"\n"
" Gives the user specified by <name> (or the user to whom <hostmask>\n"
" currently maps) the specified capability <capability>\n"
" "
msgstr ""
"<nimi|hostmask> <valtuus>\n"
"\n"
"Antaa <nimen> määrittämälle käyttäjälle (tai käyttäjälle jonka <hostmask>\n"
"ilmoittaa) määritetyn valtuuden <valtuus>\n"
" "
#: plugin.py:272
msgid "The \"owner\" capability can't be added in the bot. Use the supybot-adduser program (or edit the users.conf file yourself) to add an owner capability."
msgstr "\"Owner\" valtuutta ei voida lisätä bottiin. Käytä supybot-adduser ohjelmaa (tai muokkaa users.conf tiedostoa itse) lisätäksesi owner valtuuden."
#: plugin.py:283
msgid "You can't add capabilities you don't have."
msgstr "Et voi lisätä valtuuksia, joita sinulla ei ole."
#: plugin.py:288
msgid ""
"<name|hostmask> <capability>\n"
"\n"
" Takes from the user specified by <name> (or the user to whom\n"
" <hostmask> currently maps) the specified capability <capability>\n"
" "
msgstr ""
"<nimi|hostmask> <valtuus>\n"
"\n"
"Ottaa <nimen> määrittämältä käyttäjältä (tai käyttäjältä jonka\n"
" <hostmask> sopii) määritetyn valtuuden <valtuus>\n"
" "
#: plugin.py:300
msgid "That user doesn't have that capability."
msgstr "Tuolla käyttäjällä ei ole sitä valtuutta."
#: plugin.py:302
msgid "You can't remove capabilities you don't have."
msgstr "Sinä et voi poistaa valtuuksia, joita sinulla ei ole."
#: plugin.py:310
msgid ""
"<hostmask|nick> [<expires>]\n"
"\n"
" This will set a persistent ignore on <hostmask> or the hostmask\n"
" currently associated with <nick>. <expires> is an optional argument\n"
" specifying when (in \"seconds from now\") the ignore will expire; if\n"
" it isn't given, the ignore will never automatically expire.\n"
" "
msgstr ""
"<hostmask|nimimerkki> [<vanhentuu>]\n"
"\n"
"Tämä asettaa pysyvän ignoren <hostmaskiin> tai hostmaskiin,\n"
" joka on tällä hetkellä yhdistetty <nimimerkkiin>. <vanhentuu> on vaihtoehtoinen paremetri,\n"
"joka määrittää (in \"sekuntit\") jolloin ignore vanhentuu; jos\n"
"sitä ei ole annettu, ignore ei vanhene automaattisesti ikinä.\n"
" "
#: plugin.py:323
msgid ""
"<hostmask|nick>\n"
"\n"
" This will remove the persistent ignore on <hostmask> or the\n"
" hostmask currently associated with <nick>.\n"
" "
msgstr ""
"<hostmask|nimimerkki>\n"
"\n"
"Tämä poistaa pysyvän ignoren <hostmaskista> tai\n"
"hostmaskista joka on tällä hetkellä yhdistetty <nimimerkkiin>.\n"
" "
#: plugin.py:332
msgid "%s wasn't in the ignores database."
msgstr "%s ei ollut ignore tietokannassa."
#: plugin.py:337
msgid ""
"takes no arguments\n"
"\n"
" Lists the hostmasks that the bot is ignoring.\n"
" "
msgstr ""
"Ei ota parametrejä\n"
"\n"
"Luetteloi hostmaskit jotka ovat botin ignoressa.\n"
" "
#: plugin.py:345
msgid "I'm not currently globally ignoring anyone."
msgstr "Kukaan ei juuri nyt ole globaalisti estolistalla."

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-16 10:43+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"

109
plugins/Alias/locale/fi.po Normal file
View File

@ -0,0 +1,109 @@
msgid ""
msgstr ""
"Project-Id-Version: Supybot Alias plugin\n"
"POT-Creation-Date: 2010-10-16 14:10+CEST\n"
"PO-Revision-Date: \n"
"Last-Translator: Mika Suomalainen <mika.henrik.mainio@hotmail.com>\n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Finnish\n"
"X-Poedit-Country: FINLAND\n"
#: plugin.py:45
msgid ""
"Returns the channel the msg came over or the channel given in args.\n"
"\n"
" If the channel was given in args, args is modified (the channel is\n"
" removed).\n"
" "
msgstr ""
"Palauttaa kanavan, jolta viesti tuli tai kanavan, joka on annettu parametreissä.\n"
"\n"
"Jos kanava annetaan parametreissä, parametriä muokataan (kanava\n"
"poistetaan).\n"
" "
#: plugin.py:164
msgid " at least"
msgstr "vähintään"
#: plugin.py:165
msgid ""
"<an alias,%s %n>\n"
"\n"
"Alias for %q."
msgstr ""
"<alias,%s %n>\n"
"\n"
"Alias %q:lle."
#: plugin.py:220
msgid ""
"<alias>\n"
"\n"
" Locks an alias so that no one else can change it.\n"
" "
msgstr ""
"<alias>\n"
"\n"
"Lukitsee aliaksen, niin ettei kukaan muu voi muuttaa sitä.\n"
" "
#: plugin.py:229
#: plugin.py:243
msgid "There is no such alias."
msgstr "Tuollaista aliasta ei ole."
#: plugin.py:234
msgid ""
"<alias>\n"
"\n"
" Unlocks an alias so that people can define new aliases over it.\n"
" "
msgstr ""
"<alias>\n"
"\n"
"Poistaa lukituksen aliaksesta, jotta ihmiset vouvat määrittää uusia aliaksia sen päälle.\n"
" "
#: plugin.py:254
msgid "That name isn't valid. Try %q instead."
msgstr "Nimi ei ole kelvollinen. Yritä sen sijaa %q:ta."
#: plugin.py:292
msgid ""
"<name> <alias>\n"
"\n"
" Defines an alias <name> that executes <alias>. The <alias>\n"
" should be in the standard \"command argument [nestedcommand argument]\"\n"
" arguments to the alias; they'll be filled with the first, second, etc.\n"
" arguments. $1, $2, etc. can be used for required arguments. @1, @2,\n"
" etc. can be used for optional arguments. $* simply means \"all\n"
" remaining arguments,\" and cannot be combined with optional arguments.\n"
" "
msgstr ""
"<nimi> <alias>\n"
"\n"
"Määrittää aliaksen <nimi>, joka suorittaa <aliaksen>. <Alias>\n"
"Aliaksen pitäisi olla tavallisia \"komento parametri [sisäkkäiset parametrit]\"\n"
"parametrejä aliakselle; ne täytetään ensinmäinen, toinen, jne.\n"
"Parametrit. $1, $2, jne. voidaan käyttää vaadittuina parametreinä. @1, @2,\n"
"jne. voidaan käyttää vaihtoehtoisina parametreinä. $* tarkoittaa yksinkertaisesti \"kaikki\n"
"jäljellä olevat parametrit,\" ja johon ei voida yhdistää vaihtoehtoisia parametrejä.\n"
" "
#: plugin.py:315
msgid ""
"<name>\n"
"\n"
" Removes the given alias, if unlocked.\n"
" "
msgstr ""
"<nimi>\n"
"\n"
"Poistaa annetun aliaksen jos se ei ole lukittu.\n"
" "

View File

@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: Supybot-fr\n"
"POT-Creation-Date: 2010-10-16 14:10+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\n"
"PO-Revision-Date: \n"
"Last-Translator: Valentin Lorentz <progval@gmail.com>\n"
"Language-Team: Supybot-fr <progval@gmail.com>\n"
@ -39,6 +39,10 @@ msgstr ""
"\n"
"Alias pour %q."
#: plugin.py:166
msgid "argument"
msgstr "argument"
#: plugin.py:220
msgid ""
"<alias>\n"

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-16 14:10+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -36,6 +36,10 @@ msgid ""
"Alias for %q."
msgstr ""
#: plugin.py:166
msgid "argument"
msgstr ""
#: plugin.py:220
#, docstring
msgid ""

View File

@ -0,0 +1,101 @@
msgid ""
msgstr ""
"Project-Id-Version: Supybot Anonymous\n"
"POT-Creation-Date: 2010-10-16 15:14+CEST\n"
"PO-Revision-Date: \n"
"Last-Translator: Mika Suomalainen <mika.henrik.mainio@hotmail.com>\n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Finnish\n"
"X-Poedit-Country: FINLAND\n"
#: config.py:49
msgid ""
"Determines whether\n"
" the bot should require people trying to use this plugin to be in the\n"
" channel they wish to anonymously send to."
msgstr ""
"Määrittelee täytyisikö \n"
"botin vaatia ihmisiä, jotka yrittävät käyttää tätä lisäosaa olla\n"
"kanavalla, jonne he tahtovat lähettää viestin tuntemattomasti."
#: config.py:53
msgid ""
"Determines whether the bot should require\n"
" people trying to use this plugin to be registered."
msgstr ""
"Määrittelee täytyisikö botin vaatia\n"
"ihmisiä, jotka yrittävät käyttää tätä lisäosaa olla rekisteröityneitä."
#: config.py:56
msgid ""
"Determines what capability (if any) the bot should\n"
" require people trying to use this plugin to have."
msgstr ""
"Määrittää mitä valtuutta (jos mitään) botin täytyisi\n"
"vaatia ihmisiltä, jotka yrittävät käyttää tätä lisäosaa."
#: config.py:59
msgid ""
"Determines whether the bot will require \n"
" targets of the \"say\" command to be public (i.e., channels). If this is\n"
" True, the bot will allow people to use the \"say\" command to send private\n"
" messages to other users."
msgstr ""
"Määrittelee täytyykö botin vaatia \n"
"\"say\" komennon olevan julkisia (esim., kanavia). Jos tämä on\n"
" True, botti sallii ihmisten käyttää \"say\" komentoa lähettääkseen yksityisviestejä \n"
" toisille käyttäjille."
#: plugin.py:41
msgid ""
"This plugin allows users to act through the bot anonymously. The 'do'\n"
" command has the bot perform an anonymous action in a given channel, and\n"
" the 'say' command allows other people to speak through the bot. Since\n"
" this can be fairly well abused, you might want to set\n"
" supybot.plugins.Anonymous.requireCapability so only users with that\n"
" capability can use this plugin. For extra security, you can require that\n"
" the user be *in* the channel they are trying to address anonymously with\n"
" supybot.plugins.Anonymous.requirePresenceInChannel, or you can require\n"
" that the user be registered by setting\n"
" supybot.plugins.Anonymous.requireRegistration.\n"
" "
msgstr ""
"Tämä lisäosa sallii käyttäjien toimia botin kautta tuntemattomasti.\n"
"Komento 'do' sallii botin tehdä Anonymous toiminnon annetulla kanavalla ja\n"
"'say' komento sallii toisten ihmisten puhua botin läpi. Koska\n"
"tätä voidaan väärinkäyttää helposti voit tahtoa asettaa \n"
"supybot.plugins.Anonymous.requireCapability niin, että vain käyttäjät tuolla\n"
"valtuudella. Lisäturvallisuuden vuoksi voit vaatia, että käyttäjän täytyy *olla* kanavalla joita he yrittävät puhutella tuntemattomasti asetuksella supybot.plugins.Anonymous.requirePresenceInChannel, tai sinä voit vaatia,\n"
"että tuo käyttäjä on rekisteröitynyt asetuksella\n"
"supybot.plugins.Anonymous.requireRegistration"
#: plugin.py:81
msgid ""
"<channel|nick> <text>\n"
"\n"
" Sends <text> to <channel|nick>. Can only send to <nick> if\n"
" supybot.plugins.Anonymous.allowPrivateTarget is True.\n"
" "
msgstr ""
"<kanava|nimimerkki> <teksti>\n"
"\n"
"Lähettää <tekstin> <kanavalle|nimimerkille>. <Nimimerkille> voi lähettää vain jos\n"
"supybot.plugins.Anonymous.allowPrivateTarget on True.\n"
" "
#: plugin.py:95
msgid ""
"<channel> <action>\n"
"\n"
" Performs <action> in <channel>.\n"
" "
msgstr ""
"<kanava> <toiminto>\n"
"\n"
"Suorittaa <toiminnon> <kanavalla>.\n"
" "

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-16 15:14+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -42,7 +42,7 @@ msgid ""
" messages to other users."
msgstr ""
#: plugin.py:41
#: plugin.py:40
#, docstring
msgid ""
"This plugin allows users to act through the bot anonymously. The 'do'\n"
@ -58,7 +58,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:81
#: plugin.py:80
#, docstring
msgid ""
"<channel|nick> <text>\n"
@ -68,7 +68,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:95
#: plugin.py:94
#, docstring
msgid ""
"<channel> <action>\n"

View File

@ -0,0 +1,86 @@
msgid ""
msgstr ""
"Project-Id-Version: Supybot AutoMode\n"
"POT-Creation-Date: 2010-10-16 18:48+CEST\n"
"PO-Revision-Date: \n"
"Last-Translator: Mika Suomalainen <mika.henrik.mainio@hotmail.com>\n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Finnish\n"
"X-Poedit-Country: FINLAND\n"
#: config.py:46
msgid ""
"Determines whether this plugin is enabled.\n"
" "
msgstr ""
"Määrittää onko tämä lisäosa käytössä.\n"
" "
#: config.py:49
msgid ""
"Determines whether this plugin will automode\n"
" owners."
msgstr ""
"Määrittää käytetäänkö tätä lisäosaa \n"
"omistajiin."
#: config.py:52
msgid ""
"Determines whether the bot will \"fall through\n"
" to halfop/voicing when auto-opping is turned off but\n"
" auto-halfopping/voicing are turned on."
msgstr ""
"Määrittää \"siirtyykö botti\n"
"halfoppaamiseen/voicen antamiseen kun automaatti-oppaaminen on on/off asennossa mutta\n"
"automaattinen-halfoppaaminen/voicen antaminen ovat käytössä."
#: config.py:56
msgid ""
"Determines whether the bot will automatically\n"
" op people with the <channel>,op capability when they join the channel.\n"
" "
msgstr ""
"Määrittää oppaako botti\n"
"ihmiset <kanava>,op valtuudella automaattisesti, kun he liittyvät kanavalle.\n"
" "
#: config.py:60
msgid ""
"Determines whether the bot will automatically\n"
" halfop people with the <channel>,halfop capability when they join the\n"
" channel."
msgstr ""
"Miirittää halfoppaako botti automaattisesti, kun\n"
"ihmiset <kanava>,halfop valtuudella, kun he liittyvät \n"
"kanavalle."
#: config.py:64
msgid ""
"Determines whether the bot will automatically\n"
" voice people with the <channel>,voice capability when they join the\n"
" channel."
msgstr ""
"Määrittää antaako botti automaattisesti voicen\n"
" ihmisille, joilla on <kanava>,voice valtuus kun he liittyvät\n"
"kanavalle."
#: config.py:68
msgid ""
"Determines whether the bot will automatically\n"
" ban people who join the channel and are on the banlist."
msgstr ""
"Määrittää bannaako botti ihmiset,\n"
"jotka liittyvät kanavalle ja ovat banni listalla."
#: config.py:71
msgid ""
"Determines how many seconds the bot\n"
" will automatically ban a person when banning."
msgstr ""
"Määrittää kuinka moneksi sekuntiksi botti\n"
"bannaa henkilön, kun ollaan bannaamassa."

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-16 18:48+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"

View File

@ -0,0 +1,184 @@
msgid ""
msgstr ""
"Project-Id-Version: Supybot BadWords\n"
"POT-Creation-Date: 2011-01-29 11:48+CET\n"
"PO-Revision-Date: \n"
"Last-Translator: Mika Suomalainen <mika.henrik.mainio@hotmail.com>\n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Finnish\n"
"X-Poedit-Country: FINLAND\n"
#: __init__.py:30
msgid ""
"\n"
"Filters bad words on outgoing messages from the bot, so the bot can't be made\n"
"to say bad words.\n"
msgstr ""
"\n"
"Suodattaa botin ulostulevista viesteistä pahat sanat, jotta bottia ei saada\n"
"sanomaan pahoja sanoja.\n"
#: config.py:40
msgid "Would you like to add some bad words?"
msgstr "Haluaisitko lisätä joitakin pahoja sanoja?"
#: config.py:41
msgid "What words? (separate individual words by spaces)"
msgstr "Mitkä sanoja? (Eristä erilliset sanat käyttämällä välilyöntiä."
#: config.py:53
msgid ""
"Determines what words are\n"
" considered to be 'bad' so the bot won't say them."
msgstr ""
"Määrittää mitkä sanat ovat\n"
"'pahoja', jottei botti sano niitä."
#: config.py:56
msgid ""
"Determines whether the bot will require bad\n"
" words to be independent words, or whether it will censor them within other\n"
" words. For instance, if 'darn' is a bad word, then if this is true, 'darn'\n"
" will be censored, but 'darnit' will not. You probably want this to be\n"
" false. After changing this setting, the BadWords regexp needs to be\n"
" regenerated by adding/removing a word to the list, or reloading the\n"
" plugin."
msgstr ""
"Määrittää vaatiiko botti pahojen sanojen\n"
"olevan toisistaan riippumattomia sanoja, vai sensuroiko se ne toisten sanojen\n"
"sisältä. Esimerkiksi,jos 'pah' on paha sana, ja jos tämä on asetettu true asentoon, 'pah'\n"
"sensuroidaan, mutta 'pahus' ei sensuroida. Sinä luultavasti tahdot pitää tämän\n"
"false asennossa. Tämän asetuksen muuttamisen jälkeen, BadWords regexp täytyy\n"
"luoda uudelleen lisäämällä/poistamalla sana listalta, tai uudelleenlataamalla \n"
"lisäosa."
#: config.py:73
msgid ""
"Determines what characters will replace bad words; a\n"
" chunk of these characters matching the size of the replaced bad word will\n"
" be used to replace the bad words you've configured."
msgstr ""
"Määrittä mitkä merkit korvaavat pahat sanat; \n"
"osa näistä merkeistä, jotka sopivat pahasanan kokoon\n"
"käytetään määrittämiesi pahojen sanojen korvaamisessa."
#: config.py:81
msgid ""
"Determines the manner in which\n"
" bad words will be replaced. 'nastyCharacters' (the default) will replace a\n"
" bad word with the same number of 'nasty characters' (like those used in\n"
" comic books; configurable by supybot.plugins.BadWords.nastyChars).\n"
" 'simple' will replace a bad word with a simple strings (regardless of the\n"
" length of the bad word); this string is configurable via\n"
" supybot.plugins.BadWords.simpleReplacement."
msgstr ""
"Määrittää millä tavalla\n"
"pahat sanat korvataan. 'nastyCharacters' (oletus) korvaa\n"
"pahan sanan samalla määrällä 'häijyjä merkkejä' (kuten ne jotka ovat\n"
"sarjakuvissa; muokattavissa supybot.plugins.BadWords.nastyChars asetuksella).\n"
"'simple' korvaa pahan sanan yksinkertaisella merkkijonolla (riippumatta\n"
"pahan sanan koosta); tämä merkkijono on muokattavissa\n"
"asetuksella supybot.plugins.BadWords.simpleReplacement."
#: config.py:89
msgid ""
"Determines what word will replace bad\n"
" words if the replacement method is 'simple'."
msgstr ""
"Määrittää mikä sana korvaa pahat\n"
"sanat jos korvausmenetelmä on 'simple'."
#: config.py:92
msgid ""
"Determines whether the bot will strip\n"
" formatting characters from messages before it checks them for bad words.\n"
" If this is False, it will be relatively trivial to circumvent this plugin's\n"
" filtering. If it's True, however, it will interact poorly with other\n"
" plugins that do coloring or bolding of text."
msgstr ""
"Määrittää riisuuko botti\n"
" muotoilun merkeistä ennen kuin se tarkistaa ne pahojen sanojen varalta.\n"
" Jos tämä on 'False', on hyvin pinnallista kiertää tämän lisäosan\n"
" suodatusta. Jos se kuitenkin on 'True', se on huonosti vuorovaikutuksessa\n"
"tekstin värittämistä tai korostamista tekevien lisäosien kanssa."
#: config.py:99
msgid ""
"Determines whether the bot will kick people with\n"
" a warning when they use bad words."
msgstr ""
"Määrittää potkiiko botti ihmiset\n"
"varoituksella jos he käyttävät pahoja sanoja."
#: config.py:102
msgid ""
"You have been kicked for using a word\n"
" prohibited in the presence of this bot. Please use more appropriate\n"
" language in the future."
msgstr ""
"Sinut on potkittu kielletyn sanan\n"
"käytöstä tämän botin läsnäollessa. Ole hyvä ja käytä asianmukaisempaa\n"
" kieltä tulevaisuudessa."
#: config.py:104
msgid ""
"Determines the kick message used by the\n"
" bot when kicking users for saying bad words."
msgstr ""
"Määrittää potkimisviestin, jota\n"
"botti käyttää, kun potkii käyttäjiä pahojen sanojen käyttämistä."
#: plugin.py:46
msgid ""
"Maintains a list of words that the bot is not allowed to say.\n"
" Can also be used to kick people that say these words, if the bot\n"
" has op."
msgstr ""
"Säilyttää listaa sanoista, joita botin ei ole sallittua sanoa.\n"
"Voidaan myös käyttää potkimaan ihmisiä, jotka sanovat näitä sanoja, jos botilla\n"
"on kanavaoperaattori."
#: plugin.py:113
msgid ""
"takes no arguments\n"
"\n"
" Returns the list of words being censored.\n"
" "
msgstr ""
"ei ota parametrejä\n"
"\n"
"Palauttaa listan sanoista, joita sensuroidaan.\n"
" "
#: plugin.py:123
msgid "I'm not currently censoring any bad words."
msgstr "Tällä hetkellä ei sensuroida yhtään pahoja sanoja."
#: plugin.py:128
msgid ""
"<word> [<word> ...]\n"
"\n"
" Adds all <word>s to the list of words being censored.\n"
" "
msgstr ""
"<sana> [<sana> ...]\n"
"\n"
"Lisää kaikki <sana>(t) sensuroidaan.\n"
" "
#: plugin.py:140
msgid ""
"<word> [<word> ...]\n"
"\n"
" Removes <word>s from the list of words being censored.\n"
" "
msgstr ""
"<sana> [<sana> ...]\n"
"\n"
"Poistaa <sanat>(t) sensuroitujen sanojen listalta.\n"
" "

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2011-01-29 11:48+CET\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -15,14 +15,6 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n"
#: __init__.py:30
#, docstring
msgid ""
"\n"
"Filters bad words on outgoing messages from the bot, so the bot can't be made\n"
"to say bad words.\n"
msgstr ""
#: config.py:40
msgid "Would you like to add some bad words?"
msgstr ""

View File

@ -0,0 +1,833 @@
msgid ""
msgstr ""
"Project-Id-Version: Supybot Channel\n"
"POT-Creation-Date: 2010-10-25 13:10+CEST\n"
"PO-Revision-Date: \n"
"Last-Translator: Mika Suomalainen <mika.henrik.mainio@hotmail.com>\n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Finnish\n"
"X-Poedit-Country: FINLAND\n"
#: config.py:48
msgid ""
"Determines whether the bot will always try to\n"
" rejoin a channel whenever it's kicked from the channel."
msgstr ""
"Määrittää yrittääkö botti aina\n"
"liittyä kanavalle uudelleen, kun se on potkittu kanavalta."
#: plugin.py:69
msgid ""
"[<channel>] <mode> [<arg> ...]\n"
"\n"
" Sets the mode in <channel> to <mode>, sending the arguments given.\n"
" <channel> is only necessary if the message isn't sent in the channel\n"
" itself.\n"
" "
msgstr ""
"[<kanava>] <tila> [<parametri> ...]\n"
"\n"
"Asettaa <kanavan> <tilan>, tilaksi lähettäen annetut parametrin.\n"
"<kanava> on vaadittu vain, jos viestiä ei lähetetä kanavalta\n"
"jonka tilaa vaihdetaan.\n"
" "
#: plugin.py:76
msgid "change the mode"
msgstr "vaihda tila"
#: plugin.py:80
msgid ""
"[<channel>] [<limit>]\n"
"\n"
" Sets the channel limit to <limit>. If <limit> is 0, or isn't given,\n"
" removes the channel limit. <channel> is only necessary if the message\n"
" isn't sent in the channel itself.\n"
" "
msgstr ""
"[<kanava>] [<rajoitus>]\n"
"\n"
"Asettaa kanavan <rajoituksen>. Jos <rajoitus> on 0, tai ei annettu,\n"
"kanava rajoitus poistetaan. <Kanava>on vaadittu cain jos\n"
"viestiä ei lähetetä kanavalta itseltään.\n"
" "
#: plugin.py:90
msgid "change the limit"
msgstr "Vaihda rajoitusta"
#: plugin.py:95
msgid ""
"[<channel>]\n"
"\n"
" Sets +m on <channel>, making it so only ops and voiced users can\n"
" send messages to the channel. <channel> is only necessary if the\n"
" message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<kanava>]\n"
"\n"
"Asettaa tilan +m <kanavalla>, sallien vain operaattoireiden ja käyttäjien jolla on voice\n"
"lähettää viestejä kanavalle. <Kanava> on vaadittu vain jos\n"
"viestiä ei lähetetä itse kanavalta.\n"
" "
#: plugin.py:102
msgid "moderate the channel"
msgstr "valvo kanavaa"
#: plugin.py:106
msgid ""
"[<channel>]\n"
"\n"
" Sets -m on <channel>, making it so everyone can\n"
" send messages to the channel. <channel> is only necessary if the\n"
" message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<kanava>]\n"
"\n"
"Asettaa -m tilan <kanavalla>, sallien jokaisen\n"
"lähettää viestejä kanavalla. <Kanava> on vaadittu vain jos\n"
" viestiä ei lähetetä kanavalta itseltään.\n"
" "
#: plugin.py:114
msgid "unmoderate the channel"
msgstr "lopeta kanavan valvominen"
#: plugin.py:118
msgid ""
"[<channel>] [<key>]\n"
"\n"
" Sets the keyword in <channel> to <key>. If <key> is not given, removes\n"
" the keyword requirement to join <channel>. <channel> is only necessary\n"
" if the message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<kanava>] [<avain>]\n"
"\n"
"Asettaa <kanavan>avainsanan <avaimeksi>. Jos <avainta> ei ole annettu, poistaa\n"
"poistaa avainsana vaatimuksen <kanavalle> liittymisestä. <Kanava> on vaadittu vain\n"
"jos viestiä ei lähetetä kanavalta itsestään.\n"
" "
#: plugin.py:130
msgid "change the keyword"
msgstr "vaihtaa avainsanan"
#: plugin.py:135
msgid ""
"[<channel>] [<nick> ...]\n"
"\n"
" If you have the #channel,op capability, this will give all the <nick>s\n"
" you provide ops. If you don't provide any <nick>s, this will op you.\n"
" <channel> is only necessary if the message isn't sent in the channel\n"
" itself.\n"
" "
msgstr ""
"[<kanava>] [<nimimerkki> ...]\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä antaa kaikille <nimimerkeille>\n"
"sinun tarjoamat opit. Jos et anna yhtään <nimimerkkiä>, tämä oppaa sinut.\n"
"<Kanava> on vaadittu vain jos viestiä ei lähetetä kanavalta\n"
"itsestään.\n"
" "
#: plugin.py:147
msgid "op someone"
msgstr "oppaa joku"
#: plugin.py:151
msgid ""
"[<channel>] [<nick> ...]\n"
"\n"
" If you have the #channel,halfop capability, this will give all the\n"
" <nick>s you provide halfops. If you don't provide any <nick>s, this\n"
" will give you halfops. <channel> is only necessary if the message isn't\n"
" sent in the channel itself.\n"
" "
msgstr ""
"[<Kanava>] [<nimimerkki> ...]\n"
"\n"
"Jos sinulla on #kanava,halfop valtuus, tämä antaa kaikille\n"
"<nimimerkeille> halfopit. Jos et anna yhtään <nimimerkkiä>, tämä\n"
"antaa sinulle halfopit. <Kanava> on vaadittu vain jos viestiä ei\n"
"lähetetä kanavalta itseltään.\n"
" "
#: plugin.py:163
msgid "halfop someone"
msgstr "halfoppaa joku"
#: plugin.py:168
msgid ""
"[<channel>] [<nick> ...]\n"
"\n"
" If you have the #channel,voice capability, this will voice all the\n"
" <nick>s you provide. If you don't provide any <nick>s, this will\n"
" voice you. <channel> is only necessary if the message isn't sent in the\n"
" channel itself.\n"
" "
msgstr ""
"[<Kanava>] [<nimimerkki> ...]\n"
"\n"
"Jos sinulla on #kanava,halfop valtuus, tämä antaa kaikille\n"
"<nimimerkeille> halfopit. Jos et anna yhtään <nimimerkkiä>, tämä\n"
"antaa sinulle halfopit. <Kanava> on vaadittu vain jos viestiä ei\n"
"lähetetä kanavalta itseltään.\n"
" "
#: plugin.py:190
msgid "voice someone"
msgstr "anna jollekin voice"
#: plugin.py:195
msgid ""
"[<channel>] [<nick> ...]\n"
"\n"
" If you have the #channel,op capability, this will remove operator\n"
" privileges from all the nicks given. If no nicks are given, removes\n"
" operator privileges from the person sending the message.\n"
" "
msgstr ""
"[<kanava>] [<nimimerkki> ...]\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä poistaa operaattori\n"
"kaikilta annetuilta nimimerkeiltä. Jos nimimerkkejä ei ole annettu poistaa\n"
"poistaa operaattorioikeudet henkilöltä, joka lähettää viestin.\n"
" "
#: plugin.py:202
msgid "I cowardly refuse to deop myself. If you really want me deopped, tell me to op you and then deop me yourself."
msgstr "Minä pelkurimaisesti kieltäydyn deoppaamasta itseäni. Jos todella tahdot minut deopatuksi, käske minun opata sinut ja sitten deoppaa minut itse."
#: plugin.py:210
msgid "deop someone"
msgstr "deoppaa joku"
#: plugin.py:215
msgid ""
"[<channel>] [<nick> ...]\n"
"\n"
" If you have the #channel,op capability, this will remove half-operator\n"
" privileges from all the nicks given. If no nicks are given, removes\n"
" half-operator privileges from the person sending the message.\n"
" "
msgstr ""
"[<kanava>] [<nimimerkki> ...]\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä poistaa puoli-kanavaoperaattorin\n"
"kaikilta annetuilta nimimerkeiltä. Jos nimimerkkejä ei anneta\n"
"viestin lähettäneeltä henkilöltä poistetaan puoli-kanavaoperaattorin oikeudet.\n"
" "
#: plugin.py:222
msgid "I cowardly refuse to dehalfop myself. If you really want me dehalfopped, tell me to op you and then dehalfop me yourself."
msgstr "Minä pelkurimaisesti kieltäydyn dehalfoppaamasta itseäni. Jos haluat minut depuoliopatuksi, käske minun opata sinut ja sitten dehalfoppaa minut itse."
#: plugin.py:230
msgid "dehalfop someone"
msgstr "dehalfoppaa joku"
#: plugin.py:235
msgid ""
"[<channel>] [<nick> ...]\n"
"\n"
" If you have the #channel,op capability, this will remove voice from all\n"
" the nicks given. If no nicks are given, removes voice from the person\n"
" sending the message.\n"
" "
msgstr ""
"[<kanava>] [<nimimerkki> ...]\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä poistaa voicen kaikilta\n"
"annetuilta nimimerkeiltä. Jos nimimerkkejä ei ole annettu, poistaa voicen henkilöltä\n"
"joka lähettää viestin.\n"
" "
#: plugin.py:242
msgid "I cowardly refuse to devoice myself. If you really want me devoiced, tell me to op you and then devoice me yourself."
msgstr "Minä pelkurimaisesti kieltäydyn poistamasta voicea itseltäni. Jos todella tahdot poistaa minulta voicen, käske minut oppaamaan sinut ja sitten poista ääni minulta itse."
#: plugin.py:255
msgid ""
"[<channel>]\n"
"\n"
" If you have the #channel,op capability, this will cause the bot to\n"
" \"cycle\", or PART and then JOIN the channel. <channel> is only necessary\n"
" if the message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<kanava>]\n"
"\n"
" Jos sinulla on #kanava,op valtuus, tämä aiheuttaa botin tekemään\n"
"\"cyclen\", tai POISTUMAAN ja LIITTYMÄÄN kanavalle. <Kanava> on vaadittu vain\n"
"jos viestiä ei lähetetä kanavalta itsestään.\n"
" "
#: plugin.py:268
msgid ""
"[<channel>] <nick>[, <nick>, ...] [<reason>]\n"
"\n"
" Kicks <nick>(s) from <channel> for <reason>. If <reason> isn't given,\n"
" uses the nick of the person making the command as the reason.\n"
" <channel> is only necessary if the message isn't sent in the channel\n"
" itself.\n"
" "
msgstr ""
"[<kanava>] <nimimerkki>[, <nimimerkki>, ...] [<syy>]\n"
"\n"
"Potkii <nimimerkit> <kanavalta> <syystä>. Jos <syytä> ei ole annettu,\n"
"käyttää komennon antajan nimimerkkiä syynä.\n"
"<Kanava> on vaadittu vain jos viestiä ei lähetetä kanavalta\n"
"itsestään.\n"
" "
#: plugin.py:276
msgid "I cowardly refuse to kick myself."
msgstr "Minä pelkurimaisesti kieltäydyn potkimasta itseäni."
#: plugin.py:281
msgid "The reason you gave is longer than the allowed length for a KICK reason on this server."
msgstr "Syy, jonka annoit on pidempi kuin sallittu pituus POTKIMIS syyksi tällä palvelimella."
#: plugin.py:286
msgid "kick someone"
msgstr "potki joku"
#: plugin.py:292
msgid ""
"[<channel>] [--{exact,nick,user,host}] <nick> [<seconds>] [<reason>]\n"
"\n"
" If you have the #channel,op capability, this will kickban <nick> for\n"
" as many seconds as you specify, or else (if you specify 0 seconds or\n"
" don't specify a number of seconds) it will ban the person indefinitely.\n"
" --exact bans only the exact hostmask; --nick bans just the nick;\n"
" --user bans just the user, and --host bans just the host. You can\n"
" combine these options as you choose. <reason> is a reason to give for\n"
" the kick.\n"
" <channel> is only necessary if the message isn't sent in the channel\n"
" itself.\n"
" "
msgstr ""
"[<kanava>] [--{exact,nick,user,host}] <nimimerkki> [<sekuntit>] [<syy>]\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä kickbannaa <nimimerkin> \n"
"niin moneksi sekuntiksi kuin määrität, tai muuten (jos määrität 0 sekuntia tai\n"
"tai et määritä sekuntien lukumäärää) se bannaa henkilön ikuisesti.\n"
"--exact bannaa vain hostmaskin; --nick bannaa vain nimimerkin;\n"
"--user bannaa vain käyttäjän, ja --host bannaa vain isännän. Voit\n"
"yhdistää näitä vaihtoehtoja mielesi mukaan. <Syy> on syy, jonka vuoksi annat\n"
"potkun.\n"
"<Kanava> on vaadittu vain jos viestiä ei lähetetä kanavassa\n"
"itsestään.\n"
" "
#: plugin.py:311
msgid "I cowardly refuse to kickban myself."
msgstr "Minä pelkurimaisesti kieltäydyn kickbannaamasta itseäni."
#: plugin.py:318
msgid "I haven't seen %s."
msgstr "Minä en ole nähnyt %s:ää."
#: plugin.py:326
msgid "I cowardly refuse to ban myself."
msgstr "Minä pelkurimaisesti kieltäydyn bannaamasta itseäni."
#: plugin.py:352
msgid "%s has %s too, you can't ban him/her/it."
msgstr "%s:llä on %s myös, et voi bannata häntä/sitä."
#: plugin.py:364
msgid "kick or ban someone"
msgstr "potki tai bannaa joku"
#: plugin.py:371
msgid ""
"[<channel>] [<hostmask>]\n"
"\n"
" Unbans <hostmask> on <channel>. If <hostmask> is not given, unbans\n"
" any hostmask currently banned on <channel> that matches your current\n"
" hostmask. Especially useful for unbanning yourself when you get\n"
" unexpectedly (or accidentally) banned from the channel. <channel> is\n"
" only necessary if the message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<kanava>] [<hostmask>]\n"
"\n"
"Poistaa bannin <hostmaskilta> <kanavalla>. Jos <hostmaskia> ei ole annettu, poistaa bannin\n"
"kaikilta hostmaskeilta, jotka ovat bannattuja <kanavalla> ja täsmäävät sinun\n"
"hostmaskiisi. Etenkin hyödyllinen jos joudut \n"
"odottomattomasti (tai vahingossa) bannatuksi kanavalta. <Kanava> on\n"
"vaadittu vain jos viestiä ei lähetetä kanavalta itseltään.\n"
" "
#: plugin.py:388
msgid "All bans on %s matching %s have been removed."
msgstr "Kaikki bannit, jotka sopivat %s :ään %s:tä on poistettu."
#: plugin.py:392
msgid "No bans matching %s were found on %s."
msgstr "Banneja, jotka täsmäävät %s:ään ei löydetty %s:tä."
#: plugin.py:395
msgid "unban someone"
msgstr "poista jonkun banni"
#: plugin.py:400
msgid ""
"[<channel>] <nick>\n"
"\n"
" If you have the #channel,op capability, this will invite <nick>\n"
" to join <channel>. <channel> is only necessary if the message isn't\n"
" sent in the channel itself.\n"
" "
msgstr ""
"[<kanava>] <nimimerkki>\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä kutsuu <nimimerkin>\n"
"liittymään <kanavalle>. <Kanava> on vaadittu vain jos viestiö\n"
"ei lähetetä kanavalla itsessään.\n"
" "
#: plugin.py:409
msgid "invite someone"
msgstr "kutsu joku"
#: plugin.py:428
msgid "%s is already in %s."
msgstr "%s on jo %s:ssä."
#: plugin.py:435
msgid "There is no %s on this network."
msgstr "%s ei ole tässä verkossa."
#: plugin.py:447
msgid ""
"[<channel>]\n"
"\n"
" If you have the #channel,op capability, this will \"lobotomize\" the\n"
" bot, making it silent and unanswering to all requests made in the\n"
" channel. <channel> is only necessary if the message isn't sent in\n"
" the channel itself.\n"
" "
msgstr ""
"[<kanava>]\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä \"lobotomoi\" \n"
"botin tehden sen hiljaisesksi ja vastaamattomaksi kaikkiin pyyntöihin,\n"
" jotka on tehty kanavalla. <Kanava> on vaadittu vain jos viestiä ei lähetetä\n"
" kanavalla itsessään.\n"
" "
#: plugin.py:462
msgid ""
"[<channel>]\n"
"\n"
" If you have the #channel,op capability, this will unlobotomize the\n"
" bot, making it respond to requests made in the channel again.\n"
" <channel> is only necessary if the message isn't sent in the channel\n"
" itself.\n"
" "
msgstr ""
"[<channel>]\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä poistaa lobotimian\n"
"botista, saaden sen jälleen vastaamaan kaikkiin pyyntöihin kanavalla.\n"
"<Kanava> on vaadittu vain jos viestiä ei lähetetä kanavalla itsessään.\n"
" "
#: plugin.py:477
msgid ""
"takes no arguments\n"
"\n"
" Returns the channels in which this bot is lobotomized.\n"
" "
msgstr ""
"ei ota parametrejä\n"
"\n"
"Palauttaa kanavat, joilla botti on lobotomoitu.\n"
" "
#: plugin.py:492
msgid "I'm currently lobotomized in %L."
msgstr "Minut on tällähetkellä lobotomoity %L:ssa."
#: plugin.py:495
msgid "I'm not currently lobotomized in any channels that you're in."
msgstr "En tällä hetkellä ole lobotomoitu millään kanavalla, jolla sinä olet."
#: plugin.py:502
msgid ""
"[<channel>] <nick|hostmask> [<expires>]\n"
"\n"
" If you have the #channel,op capability, this will effect a\n"
" persistent ban from interacting with the bot on the given\n"
" <hostmask> (or the current hostmask associated with <nick>. Other\n"
" plugins may enforce this ban by actually banning users with\n"
" matching hostmasks when they join. <expires> is an optional\n"
" argument specifying when (in \"seconds from now\") the ban should\n"
" expire; if none is given, the ban will never automatically expire.\n"
" <channel> is only necessary if the message isn't sent in the\n"
" channel itself.\n"
" "
msgstr ""
"[<kanava>] <nimimerkki|hostmask> [<expires>]\n"
"\n"
" Jos sinulla on #kanava,op valtuus, tämä aiheuttaa\n"
"pysyvän bannin estääkseen bottia olemasta vuorovaikutuksessa annetun\n"
"<hostmaskin> kanssa (tai <nimimerkin> nykyisen hostmaskin. Toiset\n"
"toiset lisäosat saattavat pakottaa tämän bannin bannaamalla käyttäjät\n"
" täsmäävällä hostmaskilla kun he liittyvät kanavalle. <Vanhentuu> on vaihtoehtoinen\n"
"parametri , kun määritetään ( \"sekunteja alkaen nyt\") milloin banni\n"
" vanhenee; jos mitään ei ole annettu, banni ei ikinä vanhene automaattisesti.\n"
"<Kanava> on vaadittu vain jos viestiä ei lähetetä\n"
"kanavalla itsessään.\n"
" "
#: plugin.py:522
msgid ""
"[<channel>] <hostmask>\n"
"\n"
" If you have the #channel,op capability, this will remove the\n"
" persistent ban on <hostmask>. <channel> is only necessary if the\n"
" message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<Kanava>] <hostmask>\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä poistaa\n"
"pysyvän bannin <hostmaskilta>. <Kanava> on vaadittu vain jos\n"
"viestiä ei lähetetä kanavalla itsellään.\n"
" "
#: plugin.py:534
msgid "There are no persistent bans for that hostmask."
msgstr ""
"Tuolla hostmaskilla ei ole pysyviä banneja.[<kanava>]\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä näyttää\n"
"pysyvät bannit #kanavalla.\n"
" "
#: plugin.py:539
msgid ""
"[<channel>]\n"
"\n"
" If you have the #channel,op capability, this will show you the\n"
" current persistent bans on #channel.\n"
" "
msgstr ""
"[<kanava>]\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä näyttää\n"
"pysyvät bannit #kanavalla.\n"
" "
#: plugin.py:549
msgid "%q (expires %t)"
msgstr "%q (venhentuu %t)"
#: plugin.py:552
msgid "%q (never expires)"
msgstr "%q (ei ikinä vanhennu)"
#: plugin.py:556
msgid "There are no persistent bans on %s."
msgstr "%s ei ole pysyviä banneja."
#: plugin.py:563
msgid ""
"[<channel>] <nick|hostmask> [<expires>]\n"
"\n"
" If you have the #channel,op capability, this will set a persistent\n"
" ignore on <hostmask> or the hostmask currently\n"
" associated with <nick>. <expires> is an optional argument\n"
" specifying when (in \"seconds from now\") the ignore will expire; if\n"
" it isn't given, the ignore will never automatically expire.\n"
" <channel> is only necessary if the message isn't sent in the\n"
" channel itself.\n"
" "
msgstr ""
"[<kanava>] <nimimerkki|hostmask> [<vanhentuu>]\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä asettaa pysyvän\n"
"ignoren <hostmaskiin> tai hostmaskiin\n"
"joka on <nimimerkin> käytössä. <Vanhentuu> on vaihtoehtoinen parametri\n"
"kun määritetään ( \"sekunteja alkaen nyt\")milloin ignore vanhentuu; jos\n"
"sitä ei ole annettu, banni ei ikinä vanhene automaattisesti.\n"
"<Kanava> on vaadittu vain jos viestiä ei lähetetä\n"
"kanavalla itsessään.\n"
" "
#: plugin.py:581
msgid ""
"[<channel>] <hostmask>\n"
"\n"
" If you have the #channel,op capability, this will remove the\n"
" persistent ignore on <hostmask> in the channel. <channel> is only\n"
" necessary if the message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<kanava>] <hostmask>\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä poistaa\n"
"pysyvän ignoren <hostmaskista> kanavalla. <Kanava> on vaadittu\n"
"viestiä ei lähetetä kanavalla itsellään.\n"
" "
#: plugin.py:593
msgid "There are no ignores for that hostmask."
msgstr "Tuolla hostmaskilla ei ole pysyviä ignoreja."
#: plugin.py:598
msgid ""
"[<channel>]\n"
"\n"
" Lists the hostmasks that the bot is ignoring on the given channel.\n"
" <channel> is only necessary if the message isn't sent in the\n"
" channel itself.\n"
" "
msgstr ""
"[<kanava>]\n"
"\n"
" Luettelee hostmaskit, jotka ovat botin ignoressa annetulla kanavalla.\n"
"<kanava> on vaadittu vain, jos viestiä ei lähetetä\n"
"kanavalla itsellään.\n"
" "
#: plugin.py:607
msgid "I'm not currently ignoring any hostmasks in %q"
msgstr "Minun ignoressani ei ole yhtään hostmaskia %q:ssa."
#: plugin.py:618
msgid ""
"[<channel>] <nick|username> <capability> [<capability> ...]\n"
"\n"
" If you have the #channel,op capability, this will give the user\n"
" <name> (or the user to whom <nick> maps)\n"
" the capability <capability> in the channel. <channel> is only\n"
" necessary if the message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<kanava>] <nimimerkki|käyttäjänimi> <valtuuus> [<valtuus> ...]\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä antaa käyttäjä\n"
"<nimelle> (tai käyttäjälle, jonka <nimimerkki> määrittää)\n"
" valtuuden <valtuus>kanavalla. <kanava> on\n"
"vaadittu vain jos viestiä ei lähetetä kanavalla itsessään.\n"
" "
#: plugin.py:634
msgid ""
"[<channel>] <name|hostmask> <capability> [<capability> ...]\n"
"\n"
" If you have the #channel,op capability, this will take from the\n"
" user currently identified as <name> (or the user to whom <hostmask>\n"
" maps) the capability <capability> in the channel. <channel> is only\n"
" necessary if the message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<kanava>] <nimi|hostmask> <valtuus> [<valtuus> ...]\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä ottaa\n"
"tällä hetkellä tunnistautuneelta käyttäjältä <nimi> (tai käyttäjältä, jonka <hostmask>\n"
"täsmää) valtuuden <valtuus> kanavalla. <Kanava> on\n"
" on vaadittu vain jos viestiä ei lähetetä kanavalla itsessään.\n"
" "
#: plugin.py:653
msgid "That user didn't have the %L %s."
msgstr "Tuolla käyttäjällä ei ole %L:ää %s:ssä."
#: plugin.py:662
#, fuzzy
msgid ""
"[<channel>] {True|False}\n"
"\n"
" If you have the #channel,op capability, this will set the default\n"
" response to non-power-related (that is, not {op, halfop, voice}\n"
" capabilities to be the value you give. <channel> is only necessary\n"
" if the message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<kanava>] {True|False}\n"
"\n"
" Jos sinulla on #kanava,op valtuus, tämä asettaa oletus\n"
"ei voimaan-liittyviin (se on, ei {op, halfop, voice}\n"
"valtuus arvo, jonka annat. <Kanava> on vaadittu vain\n"
"jos viestiä ei lähetetä kanavalla itsellään.\n"
" "
#: plugin.py:680
msgid ""
"[<channel>] <capability> [<capability> ...]\n"
"\n"
" If you have the #channel,op capability, this will add the channel\n"
" capability <capability> for all users in the channel. <channel> is\n"
" only necessary if the message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<Kanava>] <valtuus> [<valtuus> ...]\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä lisää\n"
"valtuuden <valtuus> kaikille käyttäjille kanavalla. <Kanava> on\n"
"vaadittu vain jos viestiä ei lähetetä kanavalla itsellään.\n"
" "
#: plugin.py:695
msgid ""
"[<channel>] <capability> [<capability> ...]\n"
"\n"
" If you have the #channel,op capability, this will unset the channel\n"
" capability <capability> so each user's specific capability or the\n"
" channel default capability will take precedence. <channel> is only\n"
" necessary if the message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<kanava>] <valtuus> [<valtuus> ...]\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä poistaa kanava valtuus\n"
"valtuuden <valtuus> joten jokaisen käyttäjäkohtainen valtuus tai\n"
"kanavan oletus valtuus otetaan käyttöön. <Kanava> on vaadittu\n"
"vain jos viestiä ei lähetetä kanavalla itsessään.\n"
" "
#: plugin.py:711
msgid "capability"
msgstr "valtuusMinä en tiedä %L:stä %s:llä"
#: plugin.py:714
msgid "I do not know about the %L %s."
msgstr ""
"[<kanava>]\n"
"\n"
" Palauttaa <kanavalla> olevat valtuudet. <Kanava> on\n"
"vaadittu vain jos viestiä ei lähetetä kanavalla itsellään.\n"
" "
#: plugin.py:721
msgid ""
"[<channel>]\n"
"\n"
" Returns the capabilities present on the <channel>. <channel> is\n"
" only necessary if the message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<kanava>] [<lisäosa>] [<komento>]\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä poistaa <komennon> käytöstä\n"
"<kanavalla>. Jos <lisäosa> on annrttu, <komento> poistetaan käytöstä\n"
"vain siitä lisäosasta. Jos vain <lisäosa> on annettu, kaikki komennot\n"
"annetusta lisäosasta poistetaan käytöstä. <Kanava> on vaadittu vain jos\n"
"viestiä ei lähetetä kanavalla itsellään.\n"
" "
#: plugin.py:733
msgid ""
"[<channel>] [<plugin>] [<command>]\n"
"\n"
" If you have the #channel,op capability, this will disable the <command>\n"
" in <channel>. If <plugin> is provided, <command> will be disabled only\n"
" for that plugin. If only <plugin> is provided, all commands in the\n"
" given plugin will be disabled. <channel> is only necessary if the\n"
" message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<kanava>] [<lisäosa>] [<komento>]\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä poistaa <komennon> käytöstä\n"
"<kanavalla>. Jos <lisäosa> on annrttu, <komento> poistetaan käytöstä\n"
"vain siitä lisäosasta. Jos vain <lisäosa> on annettu, kaikki komennot\n"
"annetusta lisäosasta poistetaan käytöstä. <Kanava> on vaadittu vain jos\n"
"viestiä ei lähetetä kanavalla itsellään.\n"
" "
#: plugin.py:749
#: plugin.py:788
msgid "The %s plugin does not have a command called %s."
msgstr "%s lisäosalla ei ole komentoa %s."
#: plugin.py:756
#: plugin.py:795
msgid "No plugin or command named %s could be found."
msgstr "Lisäosaa tai komentoa nimeltä %s ei löytynyt."
#: plugin.py:772
msgid ""
"[<channel>] [<plugin>] [<command>]\n"
"\n"
" If you have the #channel,op capability, this will enable the <command>\n"
" in <channel> if it has been disabled. If <plugin> is provided,\n"
" <command> will be enabled only for that plugin. If only <plugin> is\n"
" provided, all commands in the given plugin will be enabled. <channel>\n"
" is only necessary if the message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<kanava>] [<lisäosa>] [<komento>]\n"
"\n"
"Jos sinulla on #kanava,op valtuus, tämä ottaa <komennon> käyttöön\n"
" <kanavalla> jos se on ollut pois käytöstä. Jos <lisäosa> on annettu,\n"
" <komento> otetaan käyttöön vain siinä lisäosassa. Jos vain <lisäosa> on\n"
"annettu, kaikki komennot annetussa lisäosassa otetaan käyttööön. <Kanava>\n"
"on vaadittu vain jos viestiä ei lähetetä kanavalla itsellään.\n"
" "
#: plugin.py:809
msgid "%s was not disabled."
msgstr "%s ei ollut pois käytöstä."
#: plugin.py:818
msgid ""
"[<channel>]\n"
"\n"
" Returns the nicks in <channel>. <channel> is only necessary if the\n"
" message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<kanava>]\n"
"\n"
"Palauttaa nimimerkit, jotka ovat <kanavalla>. <Kanava> on vaadittu vain jos\n"
"ei lähetetä kanavalla itsellään.\n"
" "
#: plugin.py:829
msgid "You don't have access to that information."
msgstr "Sinulla ei ole pääsyoikeutta tuohon tietoon."
#: plugin.py:837
msgid ""
"Internal message for notifying all the #channel,ops in a channel of\n"
" a given situation."
msgstr ""
"Sisäinen viesti huomattamaan kaikkia #kanava,oppeja kanavalla\n"
" annetusta tilanteesta."
#: plugin.py:840
msgid "Alert to all %s ops: %s"
msgstr "Hälytys kaikki %s operaattoreille: %s"
#: plugin.py:842
msgid " (from %s)"
msgstr "(%s:tä)"
#: plugin.py:850
msgid ""
"[<channel>] <text>\n"
"\n"
" Sends <text> to all the users in <channel> who have the <channel>,op\n"
" capability.\n"
" "
msgstr ""
"[<kanava>] <teksti>\n"
"\n"
"Lähettää <tekstin> kaikille käyttäjille <kanavalla> joilla on <kanava>,op\n"
"valtuus.\n"
" "

View File

@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: Supybot-fr\n"
"POT-Creation-Date: 2010-10-25 13:10+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\n"
"PO-Revision-Date: \n"
"Last-Translator: Valentin Lorentz <progval@gmail.com>\n"
"Language-Team: Supybot-fr <progval@gmail.com>\n"
@ -644,35 +644,36 @@ msgstr "%s n'était pas désactivé."
#: plugin.py:818
msgid ""
"[<channel>]\n"
"[<channel>] [--count]\n"
"\n"
" Returns the nicks in <channel>. <channel> is only necessary if the\n"
" message isn't sent in the channel itself.\n"
" message isn't sent in the channel itself. Returns only the number of\n"
" nicks if --count option is provided.\n"
" "
msgstr ""
"[<canal>]\n"
"[<canal>] [--count]\n"
"\n"
"Retourne les nick sur le <canal>. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
"Retourne les nick sur le <canal>. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même. Ne retourne que le nombre de nicks si --count est donné."
#: plugin.py:829
#: plugin.py:830
msgid "You don't have access to that information."
msgstr "Vous n'avez pas accès à cette information"
#: plugin.py:837
#: plugin.py:843
msgid ""
"Internal message for notifying all the #channel,ops in a channel of\n"
" a given situation."
msgstr "Message interne pour notifier tous les #canal,ops sur un canal d'une situation donnée."
#: plugin.py:840
#: plugin.py:846
msgid "Alert to all %s ops: %s"
msgstr "Alerte à tous les ops de %s : %s"
#: plugin.py:842
#: plugin.py:848
msgid " (from %s)"
msgstr "(de %s)"
#: plugin.py:850
#: plugin.py:856
msgid ""
"[<channel>] <text>\n"
"\n"

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-25 13:10+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -579,33 +579,34 @@ msgstr ""
#: plugin.py:818
#, docstring
msgid ""
"[<channel>]\n"
"[<channel>] [--count]\n"
"\n"
" Returns the nicks in <channel>. <channel> is only necessary if the\n"
" message isn't sent in the channel itself.\n"
" message isn't sent in the channel itself. Returns only the number of\n"
" nicks if --count option is provided.\n"
" "
msgstr ""
#: plugin.py:829
#: plugin.py:830
msgid "You don't have access to that information."
msgstr ""
#: plugin.py:837
#: plugin.py:843
#, docstring
msgid ""
"Internal message for notifying all the #channel,ops in a channel of\n"
" a given situation."
msgstr ""
#: plugin.py:840
#: plugin.py:846
msgid "Alert to all %s ops: %s"
msgstr ""
#: plugin.py:842
#: plugin.py:848
msgid " (from %s)"
msgstr ""
#: plugin.py:850
#: plugin.py:856
#, docstring
msgid ""
"[<channel>] <text>\n"

View File

@ -814,11 +814,12 @@ class Channel(callbacks.Plugin):
additional('commandName')])
@internationalizeDocstring
def nicks(self, irc, msg, args, channel):
"""[<channel>]
def nicks(self, irc, msg, args, channel, optlist):
"""[<channel>] [--count]
Returns the nicks in <channel>. <channel> is only necessary if the
message isn't sent in the channel itself.
message isn't sent in the channel itself. Returns only the number of
nicks if --count option is provided.
"""
# Make sure we don't elicit information about private channels to
# people or channels that shouldn't know
@ -828,9 +829,14 @@ class Channel(callbacks.Plugin):
msg.nick not in irc.state.channels[channel].users):
irc.error(_('You don\'t have access to that information.'))
L = list(irc.state.channels[channel].users)
utils.sortBy(str.lower, L)
irc.reply(utils.str.commaAndify(L))
nicks = wrap(nicks, ['inChannel'])
keys = [option for (option, arg) in optlist]
if 'count' not in keys:
utils.sortBy(str.lower, L)
irc.reply(utils.str.commaAndify(L))
else:
irc.reply(str(len(L)))
nicks = wrap(nicks, ['inChannel',
getopts({'count':''})])
@internationalizeDocstring
def alertOps(self, irc, channel, s, frm=None):

View File

@ -214,5 +214,9 @@ class ChannelTestCase(ChannelPluginTestCase):
finally:
conf.supybot.protocols.irc.banmask.setValue(orig)
def testNicks(self):
self.assertResponse('channel nicks', 'bar, foo, and test')
self.assertResponse('channel nicks --count', '3')
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

View File

@ -0,0 +1,126 @@
msgid ""
msgstr ""
"Project-Id-Version: Supybot Channellogger\n"
"POT-Creation-Date: 2010-10-17 10:02+CEST\n"
"PO-Revision-Date: \n"
"Last-Translator: Mika Suomalainen <mika.henrik.mainio@hotmail.com>\n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Finnish\n"
"X-Poedit-Country: FINLAND\n"
#: config.py:46
msgid "Determines whether logging is enabled."
msgstr "Määrittää onko lokin pitäminen käytössä."
#: config.py:48
msgid ""
"Determines whether channel logfiles will be\n"
" flushed anytime they're written to, rather than being buffered by the\n"
" operating system."
msgstr ""
"Määrittää pitäisikö kanava lokitiedostot\n"
" tallentaa silloin kun ne kirjoitetaan, mielummin kuin käyttöjärjestelmän\n"
"puskuroimana."
#: config.py:52
msgid ""
"Determines whether formatting characters (such\n"
" as bolding, color, etc.) are removed when writing the logs to disk."
msgstr ""
"Määrittää pitääkö muotoilu merkit (kuten\n"
"korostukset, väri, jne.) poistaa kun lokeja kirjoitetaan levylle."
#: config.py:55
msgid ""
"Determines whether the logs for this channel are\n"
" timestamped with the timestamp in supybot.log.timestampFormat."
msgstr ""
"Määrittää laitetaanko tämän kanavan lokitiedostoihin\n"
"aikaleimat aikaleimalla, joka on määritetty asetuksella supybot.log.timestampFormat."
#: config.py:58
msgid ""
"Determines what string a message should be\n"
" prefixed with in order not to be logged. If you don't want any such\n"
" prefix, just set it to the empty string."
msgstr ""
"Määrittää millä merkkiketjulla aloitettuja viestejä\n"
"viestejä ei tallenneta lokiin. Jos et halua\n"
"merkkiketjua, aseta se tyhjäksi viestiketjuksi."
#: config.py:62
msgid ""
"Determines whether the bot will automatically\n"
" rotate the logs for this channel. The bot will rotate logs when the\n"
" timestamp for the log changes. The timestamp is set according to\n"
" the 'filenameTimestamp' configuration variable."
msgstr ""
"Määrittää kääntääkö botti automaattisesti\n"
"lokit tällä kanavalla. Botti kääntää lokit kun\n"
"kun aikaleima lokeille vaihtuu. Aikaleima asetetaan\n"
"'filenameTimestamp' asetuksen mukaan."
#: config.py:67
msgid ""
"Determines how to represent the timestamp\n"
" used for the filename in rotated logs. When this timestamp changes, the\n"
" old logfiles will be closed and a new one started. The format characters\n"
" for the timestamp are in the time.strftime docs at python.org. In order\n"
" for your logs to be rotated, you'll also have to enable\n"
" supybot.plugins.ChannelLogger.rotateLogs."
msgstr ""
"Määrittää kuinka aikaleima, jota käytetään\n"
"tiedostonimenä käännetyille lokeille. Kun tämä aikaleima muuttuu\n"
"vanhat lokitiedostot suljetaan ja uudet aloitetaan. Muotomerkit\n"
"aikaleimoille ovat time.strftime documenteissa python.org :issa. Saadaksesi\n"
"lokisi käännetyksi, sinun täytyy myös ottaa käyttöön\n"
"supybot.plugins.ChannelLogger.rotateLogs."
#: config.py:75
msgid ""
"Determines whether the bot will partition its\n"
" channel logs into separate directories based on different criteria."
msgstr ""
"Määrittää osioiko botti kanavalokinsa\n"
"eri hakemistoihin perustuen eri kriteereihin."
#: config.py:78
msgid ""
"Determines whether the bot will use a network\n"
" directory if using directories."
msgstr ""
"Määrittää käyttääkö botti verkkohakemistoa\n"
"jos käytetään hakemistoja."
#: config.py:81
msgid ""
"Determines whether the bot will use a channel\n"
" directory if using directories."
msgstr ""
"Määrittää käyttääkö botti kanavahakemistoa\n"
"jos käytetään hakemistoja."
#: config.py:84
msgid ""
"Determines whether the bot will use a timestamp\n"
" (determined by supybot.plugins.ChannelLogger.directories.timestamp.format)\n"
" if using directories."
msgstr ""
"Määrittää käyttääkö botti aikaleimaa\n"
" (supybot.plugins.ChannelLogger.directories.timestamp.format määrittämänä\n"
"jos käytetään hakemistoja."
#: config.py:88
msgid ""
"Determines what timestamp format will be used in\n"
" the directory stucture for channel logs if\n"
" supybot.plugins.ChannelLogger.directories.timestamp is True."
msgstr ""
"Määrittää mitä aikaleima muotoa käytetöön\n"
"hakemistorakenteessa kanavalokeille jos\n"
" supybot.plugins.ChannelLogger.directories.timestamp on True."

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 10:02+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"

View File

@ -226,10 +226,14 @@ class ChannelLogger(callbacks.Plugin):
'*** %s was kicked by %s\n', target, msg.nick)
def doPart(self, irc, msg):
if len(msg.args) > 1:
reason = " (%s)" % msg.args[1]
else:
reason = ""
for channel in msg.args[0].split(','):
self.doLog(irc, channel,
'*** %s <%s> has left %s\n',
msg.nick, msg.prefix, channel)
'*** %s <%s> has left %s%s\n',
msg.nick, msg.prefix, channel, reason)
def doMode(self, irc, msg):
channel = msg.args[0]
@ -247,13 +251,17 @@ class ChannelLogger(callbacks.Plugin):
'*** %s changes topic to "%s"\n', msg.nick, msg.args[1])
def doQuit(self, irc, msg):
if len(msg.args) == 1:
reason = " (%s)" % msg.args[0]
else:
reason = ""
if not isinstance(irc, irclib.Irc):
irc = irc.getRealIrc()
for (channel, chan) in self.lastStates[irc].channels.iteritems():
if msg.nick in chan.users:
self.doLog(irc, channel,
'*** %s <%s> has quit IRC\n',
msg.nick, msg.prefix)
'*** %s <%s> has quit IRC%s\n',
msg.nick, msg.prefix, reason)
def outFilter(self, irc, msg):
# Gotta catch my own messages *somehow* :)

View File

@ -0,0 +1,202 @@
msgid ""
msgstr ""
"Project-Id-Version: Supybot ChannelStats\n"
"POT-Creation-Date: 2010-10-16 09:41+CEST\n"
"PO-Revision-Date: \n"
"Last-Translator: Mika Suomalainen <mika.henrik.mainio@hotmail.com>\n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Finnish\n"
"X-Poedit-Country: FINLAND\n"
#: config.py:60
msgid ""
"Determines whether the bot will keep channel\n"
" statistics on itself, possibly skewing the channel stats (especially in\n"
" cases where the bot is relaying between channels on a network)."
msgstr ""
"Määrittää pitääkö botti kanava\n"
"tilastot itsellään, mahdollisesti vinouttaen kanava tilastot (etenkin\n"
"tilanteissa, joissa botti on välittämässä kanavien välillä verkossa)."
#: config.py:64
msgid ""
"Determines what\n"
" words (i.e., pieces of text with no spaces in them) are considered\n"
" 'smileys' for the purposes of stats-keeping."
msgstr ""
"Määrittää mitkä\n"
"sanat (esim. tekstin paloja ilman välilyöntiä niiden välissä ) lasketaan\n"
" 'hymiöiksi' tilastojen pitämisen takia."
#: config.py:68
msgid ""
"Determines what words\n"
" (i.e., pieces of text with no spaces in them ) are considered 'frowns' for\n"
" the purposes of stats-keeping."
msgstr ""
"Määrittää mitkä sanat\n"
" (esim. paloja tekstiä ilman välilyöntiä välissä ) lasketaan 'surioiksi'\n"
"tilastojen pitämistä varten."
#: plugin.py:246
msgid ""
"[<channel>] [<name>]\n"
"\n"
" Returns the statistics for <name> on <channel>. <channel> is only\n"
" necessary if the message isn't sent on the channel itself. If <name>\n"
" isn't given, it defaults to the user sending the command.\n"
" "
msgstr ""
"[<kanava>] [<nimi>]\n"
"\n"
" <nimi> <kanavalla>. <kanava> on vaadittu\n"
"jos viestiä ei lähetetä kanavalla itsessään. Jos <nimi>\n"
" ei ole annettu, se on oletuksena viestin lähettänyt henkilö.\n"
" "
#: plugin.py:259
msgid "I couldn't find you in my user database."
msgstr "Minä en voi löytää sinua käyttäjä tietokannastani."
#: plugin.py:272
#, fuzzy
msgid "%s has sent %n; a total of %n, %n, %n, and %n; %s of those messages %s. %s has joined %n, parted %n, quit %n, kicked someone %n, been kicked %n, changed the topic %n, and changed the mode %n."
msgstr "%s on lähettänyt %n; yhteensä %n, %n, %n, ja %n; %s noista viesteistä %s. %s on liittynyt %n, poistunut kanavalta %n, sulkenut %n, potkinut jonkun %n, ollut potkittu %n, vaihtanut aihetta %n, vaihtanut tilaa %n."
#: plugin.py:279
msgid "character"
msgstr "merkki"
#: plugin.py:280
#: plugin.py:363
msgid "word"
msgstr "sana"
#: plugin.py:281
#: plugin.py:364
msgid "smiley"
msgstr "hymiö"
#: plugin.py:282
#: plugin.py:365
msgid "frown"
msgstr "surio"
#: plugin.py:284
#: plugin.py:366
msgid "was an ACTION"
msgstr "oli TOIMINTO"
#: plugin.py:285
#: plugin.py:367
msgid "were ACTIONs"
msgstr "olivat TOIMINTOja"
#: plugin.py:287
#: plugin.py:288
#: plugin.py:289
#: plugin.py:290
#: plugin.py:291
#: plugin.py:292
#: plugin.py:293
msgid "time"
msgstr "aika"
#: plugin.py:296
msgid "I have no stats for that %s in %s."
msgstr "Minulla ei ole tilastoja %s:stä %s:ään."
#: plugin.py:304
msgid ""
"[<channel>] <stat expression>\n"
"\n"
" Returns the ranking of users according to the given stat expression.\n"
" Valid variables in the stat expression include 'msgs', 'chars',\n"
" 'words', 'smileys', 'frowns', 'actions', 'joins', 'parts', 'quits',\n"
" 'kicks', 'kicked', 'topics', and 'modes'. Any simple mathematical\n"
" expression involving those variables is permitted.\n"
" "
msgstr ""
"[<kanava>] <tilasto lauseke>\n"
"\n"
"Palauttaa käyttäjien arvoasteikon perustuen annettuun tilasto lausekkeeseen.\n"
"kelvolliset tilasto lausekkeet sisältävät 'msgs', 'chars',\n"
"'words', 'smileys', 'frowns', 'actions', 'joins', 'parts', 'quits',\n"
"'kicks', 'kicked', 'topics', and 'modes'. Mikä tahansa yksinkertainen matemaattinen\n"
" lauseke sisältäen nämä arvot on sallittu.\n"
" "
#: plugin.py:315
msgid "There's really no reason why you should have underscores or brackets in your mathematical expression. Please remove them."
msgstr "Ei ole mitään syytä miksi haluaisit alaviivoja tai sulkuja matemaattisessa lausekkeessa. Ole hyvä ja poista ne."
#: plugin.py:319
#, fuzzy
msgid "You can't use lambda in this command."
msgstr "Et voi käyttää lambdaa tässä komennossa."
#: plugin.py:333
msgid "stat variable"
msgstr "aloita muuttuja"
#: plugin.py:349
msgid ""
"[<channel>]\n"
"\n"
" Returns the statistics for <channel>. <channel> is only necessary if\n"
" the message isn't sent on the channel itself.\n"
" "
msgstr ""
"[<kanava>]\n"
"\n"
"Palauttaa tilastot <kanavalle>. <Kanava> on vaadittu vain jos viestiä ei lähetetä\n"
"kanavalla itsessään.\n"
" "
#: plugin.py:357
#, fuzzy
msgid "On %s there %h been %i messages, containing %i characters, %n, %n, and %n; %i of those messages %s. There have been %n, %n, %n, %n, %n, and %n. There %b currently %n and the channel has peaked at %n."
msgstr "%s:ssä %h on ollut %i viestiä, sisältäen %i merkkiä, %n, %n, ja %n; %i noista viesteistä %s. On ollut %n, %n, %n, %n, %n, ja %n. %b tällä hetkellä %n kanavalla on huipulla %n."
#: plugin.py:368
msgid "join"
msgstr "liity"
#: plugin.py:369
msgid "part"
msgstr "poistu"
#: plugin.py:370
msgid "quit"
msgstr "poistu"
#: plugin.py:371
msgid "kick"
msgstr "potki"
#: plugin.py:372
msgid "mode"
msgstr "tila"
#: plugin.py:372
#: plugin.py:373
msgid "change"
msgstr "vaihdos"
#: plugin.py:373
msgid "topic"
msgstr "aihe"
#: plugin.py:375
#: plugin.py:376
msgid "user"
msgstr "käyttäjä"
#: plugin.py:379
msgid "I've never been on %s."
msgstr "En ole ikinä ollut %s:llä."

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-16 09:41+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"

View File

@ -0,0 +1 @@
Insert a description of your plugin here, with any notes, etc. about using it.

View File

@ -0,0 +1,66 @@
###
# Copyright (c) 2010, Daniel Folkinshteyn
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
"""
Add a description of the plugin (to be presented to the user inside the wizard)
here. This should describe *what* the plugin does.
"""
import supybot
import supybot.world as world
# Use this for the version of this plugin. You may wish to put a CVS keyword
# in here if you're keeping the plugin in CVS or some similar system.
__version__ = "0.1"
# XXX Replace this with an appropriate author or supybot.Author instance.
__author__ = supybot.authors.unknown
# This is a dictionary mapping supybot.Author instances to lists of
# contributions.
__contributors__ = {}
# This is a url where the most recent plugin package can be downloaded.
__url__ = '' # 'http://supybot.com/Members/yourname/Conditional/download'
import config
import plugin
reload(plugin) # In case we're being reloaded.
# Add more reloads here if you add third-party modules and want them to be
# reloaded when this plugin is reloaded. Don't forget to import them as well!
if world.testing:
import test
Class = plugin.Class
configure = config.configure
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:

View File

@ -0,0 +1,59 @@
###
# Copyright (c) 2010, Daniel Folkinshteyn
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
import supybot.conf as conf
import supybot.registry as registry
try:
from supybot.i18n import PluginInternationalization
from supybot.i18n import internationalizeDocstring
_ = PluginInternationalization('Conditional')
except:
# This are useless functions that's allow to run the plugin on a bot
# without the i18n plugin
_ = lambda x:x
internationalizeDocstring = lambda x:x
def configure(advanced):
# This will be called by supybot to configure this module. advanced is
# a bool that specifies whether the user identified himself as an advanced
# user or not. You should effect your configuration by manipulating the
# registry as appropriate.
from supybot.questions import expect, anything, something, yn
conf.registerPlugin('Conditional', True)
Conditional = conf.registerPlugin('Conditional')
# This is where your configuration variables (if any) should go. For example:
# conf.registerGlobalValue(Conditional, 'someConfigVariableName',
# registry.Boolean(False, """Help for someConfigVariableName."""))
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:

View File

@ -0,0 +1 @@
# Stub so local is a module, used for third-party modules

View File

@ -0,0 +1,229 @@
msgid ""
msgstr ""
"Project-Id-Version: Gribble\n"
"POT-Creation-Date: 2010-11-02 11:39+CET\n"
"PO-Revision-Date: \n"
"Last-Translator: Valentin Lorentz <progval@gmail.com>\n"
"Language-Team: Supybot-fr <progval@gmail.com>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Français\n"
"X-Poedit-Country: France\n"
"X-Poedit-SourceCharset: ASCII\n"
#: plugin.py:64
msgid ""
"Add the help for \"@plugin help Conditional\" here\n"
" This should describe *how* to use this plugin."
msgstr ""
#: plugin.py:71
msgid "Run a command from message, as if command was sent over IRC."
msgstr ""
#: plugin.py:80
msgid ""
"<condition> <ifcommand> <elsecommand>\n"
" \n"
" Runs <ifcommand> if <condition> evaluates to true, runs <elsecommand>\n"
" if it evaluates to false.\n"
" \n"
" Use other logical operators defined in this plugin and command nesting\n"
" to your advantage here.\n"
" "
msgstr ""
"<condition> <commande1> <commande2>\n"
"\n"
"Exécute la <commande1> si la <condition> est évaluée à true, lance la <commande2> si elle est évaluée à false. Utilisez d'autres opérateurs logiques définis dans ce plugin et l'imbrication de commandes à votre avantage."
#: plugin.py:97
msgid ""
"<cond1> [<cond2> ... <condN>]\n"
" \n"
" Returns true if all conditions supplied evaluate to true.\n"
" "
msgstr ""
"<condition1> [<condition2> ... <conditionN>]\n"
"\n"
"Retourne True si toutes les conditions sont évaluées à true."
#: plugin.py:109
msgid ""
"<cond1> [<cond2> ... <condN>]\n"
" \n"
" Returns true if any one of conditions supplied evaluates to true.\n"
" "
msgstr ""
"<condition1> [<condition2> ... <conditionN>]\n"
"\n"
"Retourne True si une au moins des conditions est évaluée à true."
#: plugin.py:121
msgid ""
"<cond1> [<cond2> ... <condN>]\n"
" \n"
" Returns true if only one of conditions supplied evaluates to true.\n"
" "
msgstr ""
"<condition1> [<condition2> ... <conditionN>]\n"
"\n"
"Retourne True si une seule des conditions est évaluée à true."
#: plugin.py:133
msgid ""
"<item1> <item2>\n"
" \n"
" Does a string comparison on <item1> and <item2>. \n"
" Returns true if they are equal.\n"
" "
msgstr ""
"<élément1> <élément2>\n"
"\n"
"Fait une comparaison de chaîne entre <élément1> et <élément2>. Retourne true si ils sont égaux."
#: plugin.py:146
msgid ""
"<item1> <item2>\n"
" \n"
" Does a string comparison on <item1> and <item2>. \n"
" Returns true if they are not equal.\n"
" "
msgstr ""
"<élément1> <élément2>\n"
"\n"
"Fait une comparaison de chaîne entre <élément1> et <élément2>. Retourne true si ils sont inégaux."
#: plugin.py:159
msgid ""
"<item1> <item2>\n"
" \n"
" Does a string comparison on <item1> and <item2>. \n"
" Returns true if <item1> is greater than <item2>.\n"
" "
msgstr ""
"<élément1> <élément2>\n"
"\n"
"Fait une comparaison de chaîne entre <élément1> et <élément2>. Retourne true si <élément1> est plus grand que <élément2>"
#: plugin.py:172
msgid ""
"<item1> <item2>\n"
" \n"
" Does a string comparison on <item1> and <item2>. \n"
" Returns true if <item1> is greater than or equal to <item2>.\n"
" "
msgstr ""
"<élément1> <élément2>\n"
"\n"
"Fait une comparaison de chaîne entre <élément1> et <élément2>. Retourne true si <élément1> est plus grand ou égal à <élément2>"
#: plugin.py:185
msgid ""
"<item1> <item2>\n"
" \n"
" Does a string comparison on <item1> and <item2>. \n"
" Returns true if <item1> is less than <item2>.\n"
" "
msgstr ""
"<élément1> <élément2>\n"
"\n"
"Fait une comparaison de chaîne entre <élément1> et <élément2>. Retourne true si <élément1> est plus petit que <élément2>"
#: plugin.py:198
msgid ""
"<item1> <item2>\n"
" \n"
" Does a string comparison on <item1> and <item2>. \n"
" Returns true if <item1> is less than or equal to <item2>.\n"
" "
msgstr ""
"<élément1> <élément2>\n"
"\n"
"Fait une comparaison de chaîne entre <élément1> et <élément2>. Retourne true si <élément1> est plus petit ou égal à <élément2>"
#: plugin.py:211
msgid ""
"<item1> <item2>\n"
" \n"
" Determines if <item1> is a substring of <item2>. \n"
" Returns true if <item1> is contained in <item2>.\n"
" "
msgstr ""
"<élément1> <élément2>\n"
"\n"
"Détermine si <élément1> est une sous-chaîne de <élément2>. Retourne true si <élément1> est contenu dans <élément2>"
#: plugin.py:224
msgid ""
"<item1> <item2>\n"
" \n"
" Does a numeric comparison on <item1> and <item2>. \n"
" Returns true if they are equal.\n"
" "
msgstr ""
"<élément1> <élément2>\n"
"\n"
"Fait une comparaison numérique entre <élément1> et <élément2>. Retourne true si ils sont égaux."
#: plugin.py:237
msgid ""
"<item1> <item2>\n"
" \n"
" Does a numeric comparison on <item1> and <item2>. \n"
" Returns true if they are not equal.\n"
" "
msgstr ""
"<élément1> <élément2>\n"
"\n"
"Fait une comparaison numérique entre <élément1> et <élément2>. Retourne true si ils sont inégaux."
#: plugin.py:250
msgid ""
"<item1> <item2>\n"
" \n"
" Does a numeric comparison on <item1> and <item2>. \n"
" Returns true if they <item1> is greater than <item2>.\n"
" "
msgstr ""
"<élément1> <élément2>\n"
"\n"
"Fait une comparaison numérique entre <élément1> et <élément2>. Retourne true si <élément1> est plus grand que <élément2>"
#: plugin.py:263
msgid ""
"<item1> <item2>\n"
" \n"
" Does a numeric comparison on <item1> and <item2>. \n"
" Returns true if <item1> is greater than or equal to <item2>.\n"
" "
msgstr ""
"<élément1> <élément2>\n"
"\n"
"Fait une comparaison numérique entre <élément1> et <élément2>. Retourne true si <élément1> est plus grand ou égal à <élément2>"
#: plugin.py:276
msgid ""
"<item1> <item2>\n"
" \n"
" Does a numeric comparison on <item1> and <item2>. \n"
" Returns true if <item1> is less than <item2>.\n"
" "
msgstr ""
"<élément1> <élément2>\n"
"\n"
"Fait une comparaison numérique entre <élément1> et <élément2>. Retourne true si <élément1> est plus petit que <élément2>"
#: plugin.py:289
msgid ""
"<item1> <item2>\n"
" \n"
" Does a numeric comparison on <item1> and <item2>. \n"
" Returns true if <item1> is less than or equal to <item2>.\n"
" "
msgstr ""
"<élément1> <élément2>\n"
"\n"
"Fait une comparaison numérique entre <élément1> et <élément2>. Retourne true si <élément1> est plus petit ou égal à <élément2>"

View File

@ -0,0 +1,199 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-11-02 11:39+CET\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=CHARSET\n"
"Content-Transfer-Encoding: ENCODING\n"
"Generated-By: pygettext.py 1.5\n"
#: plugin.py:64
#, docstring
msgid ""
"Add the help for \"@plugin help Conditional\" here\n"
" This should describe *how* to use this plugin."
msgstr ""
#: plugin.py:71
#, docstring
msgid "Run a command from message, as if command was sent over IRC."
msgstr ""
#: plugin.py:80
#, docstring
msgid ""
"<condition> <ifcommand> <elsecommand>\n"
" \n"
" Runs <ifcommand> if <condition> evaluates to true, runs <elsecommand>\n"
" if it evaluates to false.\n"
" \n"
" Use other logical operators defined in this plugin and command nesting\n"
" to your advantage here.\n"
" "
msgstr ""
#: plugin.py:97
#, docstring
msgid ""
"<cond1> [<cond2> ... <condN>]\n"
" \n"
" Returns true if all conditions supplied evaluate to true.\n"
" "
msgstr ""
#: plugin.py:109
#, docstring
msgid ""
"<cond1> [<cond2> ... <condN>]\n"
" \n"
" Returns true if any one of conditions supplied evaluates to true.\n"
" "
msgstr ""
#: plugin.py:121
#, docstring
msgid ""
"<cond1> [<cond2> ... <condN>]\n"
" \n"
" Returns true if only one of conditions supplied evaluates to true.\n"
" "
msgstr ""
#: plugin.py:133
#, docstring
msgid ""
"<item1> <item2>\n"
" \n"
" Does a string comparison on <item1> and <item2>. \n"
" Returns true if they are equal.\n"
" "
msgstr ""
#: plugin.py:146
#, docstring
msgid ""
"<item1> <item2>\n"
" \n"
" Does a string comparison on <item1> and <item2>. \n"
" Returns true if they are not equal.\n"
" "
msgstr ""
#: plugin.py:159
#, docstring
msgid ""
"<item1> <item2>\n"
" \n"
" Does a string comparison on <item1> and <item2>. \n"
" Returns true if <item1> is greater than <item2>.\n"
" "
msgstr ""
#: plugin.py:172
#, docstring
msgid ""
"<item1> <item2>\n"
" \n"
" Does a string comparison on <item1> and <item2>. \n"
" Returns true if <item1> is greater than or equal to <item2>.\n"
" "
msgstr ""
#: plugin.py:185
#, docstring
msgid ""
"<item1> <item2>\n"
" \n"
" Does a string comparison on <item1> and <item2>. \n"
" Returns true if <item1> is less than <item2>.\n"
" "
msgstr ""
#: plugin.py:198
#, docstring
msgid ""
"<item1> <item2>\n"
" \n"
" Does a string comparison on <item1> and <item2>. \n"
" Returns true if <item1> is less than or equal to <item2>.\n"
" "
msgstr ""
#: plugin.py:211
#, docstring
msgid ""
"<item1> <item2>\n"
" \n"
" Determines if <item1> is a substring of <item2>. \n"
" Returns true if <item1> is contained in <item2>.\n"
" "
msgstr ""
#: plugin.py:224
#, docstring
msgid ""
"<item1> <item2>\n"
" \n"
" Does a numeric comparison on <item1> and <item2>. \n"
" Returns true if they are equal.\n"
" "
msgstr ""
#: plugin.py:237
#, docstring
msgid ""
"<item1> <item2>\n"
" \n"
" Does a numeric comparison on <item1> and <item2>. \n"
" Returns true if they are not equal.\n"
" "
msgstr ""
#: plugin.py:250
#, docstring
msgid ""
"<item1> <item2>\n"
" \n"
" Does a numeric comparison on <item1> and <item2>. \n"
" Returns true if they <item1> is greater than <item2>.\n"
" "
msgstr ""
#: plugin.py:263
#, docstring
msgid ""
"<item1> <item2>\n"
" \n"
" Does a numeric comparison on <item1> and <item2>. \n"
" Returns true if <item1> is greater than or equal to <item2>.\n"
" "
msgstr ""
#: plugin.py:276
#, docstring
msgid ""
"<item1> <item2>\n"
" \n"
" Does a numeric comparison on <item1> and <item2>. \n"
" Returns true if <item1> is less than <item2>.\n"
" "
msgstr ""
#: plugin.py:289
#, docstring
msgid ""
"<item1> <item2>\n"
" \n"
" Does a numeric comparison on <item1> and <item2>. \n"
" Returns true if <item1> is less than or equal to <item2>.\n"
" "
msgstr ""

View File

@ -0,0 +1,304 @@
###
# Copyright (c) 2010, Daniel Folkinshteyn
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
import supybot.utils as utils
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
import re
try:
from supybot.i18n import PluginInternationalization
from supybot.i18n import internationalizeDocstring
_ = PluginInternationalization('Conditional')
except:
# This are useless functions that's allow to run the plugin on a bot
# without the i18n plugin
_ = lambda x:x
internationalizeDocstring = lambda x:x
# builtin any is overwritten by callbacks... and python2.4 doesn't have it
def _any(iterable):
for element in iterable:
if element:
return True
return False
# for consistency with above, and for python2.4
def _all(iterable):
for element in iterable:
if not element:
return False
return True
class Conditional(callbacks.Plugin):
"""Add the help for "@plugin help Conditional" here
This should describe *how* to use this plugin."""
threaded = True
def __init__(self, irc):
callbacks.Plugin.__init__(self, irc)
def _runCommandFunction(self, irc, msg, command):
"""Run a command from message, as if command was sent over IRC."""
tokens = callbacks.tokenize(command)
try:
self.Proxy(irc.irc, msg, tokens)
except Exception, e:
log.exception('Uncaught exception in requested function:')
@internationalizeDocstring
def cif(self, irc, msg, args, condition, ifcommand, elsecommand):
"""<condition> <ifcommand> <elsecommand>
Runs <ifcommand> if <condition> evaluates to true, runs <elsecommand>
if it evaluates to false.
Use other logical operators defined in this plugin and command nesting
to your advantage here.
"""
if condition:
self._runCommandFunction(irc, msg, ifcommand)
else:
self._runCommandFunction(irc, msg, elsecommand)
irc.noReply()
cif = wrap(cif, ['boolean', 'something', 'something'])
@internationalizeDocstring
def cand(self, irc, msg, args, conds):
"""<cond1> [<cond2> ... <condN>]
Returns true if all conditions supplied evaluate to true.
"""
if _all(conds):
irc.reply("true")
else:
irc.reply("false")
cand = wrap(cand, [many('boolean'),])
@internationalizeDocstring
def cor(self, irc, msg, args, conds):
"""<cond1> [<cond2> ... <condN>]
Returns true if any one of conditions supplied evaluates to true.
"""
if _any(conds):
irc.reply("true")
else:
irc.reply("false")
cor = wrap(cor, [many('boolean'),])
@internationalizeDocstring
def cxor(self, irc, msg, args, conds):
"""<cond1> [<cond2> ... <condN>]
Returns true if only one of conditions supplied evaluates to true.
"""
if sum(conds) == 1:
irc.reply("true")
else:
irc.reply("false")
cxor = wrap(cxor, [many('boolean'),])
@internationalizeDocstring
def ceq(self, irc, msg, args, item1, item2):
"""<item1> <item2>
Does a string comparison on <item1> and <item2>.
Returns true if they are equal.
"""
if item1 == item2:
irc.reply('true')
else:
irc.reply('false')
ceq = wrap(ceq, ['anything', 'anything'])
@internationalizeDocstring
def ne(self, irc, msg, args, item1, item2):
"""<item1> <item2>
Does a string comparison on <item1> and <item2>.
Returns true if they are not equal.
"""
if item1 != item2:
irc.reply('true')
else:
irc.reply('false')
ne = wrap(ne, ['anything', 'anything'])
@internationalizeDocstring
def gt(self, irc, msg, args, item1, item2):
"""<item1> <item2>
Does a string comparison on <item1> and <item2>.
Returns true if <item1> is greater than <item2>.
"""
if item1 > item2:
irc.reply('true')
else:
irc.reply('false')
gt = wrap(gt, ['anything', 'anything'])
@internationalizeDocstring
def ge(self, irc, msg, args, item1, item2):
"""<item1> <item2>
Does a string comparison on <item1> and <item2>.
Returns true if <item1> is greater than or equal to <item2>.
"""
if item1 >= item2:
irc.reply('true')
else:
irc.reply('false')
ge = wrap(ge, ['anything', 'anything'])
@internationalizeDocstring
def lt(self, irc, msg, args, item1, item2):
"""<item1> <item2>
Does a string comparison on <item1> and <item2>.
Returns true if <item1> is less than <item2>.
"""
if item1 < item2:
irc.reply('true')
else:
irc.reply('false')
lt = wrap(lt, ['anything', 'anything'])
@internationalizeDocstring
def le(self, irc, msg, args, item1, item2):
"""<item1> <item2>
Does a string comparison on <item1> and <item2>.
Returns true if <item1> is less than or equal to <item2>.
"""
if item1 <= item2:
irc.reply('true')
else:
irc.reply('false')
le = wrap(le, ['anything', 'anything'])
@internationalizeDocstring
def match(self, irc, msg, args, item1, item2):
"""<item1> <item2>
Determines if <item1> is a substring of <item2>.
Returns true if <item1> is contained in <item2>.
"""
if item2.find(item1) != -1:
irc.reply('true')
else:
irc.reply('false')
match = wrap(match, ['something', 'something'])
@internationalizeDocstring
def nceq(self, irc, msg, args, item1, item2):
"""<item1> <item2>
Does a numeric comparison on <item1> and <item2>.
Returns true if they are equal.
"""
if item1 == item2:
irc.reply('true')
else:
irc.reply('false')
nceq = wrap(nceq, ['float', 'float'])
@internationalizeDocstring
def nne(self, irc, msg, args, item1, item2):
"""<item1> <item2>
Does a numeric comparison on <item1> and <item2>.
Returns true if they are not equal.
"""
if item1 != item2:
irc.reply('true')
else:
irc.reply('false')
nne = wrap(nne, ['float', 'float'])
@internationalizeDocstring
def ngt(self, irc, msg, args, item1, item2):
"""<item1> <item2>
Does a numeric comparison on <item1> and <item2>.
Returns true if they <item1> is greater than <item2>.
"""
if item1 > item2:
irc.reply('true')
else:
irc.reply('false')
ngt = wrap(ngt, ['float', 'float'])
@internationalizeDocstring
def nge(self, irc, msg, args, item1, item2):
"""<item1> <item2>
Does a numeric comparison on <item1> and <item2>.
Returns true if <item1> is greater than or equal to <item2>.
"""
if item1 >= item2:
irc.reply('true')
else:
irc.reply('false')
nge = wrap(nge, ['float', 'float'])
@internationalizeDocstring
def nlt(self, irc, msg, args, item1, item2):
"""<item1> <item2>
Does a numeric comparison on <item1> and <item2>.
Returns true if <item1> is less than <item2>.
"""
if item1 < item2:
irc.reply('true')
else:
irc.reply('false')
nlt = wrap(nlt, ['float', 'float'])
@internationalizeDocstring
def nle(self, irc, msg, args, item1, item2):
"""<item1> <item2>
Does a numeric comparison on <item1> and <item2>.
Returns true if <item1> is less than or equal to <item2>.
"""
if item1 <= item2:
irc.reply('true')
else:
irc.reply('false')
nle = wrap(nle, ['float', 'float'])
Condition = internationalizeDocstring(Conditional)
Class = Conditional
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

143
plugins/Conditional/test.py Normal file
View File

@ -0,0 +1,143 @@
###
# Copyright (c) 2010, Daniel Folkinshteyn
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
from supybot.test import *
class ConditionalTestCase(PluginTestCase):
plugins = ('Conditional','Utilities',)
def testCif(self):
self.assertError('cif stuff')
self.assertRegexp('cif [ceq bla bla] "echo moo" "echo foo"', 'moo')
self.assertRegexp('cif [ceq bla bar] "echo moo" "echo foo"', 'foo')
self.assertRegexp('cif [cand [ceq bla bla] [ne soo boo]] "echo moo" "echo foo"', 'moo')
self.assertRegexp('cif [ceq [echo $nick] "test"] "echo yay" "echo nay"', 'yay')
def testCand(self):
self.assertRegexp('cand true true', 'true')
self.assertRegexp('cand false true', 'false')
self.assertRegexp('cand true false', 'false')
self.assertRegexp('cand false false', 'false')
self.assertRegexp('cand true true true', 'true')
def testCor(self):
self.assertRegexp('cor true true', 'true')
self.assertRegexp('cor false true', 'true')
self.assertRegexp('cor true false', 'true')
self.assertRegexp('cor false false', 'false')
self.assertRegexp('cor true true true', 'true')
def testCxor(self):
self.assertRegexp('cxor true true', 'false')
self.assertRegexp('cxor false true', 'true')
self.assertRegexp('cxor true false', 'true')
self.assertRegexp('cxor false false', 'false')
self.assertRegexp('cxor true true true', 'false')
def testCeq(self):
self.assertRegexp('ceq bla bla', 'true')
self.assertRegexp('ceq bla moo', 'false')
self.assertError('ceq bla bla bla')
def testNe(self):
self.assertRegexp('ne bla bla', 'false')
self.assertRegexp('ne bla moo', 'true')
self.assertError('ne bla bla bla')
def testGt(self):
self.assertRegexp('gt bla bla', 'false')
self.assertRegexp('gt bla moo', 'false')
self.assertRegexp('gt moo bla', 'true')
self.assertError('gt bla bla bla')
def testGe(self):
self.assertRegexp('ge bla bla', 'true')
self.assertRegexp('ge bla moo', 'false')
self.assertRegexp('ge moo bla', 'true')
self.assertError('ge bla bla bla')
def testLt(self):
self.assertRegexp('lt bla bla', 'false')
self.assertRegexp('lt bla moo', 'true')
self.assertRegexp('lt moo bla', 'false')
self.assertError('lt bla bla bla')
def testLe(self):
self.assertRegexp('le bla bla', 'true')
self.assertRegexp('le bla moo', 'true')
self.assertRegexp('le moo bla', 'false')
self.assertError('le bla bla bla')
def testMatch(self):
self.assertRegexp('match bla mooblafoo', 'true')
self.assertRegexp('match bla mooblfoo', 'false')
self.assertError('match bla bla stuff')
def testNceq(self):
self.assertRegexp('nceq 10.0 10', 'true')
self.assertRegexp('nceq 4 5', 'false')
self.assertError('nceq 1 2 3')
self.assertError('nceq bla 1')
def testNne(self):
self.assertRegexp('nne 1 1', 'false')
self.assertRegexp('nne 2.2 3', 'true')
self.assertError('nne 1 2 3')
self.assertError('nne bla 3')
def testNgt(self):
self.assertRegexp('ngt 3 3', 'false')
self.assertRegexp('ngt 2 3', 'false')
self.assertRegexp('ngt 4 3', 'true')
self.assertError('ngt 1 2 3')
self.assertError('ngt 3 bla')
def testNge(self):
self.assertRegexp('nge 3 3', 'true')
self.assertRegexp('nge 3 4', 'false')
self.assertRegexp('nge 5 4.3', 'true')
self.assertError('nge 3 4.5 4')
self.assertError('nge 45 bla')
def testNlt(self):
self.assertRegexp('nlt 3 3', 'false')
self.assertRegexp('nlt 3 4.5', 'true')
self.assertRegexp('nlt 5 3', 'false')
self.assertError('nlt 2 3 4')
self.assertError('nlt bla bla')
def testNle(self):
self.assertRegexp('nle 2 2', 'true')
self.assertRegexp('nle 2 3.5', 'true')
self.assertRegexp('nle 4 3', 'false')
self.assertError('nle 3 4 5')
self.assertError('nle 1 bla')
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:

176
plugins/Config/locale/fi.po Normal file
View File

@ -0,0 +1,176 @@
msgid ""
msgstr ""
"Project-Id-Version: Finnish translation of Config plugin in Supybot\n"
"POT-Creation-Date: 2010-12-12 15:02+CET\n"
"PO-Revision-Date: \n"
"Last-Translator: Mika Suomalainen <mika.henrik.mainio@hotmail.com>\n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Finnish\n"
"X-Poedit-Country: FINLAND\n"
#: plugin.py:103
msgid "configuration variable"
msgstr "asetusarvo"
#: plugin.py:109
msgid "settable configuration variable"
msgstr "asetettava asetusarvo"
#: plugin.py:136
msgid ""
"<group>\n"
"\n"
" Returns the configuration variables available under the given\n"
" configuration <group>. If a variable has values under it, it is\n"
" preceded by an '@' sign. If a variable is a 'ChannelValue', that is,\n"
" it can be separately configured for each channel using the 'channel'\n"
" command in this plugin, it is preceded by an '#' sign.\n"
" "
msgstr ""
"<ryhmä>\n"
"\n"
"Palauttaa asetusarvot, jotka ovat annetun\n"
"asetus <ryhmän> alla. Jos arvolla on toisia arvoja allaan, se on\n"
" merkitty '@' merkillä. Jos arvo on 'ChannelValue', se voi olla,\n"
"erikseen määritelty jokaiselle kanavalle käyttämällä 'channel'\n"
"komentoa tässä lisäosassa, se on merkitty '#' merkillä.\n"
" "
#: plugin.py:148
msgid "There don't seem to be any values in %s."
msgstr "%s:ssä ei näytä olevan yhtään asetusarvoja."
#: plugin.py:154
msgid ""
"<word>\n"
"\n"
" Searches for <word> in the current configuration variables.\n"
" "
msgstr ""
"<sana>\n"
"\n"
"Etsii <sanaa> nykyisistä asetus arvoista.\n"
" "
#: plugin.py:167
msgid "There were no matching configuration variables."
msgstr "Täsmääviä asetusarvoja ei löytynyt."
#: plugin.py:174
msgid "Global: %s; %s: %s"
msgstr "Globaali: %s; %s: %s"
#: plugin.py:185
msgid "That registry variable has no value. Use the list command in this plugin to see what variables are available in this group."
msgstr "Sillä rekisteriarvolla ei ole arvoa. Käytä list komentoa tässä lisäosassa nähdäksesi mitä arvoja on saatavilla tässä ryhmässä."
#: plugin.py:200
msgid ""
"[<channel>] <name> [<value>]\n"
"\n"
" If <value> is given, sets the channel configuration variable for <name>\n"
" to <value> for <channel>. Otherwise, returns the current channel\n"
" configuration value of <name>. <channel> is only necessary if the\n"
" message isn't sent in the channel itself."
msgstr ""
"[<kanava>] <nimi> [<arvo>]\n"
"\n"
"Jos <arvo> on annettu, asettaa <nimen> kanavan asetusarvon\n"
"<arvoksi> <kanavalle>. Muutoin, palauttaa nykyisen \n"
"<nimen> nykyisen kanava asetusarvon. <Kanava> on vaadittu vain\n"
"jos viestiä ei lähetetä kanavalla itsellään."
#: plugin.py:207
msgid "That configuration variable is not a channel-specific configuration variable."
msgstr "Tällä asetusarvolla ei ole kanava kohtaista asetusarvoa."
#: plugin.py:220
msgid ""
"<name> [<value>]\n"
"\n"
" If <value> is given, sets the value of <name> to <value>. Otherwise,\n"
" returns the current value of <name>. You may omit the leading\n"
" \"supybot.\" in the name if you so choose.\n"
" "
msgstr ""
"<kanava> [<arvo>]\n"
"\n"
"Jos <arvo> on annettu, asettaa <nimen> arvon <arvoksi>. Muutoin palauttaa,\n"
"<nimen> nykyisen arvon. Voit jättää pois seuraavan rivin pois \n"
" \"supybot.\" .\n"
" "
#: plugin.py:234
msgid ""
"<name>\n"
"\n"
" Returns the description of the configuration variable <name>.\n"
" "
msgstr ""
"<nimi>\n"
"\n"
" Palauttaa asetusarvon kuvauksen <nimi>.\n"
" "
#: plugin.py:242
msgid " (Current value: %s)"
msgstr " (Nykyinen arvo: %s)"
#: plugin.py:245
msgid "That configuration group exists, but seems to have no help. Try \"config list %s\" to see if it has any children values."
msgstr "Tuo asetusryhmä on olemassa, mutta sillä ei näytä olevan ohjetta. Käytä komentoa \"config list %s\" nähdäksesi onko sillä yhtään alempia arvoja."
#: plugin.py:249
msgid "%s has no help."
msgstr "%s:llä ei ole ohjetta."
#: plugin.py:254
msgid ""
"<name>\n"
"\n"
" Returns the default value of the configuration variable <name>.\n"
" "
msgstr ""
"<name>\n"
"\n"
"Palauttaa asetusarvon oletusarvon <nimi>.\n"
" "
#: plugin.py:264
msgid ""
"takes no arguments\n"
"\n"
" Reloads the various configuration files (user database, channel\n"
" database, registry, etc.).\n"
" "
msgstr ""
"ei ota parametrejä\n"
"\n"
"Lataa uudelleen joitain asetustiedostoja(käyttäjä tietokanta, kanava\n"
" tietokanta, rekisteri, jne.).\n"
" "
#: plugin.py:275
msgid ""
"<filename>\n"
"\n"
" Exports the public variables of your configuration to <filename>.\n"
" If you want to show someone your configuration file, but you don't\n"
" want that person to be able to see things like passwords, etc., this\n"
" command will export a \"sanitized\" configuration file suitable for\n"
" showing publicly.\n"
" "
msgstr ""
"<tiedostonimi>\n"
"\n"
"Vie julkiset asetusarvot asetustiedostostasi <tiedostonimeen>.\n"
" Jos haluat näyttää jollekulle asetustiedostosi, mutta et\n"
"halua tuon henkilön näkevän salasanojasi, jne., tämä\n"
"komento vie \"järjellistetyn\" asetustiedoston, joka sopii\n"
"julkisesti näyttämiseen.\n"
" "

View File

@ -152,5 +152,5 @@ msgid ""
msgstr ""
"<nom de fichier>\n"
"\n"
"Exporte les variables de configuration publiques dans le fichier<nom de fichier. Si vous voulez donner à quelqu'un votre fichierde configuration, mais que vous ne voulez pas que cette personnepuisse voir des choses comme les mot de passe, ... cette commandedébarrasse le fichier de configuration exporté des données qui nedoivent pas être publiques."
"Exporte les variables de configuration publiques dans le fichier<nom de fichier. Si vous voulez donner à quelqu'un votre fichier de configuration, mais que vous ne voulez pas que cette personne puisse voir des choses comme les mots de passe, ... cette commande débarrasse le fichier de configuration exporté des données qui ne doivent pas être publiques."

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-12-12 15:02+CET\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"

View File

@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: Supybot-fr\n"
"POT-Creation-Date: 2010-10-26 18:57+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\n"
"PO-Revision-Date: \n"
"Last-Translator: Valentin Lorentz <progval@gmail.com>\n"
"Language-Team: Supybot-fr <progval@gmail.com>\n"
@ -13,7 +13,46 @@ msgstr ""
"X-Poedit-Country: France\n"
"X-Poedit-SourceCharset: ASCII\n"
#: plugin.py:77
msgid "PING ?(.*)"
msgstr ""
#: plugin.py:86
msgid "VERSION"
msgstr ""
#: plugin.py:91
msgid "USERINFO"
msgstr ""
#: plugin.py:96
msgid "TIME"
msgstr ""
#: plugin.py:101
msgid "FINGER"
msgstr ""
#: plugin.py:104
msgid "Supybot, the best Python IRC bot in existence!"
msgstr "Supybot, le meilleur bot IRC en Python au monde !"
#: plugin.py:107
msgid "SOURCE"
msgstr ""
#: plugin.py:123
msgid ""
"[<channel>] [--nicks]\n"
"\n"
" Sends a CTCP VERSION to <channel>, returning the various\n"
" version strings returned. It waits for 10 seconds before returning\n"
" the versions received at that point. If --nicks is given, nicks are\n"
" associated with the version strings; otherwise, only the version\n"
" strings are given.\n"
" "
msgstr ""
"[<canal>] [--nicks]\n"
"\n"
"Envoie un CTCP VERSION au canal, et renvoie les différentes réponses reçues. Il attend 10 secondes avant de renvoyer les réponses reçues jusqu'alors. Si --nicks est donné, les nicks sont associés à la chaîne de version ; sinon, seules les chaînes sont données."

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-26 18:57+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -15,7 +15,50 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n"
#: plugin.py:77
#, docstring
msgid "\001PING ?(.*)\001"
msgstr ""
#: plugin.py:86
#, docstring
msgid "\001VERSION\001"
msgstr ""
#: plugin.py:91
#, docstring
msgid "\001USERINFO\001"
msgstr ""
#: plugin.py:96
#, docstring
msgid "\001TIME\001"
msgstr ""
#: plugin.py:101
#, docstring
msgid "\001FINGER\001"
msgstr ""
#: plugin.py:104
msgid "Supybot, the best Python IRC bot in existence!"
msgstr ""
#: plugin.py:107
#, docstring
msgid "\001SOURCE\001"
msgstr ""
#: plugin.py:123
#, docstring
msgid ""
"[<channel>] [--nicks]\n"
"\n"
" Sends a CTCP VERSION to <channel>, returning the various\n"
" version strings returned. It waits for 10 seconds before returning\n"
" the versions received at that point. If --nicks is given, nicks are\n"
" associated with the version strings; otherwise, only the version\n"
" strings are given.\n"
" "
msgstr ""

View File

@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: Supybot-fr\n"
"POT-Creation-Date: 2010-10-17 10:39+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\n"
"PO-Revision-Date: \n"
"Last-Translator: Valentin Lorentz <progval@gmail.com>\n"
"Language-Team: Supybot-fr <progval@gmail.com>\n"
@ -34,7 +34,7 @@ msgid ""
" will use all dictionaries to define words."
msgstr "Détermine le dictionnaire par défaut dans lequel le bot cherchera les définitions. Si la valeur est '*' (sans les guillemets), le bot utilisera tous les dictionnaires pour définir le mot."
#: plugin.py:52
#: plugin.py:54
msgid ""
"takes no arguments\n"
"\n"
@ -45,7 +45,7 @@ msgstr ""
"\n"
"Retourne les dictionnaires valides pour la commande dict."
#: plugin.py:68
#: plugin.py:70
msgid ""
"takes no arguments\n"
"\n"
@ -56,7 +56,7 @@ msgstr ""
"\n"
"Retourne un dictionnaire valide aléatoire."
#: plugin.py:83
#: plugin.py:85
msgid ""
"[<dictionary>] <word>\n"
"\n"
@ -68,19 +68,33 @@ msgstr ""
"\n"
"Recherche la définition du mot sur le serveur dictd spécifié par la variable de configuration supybot.plugins.Dict.server."
#: plugin.py:106
#: plugin.py:108
msgid "You must give a word to define."
msgstr "Vous devez donner un mot à définir."
#: plugin.py:112
#: plugin.py:114
msgid "No definition for %q could be found."
msgstr "La définition de %q ne peut être trouvée."
#: plugin.py:115
#: plugin.py:117
msgid "No definition for %q could be found in %s"
msgstr "La définition de %q ne peut être trouvée dans %s."
#: plugin.py:127
#: plugin.py:129
msgid "%L responded: %s"
msgstr "%L a répondu : %s"
#: plugin.py:136
msgid ""
"<word> [<word> ...]\n"
" Gets a random synonym from the Moby Thesaurus (moby-thes) database.\n"
" \n"
" If given many words, gets a random synonym for each of them.\n"
" \n"
" Quote phrases to have them treated as one lookup word.\n"
" "
msgstr ""
"<mot> [<mot> ...]\n"
"\n"
"Récupère un synonyme aléatoire à partir de la base de données de Moby Thesaurus (moby-thes).Si plusieurs mots sont donnés, récupère un synonyme aléatoire pour chaque d'eux.Mettez les phrases entre guillemets pour qu'elles soient considérées comme un seul mot."

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 10:39+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -36,7 +36,7 @@ msgid ""
" will use all dictionaries to define words."
msgstr ""
#: plugin.py:52
#: plugin.py:54
#, docstring
msgid ""
"takes no arguments\n"
@ -45,7 +45,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:68
#: plugin.py:70
#, docstring
msgid ""
"takes no arguments\n"
@ -54,7 +54,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:83
#: plugin.py:85
#, docstring
msgid ""
"[<dictionary>] <word>\n"
@ -64,19 +64,31 @@ msgid ""
" "
msgstr ""
#: plugin.py:106
#: plugin.py:108
msgid "You must give a word to define."
msgstr ""
#: plugin.py:112
#: plugin.py:114
msgid "No definition for %q could be found."
msgstr ""
#: plugin.py:115
#: plugin.py:117
msgid "No definition for %q could be found in %s"
msgstr ""
#: plugin.py:127
#: plugin.py:129
msgid "%L responded: %s"
msgstr ""
#: plugin.py:136
#, docstring
msgid ""
"<word> [<word> ...]\n"
" Gets a random synonym from the Moby Thesaurus (moby-thes) database.\n"
" \n"
" If given many words, gets a random synonym for each of them.\n"
" \n"
" Quote phrases to have them treated as one lookup word.\n"
" "
msgstr ""

View File

@ -38,6 +38,8 @@ import supybot.callbacks as callbacks
from supybot.i18n import PluginInternationalization, internationalizeDocstring
_ = PluginInternationalization('Dict')
import random
try:
dictclient = utils.python.universalImport('dictclient', 'local.dictclient')
except ImportError:
@ -130,6 +132,33 @@ class Dict(callbacks.Plugin):
irc.reply(s)
dict = wrap(dict, [many('something')])
def synonym(self, irc, msg, args, words):
"""<word> [<word> ...]
Gets a random synonym from the Moby Thesaurus (moby-thes) database.
If given many words, gets a random synonym for each of them.
Quote phrases to have them treated as one lookup word.
"""
try:
server = conf.supybot.plugins.Dict.server()
conn = dictclient.Connection(server)
except socket.error, e:
irc.error(utils.web.strError(e), Raise=True)
dictionary = 'moby-thes'
response = []
for word in words:
definitions = conn.define(dictionary, word)
if not definitions:
asynonym = word
else:
defstr = definitions[0].getdefstr()
synlist = ' '.join(defstr.split('\n')).split(': ', 1)[1].split(',')
asynonym = random.choice(synlist).strip()
response.append(asynonym)
irc.reply(' '.join(response))
synonym = wrap(synonym, [many('something')])
Class = Dict

View File

@ -43,5 +43,10 @@ class DictTestCase(PluginTestCase):
def testRandomDictionary(self):
self.assertNotError('random')
self.assertNotError('dict [random] moo')
def testSynonym(self):
self.assertNotError('synonym stuff')
self.assertNotError('synonym someone goes home')
self.assertRegexp('synonym nanotube', 'nanotube')
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 10:48+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -21,7 +21,7 @@ msgid ""
" of the user giving an invalid command to the \"dunno\" response."
msgstr ""
#: plugin.py:38
#: plugin.py:37
#, docstring
msgid ""
"This plugin was written initially to work with MoobotFactoids, the two\n"

View File

@ -60,10 +60,20 @@ conf.registerChannelValue(Factoids, 'replyWhenInvalidCommand',
registry.Boolean(True, _("""Determines whether the bot will reply to invalid
commands by searching for a factoid; basically making the whatis
unnecessary when you want all factoids for a given key.""")))
conf.registerChannelValue(Factoids, 'replyApproximateSearchKeys',
registry.Boolean(True, _("""If you try to look up a nonexistent factoid,
this setting make the bot try to find some possible matching keys through
several approximate matching algorithms and return a list of matching keys,
before giving up.""")))
conf.registerChannelValue(Factoids, 'format',
FactoidFormat(_('$key could be $value.'), _("""Determines the format of
the response given when a factoid's value is requested. All the standard
substitutes apply, in addition to "$key" for the factoid's key and "$value"
for the factoid's value.""")))
conf.registerChannelValue(Factoids, 'keepRankInfo',
registry.Boolean(True, """Determines whether we keep updating the usage
count for each factoid, for popularity ranking."""))
conf.registerChannelValue(Factoids, 'rankListLength',
registry.Integer(20, """Determines the number of factoid keys returned
by the factrank command."""))
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

View File

@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: Supybot-fr\n"
"POT-Creation-Date: 2010-11-11 12:37+CET\n"
"POT-Creation-Date: 2011-02-26 09:58+CET\n"
"PO-Revision-Date: \n"
"Last-Translator: Valentin Lorentz <progval@gmail.com>\n"
"Language-Team: Supybot-fr <progval@gmail.com>\n"
@ -42,10 +42,18 @@ msgid ""
msgstr "Détermine si le bot répondra aux commandes invalides lors de la recherche d'une factoid ; permet simplement de rendre la commande 'whatis' inutile lorsque vous voulez toutes les factoids d'un clef donnée."
#: config.py:64
msgid ""
"If you try to look up a nonexistent factoid,\n"
" this setting make the bot try to find some possible matching keys through\n"
" several approximate matching algorithms and return a list of matching keys,\n"
" before giving up."
msgstr "Si vous essayez de chercher une factoid inexistante, cette option permet de faire en sorte que le bot recherche les clefs de factoids dont le nom est proche, et qu'il les affiche."
#: config.py:69
msgid "$key could be $value."
msgstr "$key semble être $value."
#: config.py:64
#: config.py:69
msgid ""
"Determines the format of\n"
" the response given when a factoid's value is requested. All the standard\n"
@ -53,7 +61,7 @@ msgid ""
" for the factoid's value."
msgstr "Détermine le format de la réponse donnée lorsqu'une valeur de factoid est demandée. Tous les substitus standards s'appliquent, en plus de \"$key\" pour la clef de la factoid et \"$value\" pour la valeur de la factoid."
#: plugin.py:153
#: plugin.py:179
msgid ""
"[<channel>] <key> %s <value>\n"
"\n"
@ -68,35 +76,95 @@ msgstr ""
"\n"
"Associer la <clef> avec la <valeur>. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même. Le mot '%s' est nécessaire pour séparer la clef de la valeur. Il peut être modifié avec la valeur de registre learnSeparator."
#: plugin.py:179
#: plugin.py:199
msgid ""
"Try to typo-match input to possible factoids.\n"
" \n"
" Assume first letter is correct, to reduce processing time. \n"
" First, try a simple wildcard search.\n"
" If that fails, use the Damerau-Levenshtein edit-distance metric.\n"
" "
msgstr ""
#: plugin.py:257
#: plugin.py:391
msgid "That's not a valid number for that key."
msgstr "Ce n'est pas un nombre valide pour cette clef."
#: plugin.py:199
#: plugin.py:345
#: plugin.py:279
#: plugin.py:377
#: plugin.py:604
msgid "No factoid matches that key."
msgstr "Aucune factoid ne correspond à cette clef."
#: plugin.py:211
#: plugin.py:304
msgid ""
"[<channel>] <key> [<number>]\n"
"[<channel>] [--raw] <key> [<number>]\n"
"\n"
" Looks up the value of <key> in the factoid database. If given a\n"
" number, will return only that exact factoid. <channel> is only\n"
" necessary if the message isn't sent in the channel itself.\n"
" number, will return only that exact factoid. If '--raw' option is\n"
" given, no variable substitution will take place on the factoid.\n"
" <channel> is only necessary if the message isn't sent in the channel\n"
" itself.\n"
" "
msgstr ""
"[<canal>] <clef> [<nombre>]\n"
"\n"
"Regarde la valeur de la <clef> dans la base de données de factoids. Si un <nombre> est donné, retourne la factoid exacte. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
"Regarde la valeur de la <clef> dans la base de données de factoids. Si un <nombre> est donné, retourne la factoid exacte.Si l'option --raw est donnée, aucune substitution de variable ne sera effectuée avant d'afficher la factoid.<canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
#: plugin.py:222
#: plugin.py:273
#: plugin.py:384
#: plugin.py:321
#: plugin.py:529
msgid "key id"
msgstr "id de clef"
#: plugin.py:230
#: plugin.py:339
msgid ""
"[<channel>] <oldkey> <newkey> [<number>]\n"
"\n"
" Adds a new key <newkey> for factoid associated with <oldkey>.\n"
" <number> is only necessary if there's more than one factoid associated\n"
" with <oldkey>.\n"
"\n"
" The same action can be accomplished by using the 'learn' function with\n"
" a new key but an existing (verbatim) factoid content.\n"
" "
msgstr ""
"[<canal>] <ancienne clef> <nouvelle clef> [<nombre>]\n"
"\n"
"Ajoute une <nouvelle clef> à la factoid correspondant à l'<ancienne clef>.Le <nombre> n'est nécessaire que si il y a plus d'une factoid associée à l'<ancienne clef>.La même action peut être accomplie en utilisant la fonction 'learn' avec la <nouvelle clef>, sans le contenu actuel de la factoid."
#: plugin.py:386
#: plugin.py:403
msgid "This key-factoid relationship already exists."
msgstr "Cette relation clef-factoid existe déjà."
#: plugin.py:394
msgid "This key has more than one factoid associated with it, but you have not provided a number."
msgstr "Cette clef a plus d'une factoid associée, mais vous n'avez pas fourni un nombre."
#: plugin.py:409
msgid ""
"[<channel>] [--plain] [--alpha] [<number>]\n"
"\n"
" Returns a list of top-ranked factoid keys, sorted by usage count\n"
" (rank). If <number> is not provided, the default number of factoid keys\n"
" returned is set by the rankListLength registry value.\n"
"\n"
" If --plain option is given, rank numbers and usage counts are not\n"
" included in output.\n"
"\n"
" If --alpha option is given in addition to --plain, keys are sorted\n"
" alphabetically, instead of by rank.\n"
"\n"
" <channel> is only necessary if the message isn't sent in the channel\n"
" itself.\n"
" "
msgstr ""
"[<canal>] [--plain] [--alpha] [<nombre>]\n"
"\n"
"Retourne une liste des factoids les plus utilisées. Si le <nombre> n'est pas fourni, il correspond par défaut au nombre de clefs de factoids défini dans la clef de registre rankListLength.Si --plain est donné, le numéro des rangs et le comptage des utilisations n'est pas inclu dans la sortie.Si --alpha est donné, en plus de --plain, les clefs seront triées alphabétiquement, au lieu de l'être par leur rang.<canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
#: plugin.py:454
msgid ""
"[<channel>] <key>\n"
"\n"
@ -109,7 +177,7 @@ msgstr ""
"\n"
"Verrouille la/les factoid(s) associé(e) à la <clef>, pour qu'elles ne puissent plus être supprimées ou modifiées. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
#: plugin.py:245
#: plugin.py:472
msgid ""
"[<channel>] <key>\n"
"\n"
@ -122,34 +190,39 @@ msgstr ""
"\n"
"Verrouille la/les factoid(s) associé(e) à la <clef>, pour qu'elles puissent être supprimées ou modifiées. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
#: plugin.py:260
#: plugin.py:511
msgid ""
"[<channel>] <key> [<number>|*]\n"
"\n"
" Removes the factoid <key> from the factoids database. If there are\n"
" more than one factoid with such a key, a number is necessary to\n"
" determine which one should be removed. A * can be used to remove all\n"
" factoids associated with a key. <channel> is only necessary if\n"
" Removes a key-fact relationship for key <key> from the factoids\n"
" database. If there is more than one such relationship for this key,\n"
" a number is necessary to determine which one should be removed.\n"
" A * can be used to remove all relationships for <key>.\n"
"\n"
" If as a result, the key (factoid) remains without any relationships to\n"
" a factoid (key), it shall be removed from the database.\n"
"\n"
" <channel> is only necessary if\n"
" the message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<canal>] <clef> [<nombre>|*]\n"
"\n"
"Enlève la factoid <clef> de la base de données. Si il y a plus d'une factoid avec cette clef, un <nombre> est requis pour déterminer laquelle sera supprimée. Un joker * peut être utilisé pour enlever toutes les factoids avec cette clef. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
"Enlève la factoid <clef> de la base de données. Si il y a plus d'une factoid avec cette clef, un <nombre> est requis pour déterminer laquelle sera supprimée. Un joker * peut être utilisé pour enlever toutes les factoids avec cette clef.Si, en tant que résultat, la clef (factoid) n'a plus aucune relation avec une autre factoid (clef), elle devrait être supprimée de la base de données.<canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
#: plugin.py:285
#: plugin.py:543
msgid "There is no such factoid."
msgstr "Cette factoid n'existe pas."
#: plugin.py:298
#: plugin.py:553
msgid "Invalid factoid number."
msgstr "Numéro de factoid invalide."
#: plugin.py:304
#: plugin.py:558
msgid "%s factoids have that key. Please specify which one to remove, or use * to designate all of them."
msgstr "%s factoids ont cette clef. Veuillez spécifier laquelle vous voulez supprimer ou utiliser * pour toutes les désigner."
#: plugin.py:312
#: plugin.py:566
msgid ""
"[<channel>]\n"
"\n"
@ -161,11 +234,11 @@ msgstr ""
"\n"
"Retourne une factoid aléatoire de la base de données pour le canal. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
#: plugin.py:330
#: plugin.py:588
msgid "I couldn't find a factoid."
msgstr "Je ne peux trouver une factoid"
#: plugin.py:335
#: plugin.py:593
msgid ""
"[<channel>] <key>\n"
"\n"
@ -178,11 +251,15 @@ msgstr ""
"\n"
"Donne des informations sur la/les factoid(s) associée(s) à la <clef>. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
#: plugin.py:358
msgid "#%i was added by %s at %s"
msgstr "#%i a été ajouté par %s le %s"
#: plugin.py:619
msgid "#%i was added by %s at %s, and has been recalled %n"
msgstr "#%i a été ajouté par %s le %s, et il y a eu %n."
#: plugin.py:369
#: plugin.py:622
msgid "time"
msgstr "rappel"
#: plugin.py:632
msgid ""
"[<channel>] <key> <number> <regexp>\n"
"\n"
@ -194,11 +271,11 @@ msgstr ""
"\n"
"Change la factoid <nombre> associée à la <clef>, en accord avec l'<expression régulière>. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
#: plugin.py:381
#: plugin.py:646
msgid "I couldn't find any key %q"
msgstr "Je ne peux trouver de clef %q"
#: plugin.py:396
#: plugin.py:661
msgid ""
"[<channel>] [--values] [--{regexp} <value>] [<glob> ...]\n"
"\n"
@ -211,11 +288,13 @@ msgstr ""
"\n"
"Recherche les clefs correspondant au <glob>. Si --regexp est donné, recherche les clefs correspondantes à l'<expression régulière>. Si --values est donné, recherche parmi les valeurs, plutôt que parmi les clefs. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
#: plugin.py:431
#: plugin.py:698
#: plugin.py:707
msgid "No keys matched that query."
msgstr "Aucune clef ne correspond à cette requête."
#: plugin.py:436
#: plugin.py:703
#: plugin.py:712
msgid "More than 100 keys matched that query; please narrow your query."
msgstr "Plus de 100 clefs correspondent à votre requête ; veuillez la préciser."

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-11-11 12:37+CET\n"
"POT-Creation-Date: 2011-02-26 09:58+CET\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"
@ -45,10 +45,18 @@ msgid ""
msgstr ""
#: config.py:64
msgid ""
"If you try to look up a nonexistent factoid,\n"
" this setting make the bot try to find some possible matching keys through\n"
" several approximate matching algorithms and return a list of matching keys,\n"
" before giving up."
msgstr ""
#: config.py:69
msgid "$key could be $value."
msgstr ""
#: config.py:64
#: config.py:69
msgid ""
"Determines the format of\n"
" the response given when a factoid's value is requested. All the standard\n"
@ -56,7 +64,7 @@ msgid ""
" for the factoid's value."
msgstr ""
#: plugin.py:153
#: plugin.py:179
msgid ""
"[<channel>] <key> %s <value>\n"
"\n"
@ -68,30 +76,85 @@ msgid ""
" "
msgstr ""
#: plugin.py:179
msgid "That's not a valid number for that key."
msgstr ""
#: plugin.py:199 plugin.py:345
msgid "No factoid matches that key."
msgstr ""
#: plugin.py:211
#: plugin.py:199
#, docstring
msgid ""
"[<channel>] <key> [<number>]\n"
"\n"
" Looks up the value of <key> in the factoid database. If given a\n"
" number, will return only that exact factoid. <channel> is only\n"
" necessary if the message isn't sent in the channel itself.\n"
"Try to typo-match input to possible factoids.\n"
" \n"
" Assume first letter is correct, to reduce processing time. \n"
" First, try a simple wildcard search.\n"
" If that fails, use the Damerau-Levenshtein edit-distance metric.\n"
" "
msgstr ""
#: plugin.py:222 plugin.py:273 plugin.py:384
#: plugin.py:257 plugin.py:391
msgid "That's not a valid number for that key."
msgstr ""
#: plugin.py:279 plugin.py:377 plugin.py:604
msgid "No factoid matches that key."
msgstr ""
#: plugin.py:304
#, docstring
msgid ""
"[<channel>] [--raw] <key> [<number>]\n"
"\n"
" Looks up the value of <key> in the factoid database. If given a\n"
" number, will return only that exact factoid. If '--raw' option is\n"
" given, no variable substitution will take place on the factoid.\n"
" <channel> is only necessary if the message isn't sent in the channel\n"
" itself.\n"
" "
msgstr ""
#: plugin.py:321 plugin.py:529
msgid "key id"
msgstr ""
#: plugin.py:230
#: plugin.py:339
#, docstring
msgid ""
"[<channel>] <oldkey> <newkey> [<number>]\n"
"\n"
" Adds a new key <newkey> for factoid associated with <oldkey>.\n"
" <number> is only necessary if there's more than one factoid associated\n"
" with <oldkey>.\n"
"\n"
" The same action can be accomplished by using the 'learn' function with\n"
" a new key but an existing (verbatim) factoid content.\n"
" "
msgstr ""
#: plugin.py:386 plugin.py:403
msgid "This key-factoid relationship already exists."
msgstr ""
#: plugin.py:394
msgid "This key has more than one factoid associated with it, but you have not provided a number."
msgstr ""
#: plugin.py:409
#, docstring
msgid ""
"[<channel>] [--plain] [--alpha] [<number>]\n"
"\n"
" Returns a list of top-ranked factoid keys, sorted by usage count\n"
" (rank). If <number> is not provided, the default number of factoid keys\n"
" returned is set by the rankListLength registry value.\n"
"\n"
" If --plain option is given, rank numbers and usage counts are not\n"
" included in output.\n"
"\n"
" If --alpha option is given in addition to --plain, keys are sorted\n"
" alphabetically, instead of by rank.\n"
"\n"
" <channel> is only necessary if the message isn't sent in the channel\n"
" itself.\n"
" "
msgstr ""
#: plugin.py:454
#, docstring
msgid ""
"[<channel>] <key>\n"
@ -102,7 +165,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:245
#: plugin.py:472
#, docstring
msgid ""
"[<channel>] <key>\n"
@ -113,32 +176,37 @@ msgid ""
" "
msgstr ""
#: plugin.py:260
#: plugin.py:511
#, docstring
msgid ""
"[<channel>] <key> [<number>|*]\n"
"\n"
" Removes the factoid <key> from the factoids database. If there are\n"
" more than one factoid with such a key, a number is necessary to\n"
" determine which one should be removed. A * can be used to remove all\n"
" factoids associated with a key. <channel> is only necessary if\n"
" Removes a key-fact relationship for key <key> from the factoids\n"
" database. If there is more than one such relationship for this key,\n"
" a number is necessary to determine which one should be removed.\n"
" A * can be used to remove all relationships for <key>.\n"
"\n"
" If as a result, the key (factoid) remains without any relationships to\n"
" a factoid (key), it shall be removed from the database.\n"
"\n"
" <channel> is only necessary if\n"
" the message isn't sent in the channel itself.\n"
" "
msgstr ""
#: plugin.py:285
#: plugin.py:543
msgid "There is no such factoid."
msgstr ""
#: plugin.py:298
#: plugin.py:553
msgid "Invalid factoid number."
msgstr ""
#: plugin.py:304
#: plugin.py:558
msgid "%s factoids have that key. Please specify which one to remove, or use * to designate all of them."
msgstr ""
#: plugin.py:312
#: plugin.py:566
#, docstring
msgid ""
"[<channel>]\n"
@ -148,11 +216,11 @@ msgid ""
" "
msgstr ""
#: plugin.py:330
#: plugin.py:588
msgid "I couldn't find a factoid."
msgstr ""
#: plugin.py:335
#: plugin.py:593
#, docstring
msgid ""
"[<channel>] <key>\n"
@ -163,11 +231,15 @@ msgid ""
" "
msgstr ""
#: plugin.py:358
msgid "#%i was added by %s at %s"
#: plugin.py:619
msgid "#%i was added by %s at %s, and has been recalled %n"
msgstr ""
#: plugin.py:369
#: plugin.py:622
msgid "time"
msgstr ""
#: plugin.py:632
#, docstring
msgid ""
"[<channel>] <key> <number> <regexp>\n"
@ -177,11 +249,11 @@ msgid ""
" "
msgstr ""
#: plugin.py:381
#: plugin.py:646
msgid "I couldn't find any key %q"
msgstr ""
#: plugin.py:396
#: plugin.py:661
#, docstring
msgid ""
"[<channel>] [--values] [--{regexp} <value>] [<glob> ...]\n"
@ -192,11 +264,11 @@ msgid ""
" "
msgstr ""
#: plugin.py:431
#: plugin.py:698 plugin.py:707
msgid "No keys matched that query."
msgstr ""
#: plugin.py:436
#: plugin.py:703 plugin.py:712
msgid "More than 100 keys matched that query; please narrow your query."
msgstr ""

View File

@ -43,11 +43,12 @@ from supybot.i18n import PluginInternationalization, internationalizeDocstring
_ = PluginInternationalization('Factoids')
try:
import sqlite
import sqlite3
except ImportError:
raise callbacks.Error, 'You need to have PySQLite installed to use this ' \
'plugin. Download it at ' \
'<http://code.google.com/p/pysqlite/>'
from pysqlite2 import dbapi2 as sqlite3 # for python2.4
import re
from supybot.utils.seq import dameraulevenshtein
def getFactoid(irc, msg, args, state):
assert not state.channel
@ -85,27 +86,29 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler):
def makeDb(self, filename):
if os.path.exists(filename):
return sqlite.connect(filename)
db = sqlite.connect(filename)
db = sqlite3.connect(filename)
db.text_factory = str
return db
db = sqlite3.connect(filename)
db.text_factory = str
cursor = db.cursor()
cursor.execute("""CREATE TABLE keys (
id INTEGER PRIMARY KEY,
key TEXT UNIQUE ON CONFLICT IGNORE,
locked BOOLEAN
key TEXT UNIQUE ON CONFLICT REPLACE
)""")
cursor.execute("""CREATE TABLE factoids (
id INTEGER PRIMARY KEY,
key_id INTEGER,
added_by TEXT,
added_at TIMESTAMP,
fact TEXT
fact TEXT UNIQUE ON CONFLICT REPLACE,
locked BOOLEAN
)""")
cursor.execute("""CREATE TABLE relations (
id INTEGER PRIMARY KEY,
key_id INTEGER,
fact_id INTEGER,
usage_count INTEGER
)""")
cursor.execute("""CREATE TRIGGER remove_factoids
BEFORE DELETE ON keys
BEGIN
DELETE FROM factoids WHERE key_id = old.id;
END
""")
db.commit()
return db
@ -126,29 +129,52 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler):
doc=method._fake__doc__ % (s, s),
name=callbacks.formatCommand(command))
return super(Factoids, self).getCommandHelp(command, simpleSyntax)
def learn(self, irc, msg, args, channel, key, factoid):
def _getKeyAndFactId(self, channel, key, factoid):
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("SELECT id, locked FROM keys WHERE key LIKE %s", key)
if cursor.rowcount == 0:
cursor.execute("""INSERT INTO keys VALUES (NULL, %s, 0)""", key)
cursor.execute("SELECT id FROM keys WHERE key=?", (key,))
keyresults = cursor.fetchall()
cursor.execute("SELECT id FROM factoids WHERE fact=?", (factoid,))
factresults = cursor.fetchall()
return (keyresults, factresults,)
def learn(self, irc, msg, args, channel, key, factoid):
# if neither key nor factoid exist, add them.
# if key exists but factoid doesn't, add factoid, link it to existing key
# if factoid exists but key doesn't, add key, link it to existing factoid
# if both key and factoid already exist, and are linked, do nothing, print nice message
db = self.getDb(channel)
cursor = db.cursor()
(keyid, factid) = self._getKeyAndFactId(channel, key, factoid)
if len(keyid) == 0:
cursor.execute("""INSERT INTO keys VALUES (NULL, ?)""", (key,))
db.commit()
cursor.execute("SELECT id, locked FROM keys WHERE key LIKE %s",key)
(id, locked) = map(int, cursor.fetchone())
capability = ircdb.makeChannelCapability(channel, 'factoids')
if not locked:
if len(factid) == 0:
if ircdb.users.hasUser(msg.prefix):
name = ircdb.users.getUser(msg.prefix).name
else:
name = msg.nick
cursor.execute("""INSERT INTO factoids VALUES
(NULL, %s, %s, %s, %s)""",
id, name, int(time.time()), factoid)
(NULL, ?, ?, ?, ?)""",
(name, int(time.time()), factoid, 0))
db.commit()
(keyid, factid) = self._getKeyAndFactId(channel, key, factoid)
cursor.execute("""SELECT id, key_id, fact_id from relations
WHERE key_id=? AND fact_id=?""",
(keyid[0][0], factid[0][0],))
existingrelation = cursor.fetchall()
if len(existingrelation) == 0:
cursor.execute("""INSERT INTO relations VALUES (NULL, ?, ?, ?)""",
(keyid[0][0],factid[0][0],0,))
db.commit()
irc.replySuccess()
else:
irc.error('That factoid is locked.')
irc.error("This key-factoid relationship already exists.")
learn = wrap(learn, ['factoid'])
learn._fake__doc__ = _("""[<channel>] <key> %s <value>
@ -163,18 +189,70 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler):
def _lookupFactoid(self, channel, key):
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT factoids.fact FROM factoids, keys
WHERE keys.key LIKE %s AND factoids.key_id=keys.id
cursor.execute("""SELECT factoids.fact, factoids.id, relations.id FROM factoids, keys, relations
WHERE keys.key LIKE ? AND relations.key_id=keys.id AND relations.fact_id=factoids.id
ORDER BY factoids.id
LIMIT 20""", key)
return [t[0] for t in cursor.fetchall()]
def _replyFactoids(self, irc, msg, key, factoids,
number=0, error=True):
LIMIT 20""", (key,))
return cursor.fetchall()
def _searchFactoid(self, channel, key):
"""Try to typo-match input to possible factoids.
Assume first letter is correct, to reduce processing time.
First, try a simple wildcard search.
If that fails, use the Damerau-Levenshtein edit-distance metric.
"""
# if you made a typo in a two-character key, boo on you.
if len(key) < 3:
return []
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT key FROM keys WHERE key LIKE ?""", ('%' + key + '%',))
wildcardkeys = cursor.fetchall()
if len(wildcardkeys) > 0:
return [line[0] for line in wildcardkeys]
cursor.execute("""SELECT key FROM keys WHERE key LIKE ?""", (key[0] + '%',))
flkeys = cursor.fetchall()
if len(flkeys) == 0:
return []
flkeys = [line[0] for line in flkeys]
dl_metrics = [dameraulevenshtein(key, sourcekey) for sourcekey in flkeys]
dict_metrics = dict(zip(flkeys, dl_metrics))
if min(dl_metrics) <= 2:
return [key for key,item in dict_metrics.iteritems() if item <= 2]
if min(dl_metrics) <= 3:
return [key for key,item in dict_metrics.iteritems() if item <= 3]
return []
def _updateRank(self, channel, factoids):
if self.registryValue('keepRankInfo', channel):
db = self.getDb(channel)
cursor = db.cursor()
for (fact,factid,relationid) in factoids:
cursor.execute("""SELECT relations.usage_count
FROM relations
WHERE relations.id=?""", (relationid,))
old_count = cursor.fetchall()[0][0]
cursor.execute("UPDATE relations SET usage_count=? WHERE id=?",
(old_count + 1, relationid,))
db.commit()
def _replyFactoids(self, irc, msg, key, channel, factoids,
number=0, error=True, raw=False):
def format_fact(text):
if raw:
return text
else:
return ircutils.standardSubstitute(irc, msg, text)
if factoids:
if number:
try:
irc.reply(factoids[number-1])
irc.reply(format_fact(factoids[number-1][0]))
self._updateRank(channel, [factoids[number-1]])
except IndexError:
irc.error(_('That\'s not a valid number for that key.'))
return
@ -186,34 +264,55 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler):
return ircutils.standardSubstitute(irc, msg,
formatter, env)
if len(factoids) == 1:
irc.reply(prefixer(factoids[0]))
irc.reply(format_fact(prefixer(factoids[0][0])))
else:
factoidsS = []
counter = 1
for factoid in factoids:
factoidsS.append(format('(#%i) %s', counter, factoid))
factoidsS.append(format('(#%i) %s', counter,
format_fact(factoid[0])))
counter += 1
irc.replies(factoidsS, prefixer=prefixer,
joiner=', or ', onlyPrefixFirst=True)
self._updateRank(channel, factoids)
elif error:
irc.error(_('No factoid matches that key.'))
def _replyApproximateFactoids(self, irc, msg, channel, key, error=True):
if self.registryValue('replyApproximateSearchKeys'):
factoids = self._searchFactoid(channel, key)
if factoids:
keylist = ["'%s'" % (fact,) for fact in factoids]
keylist = ', '.join(keylist)
irc.reply("I do not know about '%s', but I do know about these similar topics: %s" % (key, keylist))
elif error:
irc.error('No factoid matches that key.')
def invalidCommand(self, irc, msg, tokens):
if irc.isChannel(msg.args[0]):
channel = msg.args[0]
if self.registryValue('replyWhenInvalidCommand', channel):
key = ' '.join(tokens)
factoids = self._lookupFactoid(channel, key)
self._replyFactoids(irc, msg, key, factoids, error=False)
if factoids:
self._replyFactoids(irc, msg, key, channel, factoids, error=False)
else:
self._replyApproximateFactoids(irc, msg, channel, key, error=False)
@internationalizeDocstring
def whatis(self, irc, msg, args, channel, words):
"""[<channel>] <key> [<number>]
def whatis(self, irc, msg, args, channel, optlist, words):
"""[<channel>] [--raw] <key> [<number>]
Looks up the value of <key> in the factoid database. If given a
number, will return only that exact factoid. <channel> is only
necessary if the message isn't sent in the channel itself.
number, will return only that exact factoid. If '--raw' option is
given, no variable substitution will take place on the factoid.
<channel> is only necessary if the message isn't sent in the channel
itself.
"""
raw = False
for (option, arg) in optlist:
if option == 'raw':
raw = True
number = None
if len(words) > 1:
if words[-1].isdigit():
@ -222,9 +321,131 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler):
irc.errorInvalid(_('key id'))
key = ' '.join(words)
factoids = self._lookupFactoid(channel, key)
self._replyFactoids(irc, msg, key, factoids, number)
if factoids:
self._replyFactoids(irc, msg, key, channel, factoids, number, raw=raw)
else:
self._replyApproximateFactoids(irc, msg, channel, key)
whatis = wrap(whatis, ['channel', many('something')])
whatis = wrap(whatis, ['channel',
getopts({'raw': '',}),
many('something')])
@internationalizeDocstring
def alias(self, irc, msg, args, channel, oldkey, newkey, number):
"""[<channel>] <oldkey> <newkey> [<number>]
Adds a new key <newkey> for factoid associated with <oldkey>.
<number> is only necessary if there's more than one factoid associated
with <oldkey>.
The same action can be accomplished by using the 'learn' function with
a new key but an existing (verbatim) factoid content.
"""
def _getNewKey(channel, newkey, arelation):
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT id FROM keys WHERE key=?""", (newkey,))
newkey_info = cursor.fetchall()
if len(newkey_info) == 1:
# check if we already have the requested relation
cursor.execute("""SELECT id FROM relations WHERE
key_id=? and fact_id=?""",
(arelation[1], arelation[2]))
existentrelation = cursor.fetchall()
if len(existentrelation) != 0:
newkey_info = False
if len(newkey_info) == 0:
cursor.execute("""INSERT INTO keys VALUES (NULL, ?)""",
(newkey,))
db.commit()
cursor.execute("""SELECT id FROM keys WHERE key=?""", (newkey,))
newkey_info = cursor.fetchall()
return newkey_info
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT relations.id, relations.key_id, relations.fact_id
FROM keys, relations
WHERE keys.key=? AND
relations.key_id=keys.id""", (oldkey,))
results = cursor.fetchall()
if len(results) == 0:
irc.error(_('No factoid matches that key.'))
return
elif len(results) == 1:
newkey_info = _getNewKey(channel, newkey, results[0])
if newkey_info is not False:
cursor.execute("""INSERT INTO relations VALUES(NULL, ?, ?, ?)""",
(newkey_info[0][0], results[0][2], 0,))
irc.replySuccess()
else:
irc.error(_('This key-factoid relationship already exists.'))
elif len(results) > 1:
try:
arelation = results[number-1]
except IndexError:
irc.error(_("That's not a valid number for that key."))
return
except TypeError:
irc.error(_("This key has more than one factoid associated "
"with it, but you have not provided a number."))
return
newkey_info = _getNewKey(channel, newkey, arelation)
if newkey_info is not False:
cursor.execute("""INSERT INTO relations VALUES(NULL, ?, ?, ?)""",
(newkey_info[0][0], arelation[2], 0,))
irc.replySuccess()
else:
irc.error(_('This key-factoid relationship already exists.'))
alias = wrap(alias, ['channel', 'something', 'something', optional('int')])
@internationalizeDocstring
def rank(self, irc, msg, args, channel, optlist, number):
"""[<channel>] [--plain] [--alpha] [<number>]
Returns a list of top-ranked factoid keys, sorted by usage count
(rank). If <number> is not provided, the default number of factoid keys
returned is set by the rankListLength registry value.
If --plain option is given, rank numbers and usage counts are not
included in output.
If --alpha option is given in addition to --plain, keys are sorted
alphabetically, instead of by rank.
<channel> is only necessary if the message isn't sent in the channel
itself.
"""
if not number:
number = self.registryValue('rankListLength', channel)
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT keys.key, relations.usage_count
FROM keys, relations
WHERE relations.key_id=keys.id
ORDER BY relations.usage_count DESC
LIMIT ?""", (number,))
factkeys = cursor.fetchall()
plain=False
alpha=False
for (option, arg) in optlist:
if option == 'plain':
plain = True
elif option =='alpha':
alpha = True
if plain:
s = [ "%s" % (key[0],) for i, key in enumerate(factkeys) ]
if alpha:
s.sort()
else:
s = [ "#%d %s (%d)" % (i+1, key[0], key[1]) for i, key in enumerate(factkeys) ]
irc.reply(", ".join(s))
rank = wrap(rank, ['channel',
getopts({'plain': '', 'alpha': '',}),
optional('int')])
@internationalizeDocstring
def lock(self, irc, msg, args, channel, key):
"""[<channel>] <key>
@ -235,7 +456,10 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler):
"""
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("UPDATE keys SET locked=1 WHERE key LIKE %s", key)
cursor.execute("UPDATE factoids, keys, relations "
"SET factoids.locked=1 WHERE key LIKE ? AND "
"factoids.id=relations.fact_id AND "
"keys.id=relations.key_id", (key,))
db.commit()
irc.replySuccess()
lock = wrap(lock, ['channel', 'text'])
@ -250,19 +474,48 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler):
"""
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("UPDATE keys SET locked=0 WHERE key LIKE %s", key)
cursor.execute("""UPDATE factoids, keys, relations
SET factoids.locked=1 WHERE key LIKE ? AND
factoids.id=relations.fact_id AND
keys.id=relations.key_id""", (key,))
db.commit()
irc.replySuccess()
unlock = wrap(unlock, ['channel', 'text'])
def _deleteRelation(self, channel, relationlist):
db = self.getDb(channel)
cursor = db.cursor()
for (keyid, factid, relationid) in relationlist:
cursor.execute("""DELETE FROM relations where relations.id=?""",
(relationid,))
db.commit()
cursor.execute("""SELECT id FROM relations
WHERE relations.key_id=?""", (keyid,))
remaining_key_relations = cursor.fetchall()
if len(remaining_key_relations) == 0:
cursor.execute("""DELETE FROM keys where id=?""", (keyid,))
cursor.execute("""SELECT id FROM relations
WHERE relations.fact_id=?""", (factid,))
remaining_fact_relations = cursor.fetchall()
if len(remaining_fact_relations) == 0:
cursor.execute("""DELETE FROM factoids where id=?""", (factid,))
db.commit()
@internationalizeDocstring
def forget(self, irc, msg, args, channel, words):
"""[<channel>] <key> [<number>|*]
Removes the factoid <key> from the factoids database. If there are
more than one factoid with such a key, a number is necessary to
determine which one should be removed. A * can be used to remove all
factoids associated with a key. <channel> is only necessary if
Removes a key-fact relationship for key <key> from the factoids
database. If there is more than one such relationship for this key,
a number is necessary to determine which one should be removed.
A * can be used to remove all relationships for <key>.
If as a result, the key (factoid) remains without any relationships to
a factoid (key), it shall be removed from the database.
<channel> is only necessary if
the message isn't sent in the channel itself.
"""
number = None
@ -277,34 +530,32 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler):
key = ' '.join(words)
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT keys.id, factoids.id
FROM keys, factoids
WHERE key LIKE %s AND
factoids.key_id=keys.id""", key)
if cursor.rowcount == 0:
cursor.execute("""SELECT keys.id, factoids.id, relations.id
FROM keys, factoids, relations
WHERE key LIKE ? AND
relations.key_id=keys.id AND
relations.fact_id=factoids.id""", (key,))
results = cursor.fetchall()
if len(results) == 0:
irc.error(_('There is no such factoid.'))
elif cursor.rowcount == 1 or number is True:
(id, foo) = cursor.fetchone()
cursor.execute("""DELETE FROM factoids WHERE key_id=%s""", id)
cursor.execute("""DELETE FROM keys WHERE key LIKE %s""", key)
db.commit()
elif len(results) == 1 or number is True:
self._deleteRelation(channel, results)
irc.replySuccess()
else:
if number is not None:
results = cursor.fetchall()
#results = cursor.fetchall()
try:
(foo, id) = results[number-1]
arelation = results[number-1]
except IndexError:
irc.error(_('Invalid factoid number.'))
return
cursor.execute("DELETE FROM factoids WHERE id=%s", id)
db.commit()
self._deleteRelation(channel, [arelation,])
irc.replySuccess()
else:
irc.error(_('%s factoids have that key. '
'Please specify which one to remove, '
'or use * to designate all of them.') %
cursor.rowcount)
len(results))
forget = wrap(forget, ['channel', many('something')])
@internationalizeDocstring
@ -316,14 +567,18 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler):
"""
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT fact, key_id FROM factoids
cursor.execute("""SELECT id, key_id, fact_id FROM relations
ORDER BY random()
LIMIT 3""")
if cursor.rowcount != 0:
results = cursor.fetchall()
if len(results) != 0:
L = []
for (factoid, id) in cursor.fetchall():
cursor.execute("""SELECT key FROM keys WHERE id=%s""", id)
(key,) = cursor.fetchone()
for (relationid, keyid, factid) in results:
cursor.execute("""SELECT keys.key, factoids.fact
FROM keys, factoids
WHERE factoids.id=? AND
keys.id=?""", (factid,keyid,))
(key,factoid) = cursor.fetchall()[0]
L.append('"%s": %s' % (ircutils.bold(key), factoid))
irc.reply('; '.join(L))
else:
@ -340,23 +595,28 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler):
"""
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("SELECT id, locked FROM keys WHERE key LIKE %s", key)
if cursor.rowcount == 0:
cursor.execute("SELECT id FROM keys WHERE key LIKE ?", (key,))
results = cursor.fetchall()
if len(results) == 0:
irc.error(_('No factoid matches that key.'))
return
(id, locked) = map(int, cursor.fetchone())
cursor.execute("""SELECT added_by, added_at FROM factoids
WHERE key_id=%s
ORDER BY id""", id)
id = results[0][0]
cursor.execute("""SELECT factoids.added_by, factoids.added_at, factoids.locked, relations.usage_count
FROM factoids, relations
WHERE relations.key_id=? AND
relations.fact_id=factoids.id
ORDER BY relations.id""", (id,))
factoids = cursor.fetchall()
L = []
counter = 0
for (added_by, added_at) in factoids:
for (added_by, added_at, locked, usage_count) in factoids:
counter += 1
added_at = time.strftime(conf.supybot.reply.format.time(),
time.localtime(int(added_at)))
L.append(format(_('#%i was added by %s at %s'),
counter, added_by, added_at))
L.append(format(_('#%i was added by %s at %s, and has been '
'recalled %n'),
counter, added_by, added_at,
(usage_count, _('time'))))
factoids = '; '.join(L)
s = format('Key %q is %s and has %n associated with it: %s',
key, locked and 'locked' or 'not locked',
@ -374,17 +634,19 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler):
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT factoids.id, factoids.fact
FROM keys, factoids
WHERE keys.key LIKE %s AND
keys.id=factoids.key_id""", key)
if cursor.rowcount == 0:
FROM keys, factoids, relations
WHERE keys.key LIKE ? AND
keys.id=relations.key_id AND
factoids.id=relations.fact_id""", (key,))
results = cursor.fetchall()
if len(results) == 0:
irc.error(format(_('I couldn\'t find any key %q'), key))
return
elif cursor.rowcount < number:
irc.errorInvalid(_('key id'))
(id, fact) = cursor.fetchall()[number-1]
elif len(results) < number:
irc.errorInvalid('key id')
(id, fact) = results[number-1]
newfact = replacer(fact)
cursor.execute("UPDATE factoids SET fact=%s WHERE id=%s", newfact, id)
cursor.execute("UPDATE factoids SET fact=? WHERE id=?", (newfact, id))
db.commit()
irc.replySuccess()
change = wrap(change, ['channel', 'something',
@ -412,7 +674,8 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler):
target = 'factoids.fact'
if 'factoids' not in tables:
tables.append('factoids')
criteria.append('factoids.key_id=keys.id')
tables.append('relations')
criteria.append('factoids.id=relations.fact_id AND keys.id=relations.key_id')
elif option == 'regexp':
criteria.append('%s(TARGET)' % predicateName)
def p(s, r=arg):
@ -420,11 +683,12 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler):
db.create_function(predicateName, 1, p)
predicateName += 'p'
for glob in globs:
criteria.append('TARGET LIKE %s')
criteria.append('TARGET LIKE ?')
formats.append(glob.translate(self._sqlTrans))
cursor = db.cursor()
sql = """SELECT keys.key FROM %s WHERE %s""" % \
(', '.join(tables), ' AND '.join(criteria))
sql = sql + " ORDER BY keys.key"
sql = sql.replace('TARGET', target)
cursor.execute(sql, formats)
if cursor.rowcount == 0:
@ -435,8 +699,17 @@ class Factoids(callbacks.Plugin, plugins.ChannelDBHandler):
elif cursor.rowcount > 100:
irc.reply(_('More than 100 keys matched that query; '
'please narrow your query.'))
results = cursor.fetchall()
if len(results) == 0:
irc.reply(_('No keys matched that query.'))
elif len(results) == 1 and \
self.registryValue('showFactoidIfOnlyOneMatch', channel):
self.whatis(irc, msg, [channel, results[0][0]])
elif len(results) > 100:
irc.reply(_('More than 100 keys matched that query; '
'please narrow your query.'))
else:
keys = [repr(t[0]) for t in cursor.fetchall()]
keys = [repr(t[0]) for t in results]
s = format('%L', keys)
irc.reply(s)
search = wrap(search, ['channel',

View File

@ -31,137 +31,179 @@
from supybot.test import *
try:
import sqlite
import sqlite3
except ImportError:
sqlite = None
from pysqlite2 import dbapi2 as sqlite3 # for python2.4
if sqlite:
class FactoidsTestCase(ChannelPluginTestCase):
plugins = ('Factoids',)
def testRandomfactoid(self):
self.assertError('random')
self.assertNotError('learn jemfinch as my primary author')
self.assertRegexp('random', 'primary author')
class FactoidsTestCase(ChannelPluginTestCase):
plugins = ('Factoids',)
def testRandomfactoid(self):
self.assertError('random')
self.assertNotError('learn jemfinch as my primary author')
self.assertRegexp('random', 'primary author')
def testLearn(self):
self.assertError('learn as my primary author')
self.assertError('learn jemfinch as')
self.assertNotError('learn jemfinch as my primary author')
self.assertNotError('info jemfinch')
self.assertRegexp('whatis jemfinch', 'my primary author')
self.assertRegexp('whatis JEMFINCH', 'my primary author')
self.assertRegexp('whatis JEMFINCH 1', 'my primary author')
self.assertNotError('learn jemfinch as a bad assembly programmer')
self.assertRegexp('whatis jemfinch 2', 'bad assembly')
self.assertNotRegexp('whatis jemfinch 2', 'primary author')
self.assertRegexp('whatis jemfinch', r'.*primary author.*assembly')
self.assertError('forget jemfinch')
self.assertError('forget jemfinch 3')
self.assertError('forget jemfinch 0')
self.assertNotError('forget jemfinch 2')
self.assertNotError('forget jemfinch 1')
self.assertError('whatis jemfinch')
self.assertError('info jemfinch')
def testLearn(self):
self.assertError('learn as my primary author')
self.assertError('learn jemfinch as')
self.assertNotError('learn jemfinch as my primary author')
self.assertNotError('info jemfinch')
self.assertRegexp('whatis jemfinch', 'my primary author')
self.assertRegexp('whatis JEMFINCH', 'my primary author')
self.assertRegexp('whatis JEMFINCH 1', 'my primary author')
self.assertNotError('learn jemfinch as a bad assembly programmer')
self.assertRegexp('whatis jemfinch 2', 'bad assembly')
self.assertNotRegexp('whatis jemfinch 2', 'primary author')
self.assertRegexp('whatis jemfinch', r'.*primary author.*assembly')
self.assertError('forget jemfinch')
self.assertError('forget jemfinch 3')
self.assertError('forget jemfinch 0')
self.assertNotError('forget jemfinch 2')
self.assertNotError('forget jemfinch 1')
self.assertError('whatis jemfinch')
self.assertError('info jemfinch')
self.assertNotError('learn foo bar as baz')
self.assertNotError('info foo bar')
self.assertRegexp('whatis foo bar', 'baz')
self.assertNotError('learn foo bar as quux')
self.assertRegexp('whatis foo bar', '.*baz.*quux')
self.assertError('forget foo bar')
self.assertNotError('forget foo bar 2')
self.assertNotError('forget foo bar 1')
self.assertError('whatis foo bar')
self.assertError('info foo bar')
self.assertNotError('learn foo bar as baz')
self.assertNotError('info foo bar')
self.assertRegexp('whatis foo bar', 'baz')
self.assertNotError('learn foo bar as quux')
self.assertRegexp('whatis foo bar', '.*baz.*quux')
self.assertError('forget foo bar')
self.assertNotError('forget foo bar 2')
self.assertNotError('forget foo bar 1')
self.assertError('whatis foo bar')
self.assertError('info foo bar')
self.assertError('learn foo bar baz') # No 'as'
self.assertError('learn foo bar') # No 'as'
self.assertError('learn foo bar baz') # No 'as'
self.assertError('learn foo bar') # No 'as'
def testChangeFactoid(self):
self.assertNotError('learn foo as bar')
self.assertNotError('change foo 1 s/bar/baz/')
self.assertRegexp('whatis foo', 'baz')
self.assertError('change foo 2 s/bar/baz/')
self.assertError('change foo 0 s/bar/baz/')
def testChangeFactoid(self):
self.assertNotError('learn foo as bar')
self.assertNotError('change foo 1 s/bar/baz/')
self.assertRegexp('whatis foo', 'baz')
self.assertError('change foo 2 s/bar/baz/')
self.assertError('change foo 0 s/bar/baz/')
def testSearchFactoids(self):
self.assertNotError('learn jemfinch as my primary author')
self.assertNotError('learn strike as a cool guy working on me')
self.assertNotError('learn inkedmn as another of my developers')
self.assertNotError('learn jamessan as a developer of much python')
self.assertNotError('learn bwp as author of my weather command')
self.assertRegexp('factoids search --regexp /.w./', 'bwp')
self.assertRegexp('factoids search --regexp /^.+i/',
'jemfinch.*strike')
self.assertNotRegexp('factoids search --regexp /^.+i/', 'inkedmn')
self.assertRegexp('factoids search --regexp m/j/ --regexp m/ss/',
'jamessan')
self.assertRegexp('factoids search --regexp m/^j/ *ss*',
'jamessan')
self.assertRegexp('factoids search --regexp /^j/',
'jemfinch.*jamessan')
self.assertRegexp('factoids search j*', 'jemfinch.*jamessan')
self.assertRegexp('factoids search *ke*',
'inkedmn.*strike|strike.*inkedmn')
self.assertRegexp('factoids search ke',
'inkedmn.*strike|strike.*inkedmn')
self.assertRegexp('factoids search jemfinch',
'my primary author')
self.assertRegexp('factoids search --values primary author',
'my primary author')
def testSearchFactoids(self):
self.assertNotError('learn jemfinch as my primary author')
self.assertNotError('learn strike as a cool guy working on me')
self.assertNotError('learn inkedmn as another of my developers')
self.assertNotError('learn jamessan as a developer of much python')
self.assertNotError('learn bwp as author of my weather command')
self.assertRegexp('factoids search --regexp /.w./', 'bwp')
self.assertRegexp('factoids search --regexp /^.+i/',
'jemfinch.*strike')
self.assertNotRegexp('factoids search --regexp /^.+i/', 'inkedmn')
self.assertRegexp('factoids search --regexp m/j/ --regexp m/ss/',
'jamessan')
self.assertRegexp('factoids search --regexp m/^j/ *ss*',
'jamessan')
self.assertRegexp('factoids search --regexp /^j/',
'jamessan.*jemfinch')
self.assertRegexp('factoids search j*', 'jamessan.*jemfinch')
self.assertRegexp('factoids search *ke*',
'inkedmn.*strike|strike.*inkedmn')
self.assertRegexp('factoids search ke',
'inkedmn.*strike|strike.*inkedmn')
self.assertRegexp('factoids search jemfinch',
'my primary author')
self.assertRegexp('factoids search --values primary author',
'my primary author')
def testWhatisOnNumbers(self):
self.assertNotError('learn 911 as emergency number')
self.assertRegexp('whatis 911', 'emergency number')
def testWhatisOnNumbers(self):
self.assertNotError('learn 911 as emergency number')
self.assertRegexp('whatis 911', 'emergency number')
def testNotZeroIndexed(self):
self.assertNotError('learn foo as bar')
self.assertNotRegexp('info foo', '#0')
self.assertNotRegexp('whatis foo', '#0')
self.assertNotError('learn foo as baz')
self.assertNotRegexp('info foo', '#0')
self.assertNotRegexp('whatis foo', '#0')
def testNotZeroIndexed(self):
self.assertNotError('learn foo as bar')
self.assertNotRegexp('info foo', '#0')
self.assertNotRegexp('whatis foo', '#0')
self.assertNotError('learn foo as baz')
self.assertNotRegexp('info foo', '#0')
self.assertNotRegexp('whatis foo', '#0')
def testInfoReturnsRightNumber(self):
self.assertNotError('learn foo as bar')
self.assertNotRegexp('info foo', '2 factoids')
def testInfoReturnsRightNumber(self):
self.assertNotError('learn foo as bar')
self.assertNotRegexp('info foo', '2 factoids')
def testLearnSeparator(self):
self.assertError('learn foo is bar')
self.assertNotError('learn foo as bar')
self.assertRegexp('whatis foo', 'bar')
orig = conf.supybot.plugins.Factoids.learnSeparator()
try:
conf.supybot.plugins.Factoids.learnSeparator.setValue('is')
self.assertError('learn bar as baz')
self.assertNotError('learn bar is baz')
self.assertRegexp('whatis bar', 'baz')
finally:
conf.supybot.plugins.Factoids.learnSeparator.setValue(orig)
def testInfoUsageCount(self):
self.assertNotError('learn moo as cow')
self.assertRegexp('info moo', 'recalled 0 times')
self.assertNotError('whatis moo')
self.assertRegexp('info moo', 'recalled 1 time')
def testShowFactoidIfOnlyOneMatch(self):
m1 = self.assertNotError('factoids search m/foo|bar/')
orig = conf.supybot.plugins.Factoids.showFactoidIfOnlyOneMatch()
try:
conf.supybot.plugins.Factoids. \
showFactoidIfOnlyOneMatch.setValue(False)
m2 = self.assertNotError('factoids search m/foo/')
self.failUnless(m1.args[1].startswith(m2.args[1]))
finally:
conf.supybot.plugins.Factoids. \
showFactoidIfOnlyOneMatch.setValue(orig)
def testLearnSeparator(self):
self.assertError('learn foo is bar')
self.assertNotError('learn foo as bar')
self.assertRegexp('whatis foo', 'bar')
orig = conf.supybot.plugins.Factoids.learnSeparator()
try:
conf.supybot.plugins.Factoids.learnSeparator.setValue('is')
self.assertError('learn bar as baz')
self.assertNotError('learn bar is baz')
self.assertRegexp('whatis bar', 'baz')
finally:
conf.supybot.plugins.Factoids.learnSeparator.setValue(orig)
def testInvalidCommand(self):
orig = conf.supybot.plugins.Factoids.replyWhenInvalidCommand()
try:
conf.supybot.plugins.Factoids.\
replyWhenInvalidCommand.setValue(True)
self.assertNotError('learn foo as bar')
self.assertRegexp('foo', 'bar')
finally:
conf.supybot.plugins.Factoids.\
replyWhenInvalidCommand.setValue(orig)
def testShowFactoidIfOnlyOneMatch(self):
m1 = self.assertNotError('factoids search m/foo|bar/')
orig = conf.supybot.plugins.Factoids.showFactoidIfOnlyOneMatch()
try:
conf.supybot.plugins.Factoids. \
showFactoidIfOnlyOneMatch.setValue(False)
m2 = self.assertNotError('factoids search m/foo/')
self.failUnless(m1.args[1].startswith(m2.args[1]))
finally:
conf.supybot.plugins.Factoids. \
showFactoidIfOnlyOneMatch.setValue(orig)
def testInvalidCommand(self):
self.assertNotError('learn foo as bar')
self.assertRegexp('foo', 'bar')
self.assertNotError('learn mooz as cowz')
self.assertRegexp('moo', 'mooz')
self.assertRegexp('mzo', 'mooz')
self.assertRegexp('moz', 'mooz')
self.assertNotError('learn moped as pretty fast')
self.assertRegexp('moe', 'mooz.*moped')
self.assertError('nosuchthing')
def testWhatis(self):
self.assertNotError('learn foo as bar')
self.assertRegexp('whatis foo', 'bar')
self.assertRegexp('whatis foob', 'foo')
self.assertNotError('learn foob as barb')
self.assertRegexp('whatis foom', 'foo.*foob')
def testStandardSubstitute(self):
self.assertNotError('learn foo as this is $channel, and hour is $hour')
self.assertRegexp('whatis foo', 'this is #test, and hour is \d{1,2}')
self.assertRegexp('whatis --raw foo', 'this is \$channel, and hour is \$hour')
self.assertNotError('learn bar as this is $$channel escaped')
self.assertRegexp('whatis bar', 'this is \$channel')
self.assertNotError('learn bar as this is $minute')
self.assertRegexp('whatis bar', '\$channel.*\d{1,2}')
def testAlias(self):
self.assertNotError('learn foo as bar')
self.assertNotError('alias foo zoog')
self.assertRegexp('whatis zoog', 'bar')
self.assertNotError('learn foo as snorp')
self.assertError('alias foo gnoop')
self.assertNotError('alias foo gnoop 2')
self.assertRegexp('whatis gnoop', 'snorp')
def testRank(self):
self.assertNotError('learn foo as bar')
self.assertNotError('learn moo as cow')
self.assertRegexp('factoids rank', '#1 foo \(0\), #2 moo \(0\)')
self.assertRegexp('whatis moo', '.*cow.*')
self.assertRegexp('factoids rank', '#1 moo \(1\), #2 foo \(0\)')
self.assertRegexp('factoids rank 1', '#1 moo \(1\)')
self.assertNotRegexp('factoids rank 1', 'foo')
self.assertRegexp('factoids rank --plain', 'moo, foo')
self.assertRegexp('factoids rank --plain --alpha', 'foo, moo')
self.assertResponse('factoids rank --plain 1', 'moo')
def testQuoteHandling(self):
self.assertNotError('learn foo as "\\"bar\\""')
self.assertRegexp('whatis foo', r'"bar"')

View File

@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: Supybot-fr\n"
"POT-Creation-Date: 2010-10-17 11:48+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\n"
"PO-Revision-Date: \n"
"Last-Translator: Valentin Lorentz <progval@gmail.com>\n"
"Language-Team: Supybot-fr <progval@gmail.com>\n"
@ -37,7 +37,7 @@ msgid ""
" in a word before it will be shrunken by the shrink command/filter."
msgstr "Détermine le nombre minimum de lettre dans un mot pour qu'il soit coupé par la commande/le filtre shrink."
#: plugin.py:51
#: plugin.py:50
msgid ""
"This plugin offers several commands which transform text in some way.\n"
" It also provides the capability of using such commands to 'filter' the\n"
@ -46,7 +46,7 @@ msgid ""
" Not very useful, but definitely quite fun :)"
msgstr "Ce plugin offre quelques commandes qui peuvent être utilisées pour transformer du texte de différentes façons. Il fourni également la possiblité d'utiliser ces commandes pour 'filtrer' la sortie du bot ; par exemple, vous pouvez faire en sorte que tout ce que le bot dit le soit en l33tsp34k, en Morse, ou n'importe lequel des autres filtres. Pas très utile, mais plutôt fun :)"
#: plugin.py:85
#: plugin.py:84
msgid ""
"[<channel>] [<command>]\n"
"\n"
@ -59,11 +59,11 @@ msgstr ""
"\n"
"Définit le filtre de sortie de ce plugin pour être <commande>. Si aucune commande n'est définie, supprime le filtre de sortie. <canal> n'est nécessaire que si la commande n'est pas envoyée sur le canal lui-même."
#: plugin.py:98
#: plugin.py:97
msgid "That's not a valid filter command."
msgstr "Ce n'est pas une commande de filtre valide"
#: plugin.py:108
#: plugin.py:107
msgid ""
"<text>\n"
"\n"
@ -76,7 +76,7 @@ msgstr ""
"\n"
"Retire toutes les voyelles du <texte> (si vous êtes curieux de pourquoi elle s'appelle 'hebrew', c'est parce que je (jemfinch) pense que que en Hébreux, il manque souvent les voyelles)."
#: plugin.py:120
#: plugin.py:119
msgid ""
"<text>\n"
"\n"
@ -87,7 +87,7 @@ msgstr ""
"\n"
"Supprime tous les espaces du <texte>."
#: plugin.py:130
#: plugin.py:129
msgid ""
"<text>\n"
"\n"
@ -98,7 +98,7 @@ msgstr ""
"\n"
"Renvoie le texte, avec toutes les lettres consécutives dupliquées supprimées."
#: plugin.py:143
#: plugin.py:142
msgid ""
"<text>\n"
"\n"
@ -109,7 +109,19 @@ msgstr ""
"\n"
"Retourne la représentation binaire du <texte>."
#: plugin.py:169
#: plugin.py:168
msgid ""
"<text>\n"
"\n"
" Returns the character representation of binary <text>.\n"
" Assumes ASCII, 8 digits per character.\n"
" "
msgstr ""
"<texte>\n"
"\n"
"Retourne la représentation binaire du <texte>. Considère qu'il s'agit d'ASCII, 8 bits par caractère."
#: plugin.py:179
msgid ""
"<text>\n"
"\n"
@ -121,7 +133,7 @@ msgstr ""
"\n"
"Retourne une chaîne héxadécimale à partir de la chaîne donnée ; une chaîne héxadécimale est une chaîne composée de la valeur héxadécimale de chaque caractère de la chaîne."
#: plugin.py:179
#: plugin.py:189
msgid ""
"<hexstring>\n"
"\n"
@ -133,11 +145,11 @@ msgstr ""
"\n"
"Retourne la chaîne correspondant à la <chaîne hexadécimale>. Bien sûr, <chaîne hexadécimale> ne doit contenir que des caractères hexadécimaux."
#: plugin.py:187
#: plugin.py:197
msgid "Invalid input."
msgstr "Entrée invalide."
#: plugin.py:192
#: plugin.py:202
msgid ""
"<text>\n"
"\n"
@ -150,7 +162,7 @@ msgstr ""
"\n"
"Déplace chaque caractère du <texte> de 13 places vers la droite de l'alphabet. Rot13 est courremment utilisé pour les textes qui doivent être cachés des yeux indiscrets, mais être facilement reversible."
#: plugin.py:203
#: plugin.py:213
msgid ""
"<text>\n"
"\n"
@ -161,7 +173,7 @@ msgstr ""
"\n"
"Retourne la version zézéyée du texte."
#: plugin.py:234
#: plugin.py:244
msgid ""
"<text>\n"
"\n"
@ -172,7 +184,7 @@ msgstr ""
"\n"
"Retourne la version l33t du <texte>."
#: plugin.py:254
#: plugin.py:264
msgid ""
"<text>\n"
"\n"
@ -183,7 +195,7 @@ msgstr ""
"\n"
"Répond avec une traduction k-rad du <texte>."
#: plugin.py:270
#: plugin.py:280
msgid ""
"<text>\n"
"\n"
@ -195,7 +207,7 @@ msgstr ""
"\n"
"Répond avec une chaîne où chaque mot est mélangé ; c'est à dire que chaque lettre interne (=toute lettre qui n'est pas la première ni la dernière) est mélangée avec les autres."
#: plugin.py:335
#: plugin.py:345
msgid ""
"<Morse code text>\n"
"\n"
@ -206,7 +218,7 @@ msgstr ""
"\n"
"Fait l'inverse de la commande morse."
#: plugin.py:352
#: plugin.py:362
msgid ""
"<text>\n"
"\n"
@ -217,7 +229,7 @@ msgstr ""
"\n"
"Donne le code Morse équivalent à la chaîne donnée."
#: plugin.py:364
#: plugin.py:374
msgid ""
"<text>\n"
"\n"
@ -228,7 +240,7 @@ msgstr ""
"\n"
"Inverse le <texte>."
#: plugin.py:381
#: plugin.py:391
msgid ""
"<text>\n"
"\n"
@ -239,7 +251,7 @@ msgstr ""
"\n"
"Retourne le <texte> avec chaque caractère coloré de façon aléatoire."
#: plugin.py:391
#: plugin.py:401
msgid ""
"<text>\n"
"\n"
@ -250,7 +262,7 @@ msgstr ""
"\n"
"Retourne le texte colorisé comme un arc-en-ciel."
#: plugin.py:402
#: plugin.py:412
msgid ""
"<text>\n"
"\n"
@ -261,7 +273,7 @@ msgstr ""
"\n"
"Retourne le texte en retirant tous les codes de couleur"
#: plugin.py:411
#: plugin.py:421
msgid ""
"<text>\n"
"\n"
@ -272,7 +284,7 @@ msgstr ""
"\n"
"Retourne le <texte> comme si un AOLuser l'avait dit."
#: plugin.py:438
#: plugin.py:448
msgid ""
"<text>\n"
"\n"
@ -283,279 +295,279 @@ msgstr ""
"\n"
"Retourne le <texte> comme si JeffK l'avait dit lui-même."
#: plugin.py:534
#: plugin.py:544
msgid "ay"
msgstr "ah"
#: plugin.py:534
#: plugin.py:544
msgid "bee"
msgstr "bé"
#: plugin.py:534
#: plugin.py:544
msgid "dee"
msgstr "dé"
#: plugin.py:534
#: plugin.py:544
msgid "see"
msgstr "cé"
#: plugin.py:535
#: plugin.py:545
msgid "aych"
msgstr "ache"
#: plugin.py:535
#: plugin.py:545
msgid "ee"
msgstr "euh"
#: plugin.py:535
#: plugin.py:545
msgid "eff"
msgstr "èf"
#: plugin.py:535
#: plugin.py:545
msgid "gee"
msgstr "gé"
#: plugin.py:536
#: plugin.py:546
msgid "ell"
msgstr "èl"
#: plugin.py:536
#: plugin.py:546
msgid "eye"
msgstr "ih"
#: plugin.py:536
#: plugin.py:546
msgid "jay"
msgstr "ji"
#: plugin.py:536
#: plugin.py:546
msgid "kay"
msgstr "ka"
#: plugin.py:537
#: plugin.py:547
msgid "cue"
msgstr "cu"
#: plugin.py:537
#: plugin.py:547
msgid "em"
msgstr "èm"
#: plugin.py:537
#: plugin.py:547
msgid "en"
msgstr "èn"
#: plugin.py:537
#: plugin.py:547
msgid "oh"
msgstr "oh"
#: plugin.py:537
#: plugin.py:547
msgid "pee"
msgstr "pé"
#: plugin.py:538
#: plugin.py:548
msgid "arr"
msgstr "ère"
#: plugin.py:538
#: plugin.py:548
msgid "ess"
msgstr "èce"
#: plugin.py:538
#: plugin.py:548
msgid "tee"
msgstr "té"
#: plugin.py:538
#: plugin.py:548
msgid "you"
msgstr "uh"
#: plugin.py:539
#: plugin.py:549
msgid "double-you"
msgstr "double-vé"
#: plugin.py:539
#: plugin.py:549
msgid "ecks"
msgstr "icks"
#: plugin.py:539
#: plugin.py:549
msgid "vee"
msgstr "vé"
#: plugin.py:539
#: plugin.py:549
msgid "why"
msgstr "i-grec"
#: plugin.py:540
#: plugin.py:550
msgid "zee"
msgstr "zèd"
#: plugin.py:545
#: plugin.py:555
msgid "exclamation point"
msgstr "point d'exclamation"
#: plugin.py:546
#: plugin.py:556
msgid "quote"
msgstr "guillemet double"
#: plugin.py:547
#: plugin.py:557
msgid "pound"
msgstr "livre"
#: plugin.py:548
#: plugin.py:558
msgid "dollar sign"
msgstr "signe du dollar"
#: plugin.py:549
#: plugin.py:559
msgid "percent"
msgstr "pourcent"
#: plugin.py:550
#: plugin.py:560
msgid "ampersand"
msgstr "espèrluette"
#: plugin.py:551
#: plugin.py:561
msgid "single quote"
msgstr "guillemet"
#: plugin.py:552
#: plugin.py:562
msgid "left paren"
msgstr "parenthèse ouvrante"
#: plugin.py:553
#: plugin.py:563
msgid "right paren"
msgstr "parenthèse fermante"
#: plugin.py:554
#: plugin.py:564
msgid "asterisk"
msgstr "asterisque"
#: plugin.py:555
#: plugin.py:565
msgid "plus"
msgstr "plus"
#: plugin.py:556
#: plugin.py:566
msgid "comma"
msgstr "virgule"
#: plugin.py:557
#: plugin.py:567
msgid "minus"
msgstr "moins"
#: plugin.py:558
#: plugin.py:568
msgid "period"
msgstr "point"
#: plugin.py:559
#: plugin.py:569
msgid "slash"
msgstr "slash"
#: plugin.py:560
#: plugin.py:570
msgid "colon"
msgstr "double-point"
#: plugin.py:561
#: plugin.py:571
msgid "semicolon"
msgstr "point-virgule"
#: plugin.py:562
#: plugin.py:572
msgid "less than"
msgstr "inférieur"
#: plugin.py:563
#: plugin.py:573
msgid "equals"
msgstr "moins que"
#: plugin.py:564
#: plugin.py:574
msgid "greater than"
msgstr "supérieur"
#: plugin.py:565
#: plugin.py:575
msgid "question mark"
msgstr "point d'exclamation"
#: plugin.py:566
#: plugin.py:576
msgid "at"
msgstr "arobase"
#: plugin.py:567
#: plugin.py:577
msgid "left bracket"
msgstr "crochet ouvrant"
#: plugin.py:568
#: plugin.py:578
msgid "backslash"
msgstr "anti-slash"
#: plugin.py:569
#: plugin.py:579
msgid "right bracket"
msgstr "crochet fermant"
#: plugin.py:570
#: plugin.py:580
msgid "caret"
msgstr "accent circonflexe"
#: plugin.py:571
#: plugin.py:581
msgid "underscore"
msgstr "underscore"
#: plugin.py:572
#: plugin.py:582
msgid "backtick"
msgstr "accent grave"
#: plugin.py:573
#: plugin.py:583
msgid "left brace"
msgstr "crochet ouvrant"
#: plugin.py:574
#: plugin.py:584
msgid "pipe"
msgstr "pipe"
#: plugin.py:575
#: plugin.py:585
msgid "right brace"
msgstr "crochet fermant"
#: plugin.py:576
#: plugin.py:586
msgid "tilde"
msgstr "tilde"
#: plugin.py:579
#: plugin.py:589
msgid "one"
msgstr "un"
#: plugin.py:579
#: plugin.py:589
msgid "three"
msgstr "trois"
#: plugin.py:579
#: plugin.py:589
msgid "two"
msgstr "deux"
#: plugin.py:579
#: plugin.py:589
msgid "zero"
msgstr "zéro"
#: plugin.py:580
#: plugin.py:590
msgid "five"
msgstr "cinq"
#: plugin.py:580
#: plugin.py:590
msgid "four"
msgstr "quatre"
#: plugin.py:580
#: plugin.py:590
msgid "seven"
msgstr "sept"
#: plugin.py:580
#: plugin.py:590
msgid "six"
msgstr "six"
#: plugin.py:581
#: plugin.py:591
msgid "eight"
msgstr "huit"
#: plugin.py:581
#: plugin.py:591
msgid "nine"
msgstr "neuf"
#: plugin.py:585
#: plugin.py:595
msgid ""
"<text>\n"
"\n"
@ -566,7 +578,7 @@ msgstr ""
"\n"
"Retourne le <texte>, épellé phonétiquement"
#: plugin.py:615
#: plugin.py:625
msgid ""
"<text>\n"
"\n"
@ -577,7 +589,7 @@ msgstr ""
"\n"
"Retourne le <texte> comme si GNU/RMS l'avait dite."
#: plugin.py:624
#: plugin.py:634
msgid ""
"<text>\n"
"\n"
@ -590,7 +602,7 @@ msgstr ""
"\n"
"Retourne le texte avec chaque mot plus long que supybot.plugins.Filter.shrink.minimum découpé (par exemple, \"internationalization\" devient i18n)"
#: plugin.py:643
#: plugin.py:653
msgid ""
"<text>\n"
"\n"
@ -601,7 +613,7 @@ msgstr ""
"\n"
"Retourne le <texte> avec les I transormés en r et les r transformés en I."
#: plugin.py:692
#: plugin.py:702
msgid ""
"<text>\n"
"\n"

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 11:48+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -39,7 +39,7 @@ msgid ""
" in a word before it will be shrunken by the shrink command/filter."
msgstr ""
#: plugin.py:51
#: plugin.py:50
#, docstring
msgid ""
"This plugin offers several commands which transform text in some way.\n"
@ -49,7 +49,7 @@ msgid ""
" Not very useful, but definitely quite fun :)"
msgstr ""
#: plugin.py:85
#: plugin.py:84
#, docstring
msgid ""
"[<channel>] [<command>]\n"
@ -60,11 +60,11 @@ msgid ""
" "
msgstr ""
#: plugin.py:98
#: plugin.py:97
msgid "That's not a valid filter command."
msgstr ""
#: plugin.py:108
#: plugin.py:107
#, docstring
msgid ""
"<text>\n"
@ -75,7 +75,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:120
#: plugin.py:119
#, docstring
msgid ""
"<text>\n"
@ -84,7 +84,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:130
#: plugin.py:129
#, docstring
msgid ""
"<text>\n"
@ -93,7 +93,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:143
#: plugin.py:142
#, docstring
msgid ""
"<text>\n"
@ -102,7 +102,17 @@ msgid ""
" "
msgstr ""
#: plugin.py:169
#: plugin.py:168
#, docstring
msgid ""
"<text>\n"
"\n"
" Returns the character representation of binary <text>.\n"
" Assumes ASCII, 8 digits per character.\n"
" "
msgstr ""
#: plugin.py:179
#, docstring
msgid ""
"<text>\n"
@ -112,7 +122,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:179
#: plugin.py:189
#, docstring
msgid ""
"<hexstring>\n"
@ -122,11 +132,11 @@ msgid ""
" "
msgstr ""
#: plugin.py:187
#: plugin.py:197
msgid "Invalid input."
msgstr ""
#: plugin.py:192
#: plugin.py:202
#, docstring
msgid ""
"<text>\n"
@ -137,7 +147,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:203
#: plugin.py:213
#, docstring
msgid ""
"<text>\n"
@ -146,7 +156,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:234
#: plugin.py:244
#, docstring
msgid ""
"<text>\n"
@ -155,7 +165,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:254
#: plugin.py:264
#, docstring
msgid ""
"<text>\n"
@ -164,7 +174,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:270
#: plugin.py:280
#, docstring
msgid ""
"<text>\n"
@ -174,7 +184,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:335
#: plugin.py:345
#, docstring
msgid ""
"<Morse code text>\n"
@ -183,7 +193,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:352
#: plugin.py:362
#, docstring
msgid ""
"<text>\n"
@ -192,7 +202,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:364
#: plugin.py:374
#, docstring
msgid ""
"<text>\n"
@ -201,7 +211,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:381
#: plugin.py:391
#, docstring
msgid ""
"<text>\n"
@ -210,7 +220,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:391
#: plugin.py:401
#, docstring
msgid ""
"<text>\n"
@ -219,7 +229,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:402
#: plugin.py:412
#, docstring
msgid ""
"<text>\n"
@ -228,7 +238,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:411
#: plugin.py:421
#, docstring
msgid ""
"<text>\n"
@ -237,7 +247,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:438
#: plugin.py:448
#, docstring
msgid ""
"<text>\n"
@ -246,279 +256,279 @@ msgid ""
" "
msgstr ""
#: plugin.py:534
#: plugin.py:544
msgid "ay"
msgstr ""
#: plugin.py:534
#: plugin.py:544
msgid "bee"
msgstr ""
#: plugin.py:534
#: plugin.py:544
msgid "dee"
msgstr ""
#: plugin.py:534
#: plugin.py:544
msgid "see"
msgstr ""
#: plugin.py:535
#: plugin.py:545
msgid "aych"
msgstr ""
#: plugin.py:535
#: plugin.py:545
msgid "ee"
msgstr ""
#: plugin.py:535
#: plugin.py:545
msgid "eff"
msgstr ""
#: plugin.py:535
#: plugin.py:545
msgid "gee"
msgstr ""
#: plugin.py:536
#: plugin.py:546
msgid "ell"
msgstr ""
#: plugin.py:536
#: plugin.py:546
msgid "eye"
msgstr ""
#: plugin.py:536
#: plugin.py:546
msgid "jay"
msgstr ""
#: plugin.py:536
#: plugin.py:546
msgid "kay"
msgstr ""
#: plugin.py:537
#: plugin.py:547
msgid "cue"
msgstr ""
#: plugin.py:537
#: plugin.py:547
msgid "em"
msgstr ""
#: plugin.py:537
#: plugin.py:547
msgid "en"
msgstr ""
#: plugin.py:537
#: plugin.py:547
msgid "oh"
msgstr ""
#: plugin.py:537
#: plugin.py:547
msgid "pee"
msgstr ""
#: plugin.py:538
#: plugin.py:548
msgid "arr"
msgstr ""
#: plugin.py:538
#: plugin.py:548
msgid "ess"
msgstr ""
#: plugin.py:538
#: plugin.py:548
msgid "tee"
msgstr ""
#: plugin.py:538
#: plugin.py:548
msgid "you"
msgstr ""
#: plugin.py:539
#: plugin.py:549
msgid "double-you"
msgstr ""
#: plugin.py:539
#: plugin.py:549
msgid "ecks"
msgstr ""
#: plugin.py:539
#: plugin.py:549
msgid "vee"
msgstr ""
#: plugin.py:539
#: plugin.py:549
msgid "why"
msgstr ""
#: plugin.py:540
#: plugin.py:550
msgid "zee"
msgstr ""
#: plugin.py:545
#: plugin.py:555
msgid "exclamation point"
msgstr ""
#: plugin.py:546
#: plugin.py:556
msgid "quote"
msgstr ""
#: plugin.py:547
#: plugin.py:557
msgid "pound"
msgstr ""
#: plugin.py:548
#: plugin.py:558
msgid "dollar sign"
msgstr ""
#: plugin.py:549
#: plugin.py:559
msgid "percent"
msgstr ""
#: plugin.py:550
#: plugin.py:560
msgid "ampersand"
msgstr ""
#: plugin.py:551
#: plugin.py:561
msgid "single quote"
msgstr ""
#: plugin.py:552
#: plugin.py:562
msgid "left paren"
msgstr ""
#: plugin.py:553
#: plugin.py:563
msgid "right paren"
msgstr ""
#: plugin.py:554
#: plugin.py:564
msgid "asterisk"
msgstr ""
#: plugin.py:555
#: plugin.py:565
msgid "plus"
msgstr ""
#: plugin.py:556
#: plugin.py:566
msgid "comma"
msgstr ""
#: plugin.py:557
#: plugin.py:567
msgid "minus"
msgstr ""
#: plugin.py:558
#: plugin.py:568
msgid "period"
msgstr ""
#: plugin.py:559
#: plugin.py:569
msgid "slash"
msgstr ""
#: plugin.py:560
#: plugin.py:570
msgid "colon"
msgstr ""
#: plugin.py:561
#: plugin.py:571
msgid "semicolon"
msgstr ""
#: plugin.py:562
#: plugin.py:572
msgid "less than"
msgstr ""
#: plugin.py:563
#: plugin.py:573
msgid "equals"
msgstr ""
#: plugin.py:564
#: plugin.py:574
msgid "greater than"
msgstr ""
#: plugin.py:565
#: plugin.py:575
msgid "question mark"
msgstr ""
#: plugin.py:566
#: plugin.py:576
msgid "at"
msgstr ""
#: plugin.py:567
#: plugin.py:577
msgid "left bracket"
msgstr ""
#: plugin.py:568
#: plugin.py:578
msgid "backslash"
msgstr ""
#: plugin.py:569
#: plugin.py:579
msgid "right bracket"
msgstr ""
#: plugin.py:570
#: plugin.py:580
msgid "caret"
msgstr ""
#: plugin.py:571
#: plugin.py:581
msgid "underscore"
msgstr ""
#: plugin.py:572
#: plugin.py:582
msgid "backtick"
msgstr ""
#: plugin.py:573
#: plugin.py:583
msgid "left brace"
msgstr ""
#: plugin.py:574
#: plugin.py:584
msgid "pipe"
msgstr ""
#: plugin.py:575
#: plugin.py:585
msgid "right brace"
msgstr ""
#: plugin.py:576
#: plugin.py:586
msgid "tilde"
msgstr ""
#: plugin.py:579
#: plugin.py:589
msgid "one"
msgstr ""
#: plugin.py:579
#: plugin.py:589
msgid "three"
msgstr ""
#: plugin.py:579
#: plugin.py:589
msgid "two"
msgstr ""
#: plugin.py:579
#: plugin.py:589
msgid "zero"
msgstr ""
#: plugin.py:580
#: plugin.py:590
msgid "five"
msgstr ""
#: plugin.py:580
#: plugin.py:590
msgid "four"
msgstr ""
#: plugin.py:580
#: plugin.py:590
msgid "seven"
msgstr ""
#: plugin.py:580
#: plugin.py:590
msgid "six"
msgstr ""
#: plugin.py:581
#: plugin.py:591
msgid "eight"
msgstr ""
#: plugin.py:581
#: plugin.py:591
msgid "nine"
msgstr ""
#: plugin.py:585
#: plugin.py:595
#, docstring
msgid ""
"<text>\n"
@ -527,7 +537,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:615
#: plugin.py:625
#, docstring
msgid ""
"<text>\n"
@ -536,7 +546,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:624
#: plugin.py:634
#, docstring
msgid ""
"<text>\n"
@ -547,7 +557,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:643
#: plugin.py:653
#, docstring
msgid ""
"<text>\n"
@ -556,7 +566,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:692
#: plugin.py:702
#, docstring
msgid ""
"<text>\n"

View File

@ -163,6 +163,17 @@ class Filter(callbacks.Plugin):
irc.reply(''.join(L))
binary = wrap(binary, ['text'])
@internationalizeDocstring
def unbinary(self, irc, msg, args, text):
"""<text>
Returns the character representation of binary <text>.
Assumes ASCII, 8 digits per character.
"""
L = [chr(int(text[i:(i+8)], 2)) for i in xrange(0, len(text), 8)]
irc.reply(''.join(L))
unbinary = wrap(unbinary, ['text'])
@internationalizeDocstring
def hexlify(self, irc, msg, args, text):
"""<text>

View File

@ -86,6 +86,9 @@ class FilterTest(ChannelPluginTestCase):
def testBinary(self):
self.assertResponse('binary A', '01000001')
def testUnbinary(self):
self.assertResponse('unbinary 011011010110111101101111', 'moo')
def testRot13(self):
for s in map(str, range(1000, 1010)):

View File

@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: Supybot-fr\n"
"POT-Creation-Date: 2010-10-17 12:46+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\n"
"PO-Revision-Date: \n"
"Last-Translator: Valentin Lorentz <progval@gmail.com>\n"
"Language-Team: Supybot-fr <progval@gmail.com>\n"
@ -88,6 +88,18 @@ msgstr "<caractères à remplacer> doit être de la même taille que <caractère
#: plugin.py:103
msgid ""
"<substring to translate> <substring to replace it with> <text>\n"
"\n"
" Replaces all non-overlapping occurrences of <substring to translate>\n"
" with <substring to replace it with> in <text>.\n"
" "
msgstr ""
"<sous-chaîne à remplacer> <sous-chaîne de remplacement> <texte>\n"
"\n"
"Replace toutes les occurences de <sous-chaîne à remplacer> (à condition qu'elles n'entrent pas en conflit) par les <sous-chaîne de remplacement> dans le <texte>."
#: plugin.py:112
msgid ""
"<text>\n"
"\n"
" Returns <text> uppercased.\n"
@ -97,7 +109,7 @@ msgstr ""
"\n"
"Retourne le texte, en majuscules"
#: plugin.py:112
#: plugin.py:121
msgid ""
"<text>\n"
"\n"
@ -108,7 +120,7 @@ msgstr ""
"\n"
"Retourne le texte, en minuscules"
#: plugin.py:121
#: plugin.py:130
msgid ""
"<text>\n"
"\n"
@ -119,7 +131,7 @@ msgstr ""
"\n"
"Retourne le texte, capitalisé"
#: plugin.py:130
#: plugin.py:139
msgid ""
"<text>\n"
"\n"
@ -130,7 +142,7 @@ msgstr ""
"\n"
"Retourne le texte, mis en majuscules de titre."
#: plugin.py:139
#: plugin.py:148
msgid ""
"<text>\n"
"\n"
@ -141,7 +153,7 @@ msgstr ""
"\n"
"Retourne le texte, entouré de doubles guillemets."
#: plugin.py:148
#: plugin.py:157
msgid ""
"<string 1> <string 2>\n"
"\n"
@ -154,7 +166,7 @@ msgstr ""
"\n"
"Concatène les deux chaînes. Notez que ce n'est pas la même chose que de les joindre avec \"\", car, si <chaîne 2> contient des espaces, ils ne seront pas supprimés par la concaténation."
#: plugin.py:159
#: plugin.py:168
msgid ""
"<size> <text>\n"
"\n"
@ -167,7 +179,7 @@ msgstr ""
"\n"
"Coup le <texte> en morceaux de <taille>, en découpant les caractères dépassant la <taille>. Si la <taille> est un nombre négatif, il coupe en comptant à partir de la fin du texte."
#: plugin.py:170
#: plugin.py:179
msgid ""
"<number> <text>\n"
"\n"
@ -179,7 +191,7 @@ msgstr ""
"\n"
"Retourne le <nombre>-ième élément (séparé par des espaces) du <texte>. C'est à dire que si le texte est \"foo bar baz\" et que <nombre> est 2, \"bar\" sera retourné."
#: plugin.py:183
#: plugin.py:192
msgid ""
"<format string> [<arg> ...]\n"
"\n"
@ -191,7 +203,7 @@ msgstr ""
"<chaîne de formattage> [<arg> ...]\n"
"\n"
#: plugin.py:197
#: plugin.py:206
msgid "Not enough arguments for the format string."
msgstr "Pas assez d'arguments pour formatter la chaîne."

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 12:46+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -79,9 +79,10 @@ msgstr ""
#: plugin.py:103
#, docstring
msgid ""
"<text>\n"
"<substring to translate> <substring to replace it with> <text>\n"
"\n"
" Returns <text> uppercased.\n"
" Replaces all non-overlapping occurrences of <substring to translate>\n"
" with <substring to replace it with> in <text>.\n"
" "
msgstr ""
@ -90,7 +91,7 @@ msgstr ""
msgid ""
"<text>\n"
"\n"
" Returns <text> lowercased.\n"
" Returns <text> uppercased.\n"
" "
msgstr ""
@ -99,7 +100,7 @@ msgstr ""
msgid ""
"<text>\n"
"\n"
" Returns <text> capitalized.\n"
" Returns <text> lowercased.\n"
" "
msgstr ""
@ -108,7 +109,7 @@ msgstr ""
msgid ""
"<text>\n"
"\n"
" Returns <text> titlecased.\n"
" Returns <text> capitalized.\n"
" "
msgstr ""
@ -117,13 +118,22 @@ msgstr ""
msgid ""
"<text>\n"
"\n"
" Returns the text surrounded by double quotes.\n"
" Returns <text> titlecased.\n"
" "
msgstr ""
#: plugin.py:148
#, docstring
msgid ""
"<text>\n"
"\n"
" Returns the text surrounded by double quotes.\n"
" "
msgstr ""
#: plugin.py:157
#, docstring
msgid ""
"<string 1> <string 2>\n"
"\n"
" Concatenates two strings. Do keep in mind that this is *not* the same\n"
@ -132,7 +142,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:159
#: plugin.py:168
#, docstring
msgid ""
"<size> <text>\n"
@ -143,7 +153,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:170
#: plugin.py:179
#, docstring
msgid ""
"<number> <text>\n"
@ -153,7 +163,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:183
#: plugin.py:192
#, docstring
msgid ""
"<format string> [<arg> ...]\n"
@ -164,7 +174,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:197
#: plugin.py:206
msgid "Not enough arguments for the format string."
msgstr ""

View File

@ -99,6 +99,15 @@ class Format(callbacks.Plugin):
translate = wrap(translate, ['something', 'something', 'text'])
@internationalizeDocstring
def replace(self, irc, msg, args, bad, good, text):
"""<substring to translate> <substring to replace it with> <text>
Replaces all non-overlapping occurrences of <substring to translate>
with <substring to replace it with> in <text>.
"""
irc.reply(text.replace(bad, good))
replace = wrap(replace, ['something', 'something', 'text'])
def upper(self, irc, msg, args, text):
"""<text>

View File

@ -54,6 +54,10 @@ class FormatTestCase(PluginTestCase):
def testTranslate(self):
self.assertResponse('translate 123 456 1234567890', '4564567890')
self.assertError('translate 123 1234 123125151')
def testReplace(self):
self.assertResponse('replace # %23 bla#foo', 'bla%23foo')
def testUpper(self):
self.assertResponse('upper foo', 'FOO')

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 13:16+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"

View File

@ -72,7 +72,8 @@ class Language(registry.OnlySomeStrings):
'Tamil': 'ta', 'Tagalog': 'tl', 'Telugu': 'te',
'Thai': 'th', 'Tibetan': 'bo', 'Turkish': 'tr',
'Ukranian': 'uk', 'Urdu': 'ur', 'Uzbek': 'uz',
'Uighur': 'ug', 'Vietnamese': 'vi'}
'Uighur': 'ug', 'Vietnamese': 'vi',
'Detect language': 'auto'}
validStrings = ['lang_' + s for s in transLangs.values()]
validStrings.append('')
def normalize(self, s):

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2011-02-14 18:30+CET\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -27,12 +27,12 @@ msgstr ""
msgid "Do you want the Google search snarfer enabled by default?"
msgstr ""
#: config.py:88
#: config.py:89
#, docstring
msgid "Value must be 1 <= n <= 8"
msgstr ""
#: config.py:99
#: config.py:100
msgid ""
"Determines the URL that will be sent to Google for\n"
" the Referer field of the search requests. If this value is empty, a\n"
@ -40,7 +40,7 @@ msgid ""
" http://$server/$botName"
msgstr ""
#: config.py:104
#: config.py:105
msgid ""
"Determines whether the search snarfer is\n"
" enabled. If so, messages (even unaddressed ones) beginning with the word\n"
@ -48,33 +48,33 @@ msgid ""
" channel."
msgstr ""
#: config.py:109
#: config.py:110
msgid ""
"Determines whether the word 'google' in the\n"
" bot's output will be made colorful (like Google's logo)."
msgstr ""
#: config.py:112
#: config.py:113
msgid "Determines whether results are bolded."
msgstr ""
#: config.py:114
#: config.py:115
msgid ""
"Determines the maximum number of results returned\n"
" from the google command."
msgstr ""
#: config.py:117
#: config.py:118
msgid ""
"Determines what default language is used in\n"
" searches. If left empty, no specific language will be requested."
msgstr ""
#: config.py:117
#: config.py:118
msgid "en"
msgstr ""
#: config.py:120
#: config.py:121
msgid ""
"Determines what level of search filtering to use\n"
" by default. 'active' - most filtering, 'moderate' - default filtering,\n"
@ -174,12 +174,12 @@ msgstr ""
msgid "to language"
msgstr ""
#: plugin.py:297
#: plugin.py:314
#, docstring
msgid "^google\\s+(.*)$"
msgstr ""
#: plugin.py:320
#: plugin.py:336
#, docstring
msgid ""
"<expression>\n"
@ -188,11 +188,11 @@ msgid ""
" "
msgstr ""
#: plugin.py:337
#: plugin.py:353
msgid "Google's calculator didn't come up with anything."
msgstr ""
#: plugin.py:343
#: plugin.py:359
#, docstring
msgid ""
"<phone number>\n"
@ -201,7 +201,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:357
#: plugin.py:373
msgid "Google's phonebook didn't come up with anything."
msgstr ""

View File

@ -282,6 +282,11 @@ class Google(callbacks.PluginRegexp):
lang.transLangs.keys()))
else:
toLang = lang.normalize('lang_'+toLang)[5:]
if fromLang == 'auto':
fromLang = ''
if toLang == 'auto':
irc.error("Destination language cannot be 'auto'.")
return
opts['langpair'] = '%s|%s' % (fromLang, toLang)
fd = utils.web.getUrlFd('%s?%s' % (self._gtranslateUrl,
urllib.urlencode(opts)),
@ -289,8 +294,20 @@ class Google(callbacks.PluginRegexp):
json = simplejson.load(fd)
fd.close()
if json['responseStatus'] != 200:
raise callbacks.Error, 'We broke The Google!'
irc.reply(json['responseData']['translatedText'].encode('utf-8'))
raise callbacks.Error, 'Google says: Response Status %s: %s.' % \
(json['responseStatus'], json['responseDetails'],)
if fromLang != '':
irc.reply(json['responseData']['translatedText'].encode('utf-8'))
else:
detected_language = json['responseData']['detectedSourceLanguage'].encode('utf-8')
translation = json['responseData']['translatedText'].encode('utf-8')
try:
long_lang_name = [k for k,v in lang.transLangs.iteritems() if v == detected_language][0]
except IndexError: #just in case google adds langs we don't know about
long_lang_name = detected_language
responsestring = "(Detected source language: %s) %s" % \
(long_lang_name, translation)
irc.reply(responsestring)
translate = wrap(translate, ['something', 'to', 'something', 'text'])
def googleSnarfer(self, irc, msg, match):
@ -310,8 +327,7 @@ class Google(callbacks.PluginRegexp):
url = r'http://google.com/search?q=' + s
return url
_calcRe1 = re.compile(r'<table.*class="?obcontainer"?[^>]*>(.*?)</table>', re.I)
_calcRe2 = re.compile(r'<h\d class="?r"?.*?<b>(.*?)</b>', re.I)
_calcRe = re.compile(r'<h\d class="?r"?.*?<b>(.*?)</b>', re.I)
_calcSupRe = re.compile(r'<sup>(.*?)</sup>', re.I)
_calcFontRe = re.compile(r'<font size=-2>(.*?)</font>')
_calcTimesRe = re.compile(r'&(?:times|#215);')
@ -323,9 +339,7 @@ class Google(callbacks.PluginRegexp):
"""
url = self._googleUrl(expr)
html = utils.web.getUrl(url)
match = self._calcRe1.search(html)
if match is None:
match = self._calcRe2.search(html)
match = self._calcRe.search(html)
if match is not None:
s = match.group(1)
s = self._calcSupRe.sub(r'^(\1)', s)

View File

@ -39,7 +39,6 @@ class GoogleTestCase(ChannelPluginTestCase):
def testCalc(self):
self.assertNotRegexp('google calc e^(i*pi)+1', r'didn\'t')
self.assertNotRegexp('google calc 1 usd in gbp', r'didn\'t')
self.assertRegexp('google calc current time in usa', r'Time in.*USA')
def testHtmlHandled(self):
self.assertNotRegexp('google calc '
@ -61,6 +60,8 @@ class GoogleTestCase(ChannelPluginTestCase):
def testTranslate(self):
self.assertRegexp('translate en es hello world', 'mundo')
self.assertRegexp('translate auto en ciao', 'Italian.*hello')
self.assertError('translate en to auto stuff')
def testCalcDoesNotHaveExtraSpaces(self):
self.assertNotRegexp('google calc 1000^2', r'\s+,\s+')

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 15:21+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 15:20+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -15,12 +15,12 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n"
#: plugin.py:43
#: plugin.py:42
#, docstring
msgid "Add the help for \"@help Internet\" here."
msgstr ""
#: plugin.py:47
#: plugin.py:46
#, docstring
msgid ""
"<host|ip>\n"
@ -29,11 +29,11 @@ msgid ""
" "
msgstr ""
#: plugin.py:54 plugin.py:61 plugin.py:65
#: plugin.py:53 plugin.py:60 plugin.py:64
msgid "Host not found."
msgstr ""
#: plugin.py:77
#: plugin.py:76
#, docstring
msgid ""
"<domain>\n"
@ -42,39 +42,39 @@ msgid ""
" "
msgstr ""
#: plugin.py:83
#: plugin.py:82
msgid "domain"
msgstr ""
#: plugin.py:112
#: plugin.py:111
msgid "updated %s"
msgstr ""
#: plugin.py:115
#: plugin.py:114
msgid "registered %s"
msgstr ""
#: plugin.py:118
#: plugin.py:117
msgid "expires %s"
msgstr ""
#: plugin.py:138
#: plugin.py:137
msgid " <registered at %s>"
msgstr ""
#: plugin.py:140
#: plugin.py:139
msgid " <registered by %s>"
msgstr ""
#: plugin.py:145
#: plugin.py:144
msgid "%s%s is %L."
msgstr ""
#: plugin.py:148
#: plugin.py:147
msgid "I couldn't find such a domain."
msgstr ""
#: plugin.py:153
#: plugin.py:152
#, docstring
msgid ""
"<ip>\n"

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-28 15:19+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -51,11 +51,11 @@ msgid ""
" increase/decrease karma without being addressed."
msgstr ""
#: plugin.py:243 plugin.py:251
#: plugin.py:247 plugin.py:255
msgid "You're not allowed to adjust your own karma."
msgstr ""
#: plugin.py:280
#: plugin.py:284
#, docstring
msgid ""
"[<channel>] [<thing> ...]\n"
@ -69,35 +69,35 @@ msgid ""
" "
msgstr ""
#: plugin.py:293
#: plugin.py:297
msgid "%s has neutral karma."
msgstr ""
#: plugin.py:300
#: plugin.py:304
msgid "Karma for %q has been increased %n and decreased %n for a total karma of %s."
msgstr ""
#: plugin.py:302 plugin.py:303
#: plugin.py:306 plugin.py:307
msgid "time"
msgstr ""
#: plugin.py:316
#: plugin.py:320
msgid "I didn't know the karma for any of those things."
msgstr ""
#: plugin.py:326 plugin.py:355
#: plugin.py:330 plugin.py:359
msgid "I have no karma for this channel."
msgstr ""
#: plugin.py:331
#: plugin.py:335
msgid " You (%s) are ranked %i out of %i."
msgstr ""
#: plugin.py:335
#: plugin.py:339
msgid "Highest karma: %L. Lowest karma: %L.%s"
msgstr ""
#: plugin.py:343
#: plugin.py:347
#, docstring
msgid ""
"[<channel>] {increased,decreased,active}\n"
@ -108,7 +108,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:361
#: plugin.py:365
#, docstring
msgid ""
"[<channel>] <name>\n"
@ -117,7 +117,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:371
#: plugin.py:375
#, docstring
msgid ""
"[<channel>] <filename>\n"
@ -128,7 +128,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:383
#: plugin.py:387
#, docstring
msgid ""
"[<channel>] <filename>\n"

View File

@ -41,6 +41,11 @@ import supybot.callbacks as callbacks
from supybot.i18n import PluginInternationalization, internationalizeDocstring
_ = PluginInternationalization('Karma')
try:
import sqlite3
except ImportError:
from pysqlite2 import dbapi2 as sqlite3 # for python2.4
class SqliteKarmaDB(object):
def __init__(self, filename):
self.dbs = ircutils.IrcDict()
@ -51,19 +56,16 @@ class SqliteKarmaDB(object):
db.close()
def _getDb(self, channel):
try:
import sqlite
except ImportError:
raise callbacks.Error, 'You need to have PySQLite installed to ' \
'use Karma. Download it at ' \
'<http://code.google.com/p/pysqlite/>'
filename = plugins.makeChannelFilename(self.filename, channel)
if filename in self.dbs:
return self.dbs[filename]
if os.path.exists(filename):
self.dbs[filename] = sqlite.connect(filename)
return self.dbs[filename]
db = sqlite.connect(filename)
db = sqlite3.connect(filename)
db.text_factory = str
self.dbs[filename] = db
return db
db = sqlite3.connect(filename)
db.text_factory = str
self.dbs[filename] = db
cursor = db.cursor()
cursor.execute("""CREATE TABLE karma (
@ -84,20 +86,21 @@ class SqliteKarmaDB(object):
thing = thing.lower()
cursor = db.cursor()
cursor.execute("""SELECT added, subtracted FROM karma
WHERE normalized=%s""", thing)
if cursor.rowcount == 0:
WHERE normalized=?""", (thing,))
results = cursor.fetchall()
if len(results) == 0:
return None
else:
return map(int, cursor.fetchone())
return map(int, results[0])
def gets(self, channel, things):
db = self._getDb(channel)
cursor = db.cursor()
normalizedThings = dict(zip(map(lambda s: s.lower(), things), things))
criteria = ' OR '.join(['normalized=%s'] * len(normalizedThings))
criteria = ' OR '.join(['normalized=?'] * len(normalizedThings))
sql = """SELECT name, added-subtracted FROM karma
WHERE %s ORDER BY added-subtracted DESC""" % criteria
cursor.execute(sql, *normalizedThings)
cursor.execute(sql, normalizedThings.keys())
L = [(name, int(karma)) for (name, karma) in cursor.fetchall()]
for (name, _) in L:
del normalizedThings[name.lower()]
@ -109,26 +112,27 @@ class SqliteKarmaDB(object):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT name, added-subtracted FROM karma
ORDER BY added-subtracted DESC LIMIT %s""", limit)
ORDER BY added-subtracted DESC LIMIT ?""", (limit,))
return [(t[0], int(t[1])) for t in cursor.fetchall()]
def bottom(self, channel, limit):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT name, added-subtracted FROM karma
ORDER BY added-subtracted ASC LIMIT %s""", limit)
ORDER BY added-subtracted ASC LIMIT ?""", (limit,))
return [(t[0], int(t[1])) for t in cursor.fetchall()]
def rank(self, channel, thing):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT added-subtracted FROM karma
WHERE name=%s""", thing)
if cursor.rowcount == 0:
WHERE name=?""", (thing,))
results = cursor.fetchall()
if len(results) == 0:
return None
karma = int(cursor.fetchone()[0])
karma = int(results[0][0])
cursor.execute("""SELECT COUNT(*) FROM karma
WHERE added-subtracted > %s""", karma)
WHERE added-subtracted > ?""", (karma,))
rank = int(cursor.fetchone()[0])
return rank+1
@ -142,20 +146,20 @@ class SqliteKarmaDB(object):
db = self._getDb(channel)
cursor = db.cursor()
normalized = name.lower()
cursor.execute("""INSERT INTO karma VALUES (NULL, %s, %s, 0, 0)""",
name, normalized)
cursor.execute("""INSERT INTO karma VALUES (NULL, ?, ?, 0, 0)""",
(name, normalized,))
cursor.execute("""UPDATE karma SET added=added+1
WHERE normalized=%s""", normalized)
WHERE normalized=?""", (normalized,))
db.commit()
def decrement(self, channel, name):
db = self._getDb(channel)
cursor = db.cursor()
normalized = name.lower()
cursor.execute("""INSERT INTO karma VALUES (NULL, %s, %s, 0, 0)""",
name, normalized)
cursor.execute("""INSERT INTO karma VALUES (NULL, ?, ?, 0, 0)""",
(name, normalized,))
cursor.execute("""UPDATE karma SET subtracted=subtracted+1
WHERE normalized=%s""", normalized)
WHERE normalized=?""", (normalized,))
db.commit()
def most(self, channel, kind, limit):
@ -179,7 +183,7 @@ class SqliteKarmaDB(object):
cursor = db.cursor()
normalized = name.lower()
cursor.execute("""UPDATE karma SET subtracted=0, added=0
WHERE normalized=%s""", normalized)
WHERE normalized=?""", (normalized,))
db.commit()
def dump(self, channel, filename):
@ -203,13 +207,13 @@ class SqliteKarmaDB(object):
for (name, added, subtracted) in reader:
normalized = name.lower()
cursor.execute("""INSERT INTO karma
VALUES (NULL, %s, %s, %s, %s)""",
name, normalized, added, subtracted)
VALUES (NULL, ?, ?, ?, ?)""",
(name, normalized, added, subtracted,))
db.commit()
fd.close()
KarmaDB = plugins.DB('Karma',
{'sqlite': SqliteKarmaDB})
{'sqlite3': SqliteKarmaDB})
class Karma(callbacks.Plugin):
callBefore = ('Factoids', 'MoobotFactoids', 'Infobot')

View File

@ -30,182 +30,181 @@
from supybot.test import *
try:
import sqlite
import sqlite3
except ImportError:
sqlite = None
from pysqlite2 import dbapi2 as sqlite3 # for python2.4
if sqlite is not None:
class KarmaTestCase(ChannelPluginTestCase):
plugins = ('Karma',)
def testKarma(self):
self.assertError('karma')
self.assertRegexp('karma foobar', 'neutral karma')
try:
conf.replyWhenNotCommand = True
self.assertNoResponse('foobar++', 2)
finally:
conf.replyWhenNotCommand = False
self.assertRegexp('karma foobar', 'increased 1.*total.*1')
self.assertRegexp('karma FOOBAR', 'increased 1.*total.*1')
self.assertNoResponse('foobar--', 2)
self.assertRegexp('karma foobar', 'decreased 1.*total.*0')
self.assertRegexp('karma FOOBAR', 'decreased 1.*total.*0')
self.assertNoResponse('FOO++', 2)
self.assertNoResponse('BAR--', 2)
self.assertRegexp('karma foo bar foobar', '.*foo.*foobar.*bar.*')
self.assertRegexp('karma FOO BAR FOOBAR', '.*foo.*foobar.*bar.*')
self.assertRegexp('karma FOO BAR FOOBAR',
'.*FOO.*foobar.*BAR.*', flags=0)
self.assertRegexp('karma foo bar foobar asdfjkl', 'asdfjkl')
# Test case-insensitive
self.assertNoResponse('MOO++', 2)
self.assertRegexp('karma moo',
'Karma for [\'"]moo[\'"].*increased 1.*total.*1')
self.assertRegexp('karma MoO',
'Karma for [\'"]MoO[\'"].*increased 1.*total.*1')
class KarmaTestCase(ChannelPluginTestCase):
plugins = ('Karma',)
def testKarma(self):
self.assertError('karma')
self.assertRegexp('karma foobar', 'neutral karma')
try:
conf.replyWhenNotCommand = True
self.assertNoResponse('foobar++', 2)
finally:
conf.replyWhenNotCommand = False
self.assertRegexp('karma foobar', 'increased 1.*total.*1')
self.assertRegexp('karma FOOBAR', 'increased 1.*total.*1')
self.assertNoResponse('foobar--', 2)
self.assertRegexp('karma foobar', 'decreased 1.*total.*0')
self.assertRegexp('karma FOOBAR', 'decreased 1.*total.*0')
self.assertNoResponse('FOO++', 2)
self.assertNoResponse('BAR--', 2)
self.assertRegexp('karma foo bar foobar', '.*foo.*foobar.*bar.*')
self.assertRegexp('karma FOO BAR FOOBAR', '.*foo.*foobar.*bar.*')
self.assertRegexp('karma FOO BAR FOOBAR',
'.*FOO.*foobar.*BAR.*', flags=0)
self.assertRegexp('karma foo bar foobar asdfjkl', 'asdfjkl')
# Test case-insensitive
self.assertNoResponse('MOO++', 2)
self.assertRegexp('karma moo',
'Karma for [\'"]moo[\'"].*increased 1.*total.*1')
self.assertRegexp('karma MoO',
'Karma for [\'"]MoO[\'"].*increased 1.*total.*1')
def testKarmaRankingDisplayConfigurable(self):
try:
orig = conf.supybot.plugins.Karma.response()
conf.supybot.plugins.Karma.response.setValue(True)
original = conf.supybot.plugins.Karma.rankingDisplay()
self.assertNotError('foo++')
self.assertNotError('foo++')
self.assertNotError('foo++')
self.assertNotError('foo++')
self.assertNotError('bar++')
self.assertNotError('bar++')
self.assertNotError('bar++')
self.assertNotError('baz++')
self.assertNotError('baz++')
self.assertNotError('quux++')
self.assertNotError('xuuq--')
self.assertNotError('zab--')
self.assertNotError('zab--')
self.assertNotError('rab--')
self.assertNotError('rab--')
self.assertNotError('rab--')
self.assertNotError('oof--')
self.assertNotError('oof--')
self.assertNotError('oof--')
self.assertNotError('oof--')
self.assertRegexp('karma', 'foo.*bar.*baz.*oof.*rab.*zab')
conf.supybot.plugins.Karma.rankingDisplay.setValue(4)
self.assertRegexp('karma', 'foo.*bar.*baz.*quux')
finally:
conf.supybot.plugins.Karma.response.setValue(orig)
conf.supybot.plugins.Karma.rankingDisplay.setValue(original)
def testKarmaRankingDisplayConfigurable(self):
try:
orig = conf.supybot.plugins.Karma.response()
conf.supybot.plugins.Karma.response.setValue(True)
original = conf.supybot.plugins.Karma.rankingDisplay()
self.assertNotError('foo++')
self.assertNotError('foo++')
self.assertNotError('foo++')
self.assertNotError('foo++')
self.assertNotError('bar++')
self.assertNotError('bar++')
self.assertNotError('bar++')
self.assertNotError('baz++')
self.assertNotError('baz++')
self.assertNotError('quux++')
self.assertNotError('xuuq--')
self.assertNotError('zab--')
self.assertNotError('zab--')
self.assertNotError('rab--')
self.assertNotError('rab--')
self.assertNotError('rab--')
self.assertNotError('oof--')
self.assertNotError('oof--')
self.assertNotError('oof--')
self.assertNotError('oof--')
self.assertRegexp('karma', 'foo.*bar.*baz.*oof.*rab.*zab')
conf.supybot.plugins.Karma.rankingDisplay.setValue(4)
self.assertRegexp('karma', 'foo.*bar.*baz.*quux')
finally:
conf.supybot.plugins.Karma.response.setValue(orig)
conf.supybot.plugins.Karma.rankingDisplay.setValue(original)
def testMost(self):
self.assertError('most increased')
self.assertError('most decreased')
self.assertError('most active')
self.assertHelp('most aldsfkj')
self.assertNoResponse('foo++', 1)
self.assertNoResponse('foo++', 1)
self.assertNoResponse('bar++', 1)
self.assertNoResponse('bar--', 1)
self.assertNoResponse('bar--', 1)
self.assertRegexp('karma most active', 'bar.*foo')
self.assertRegexp('karma most increased', 'foo.*bar')
self.assertRegexp('karma most decreased', 'bar.*foo')
self.assertNoResponse('foo--', 1)
self.assertNoResponse('foo--', 1)
self.assertNoResponse('foo--', 1)
self.assertNoResponse('foo--', 1)
self.assertRegexp('karma most active', 'foo.*bar')
self.assertRegexp('karma most increased', 'foo.*bar')
self.assertRegexp('karma most decreased', 'foo.*bar')
def testMost(self):
self.assertError('most increased')
self.assertError('most decreased')
self.assertError('most active')
self.assertHelp('most aldsfkj')
self.assertNoResponse('foo++', 1)
self.assertNoResponse('foo++', 1)
self.assertNoResponse('bar++', 1)
self.assertNoResponse('bar--', 1)
self.assertNoResponse('bar--', 1)
self.assertRegexp('karma most active', 'bar.*foo')
self.assertRegexp('karma most increased', 'foo.*bar')
self.assertRegexp('karma most decreased', 'bar.*foo')
self.assertNoResponse('foo--', 1)
self.assertNoResponse('foo--', 1)
self.assertNoResponse('foo--', 1)
self.assertNoResponse('foo--', 1)
self.assertRegexp('karma most active', 'foo.*bar')
self.assertRegexp('karma most increased', 'foo.*bar')
self.assertRegexp('karma most decreased', 'foo.*bar')
def testSimpleOutput(self):
try:
orig = conf.supybot.plugins.Karma.simpleOutput()
conf.supybot.plugins.Karma.simpleOutput.setValue(True)
self.assertNoResponse('foo++', 2)
self.assertResponse('karma foo', 'foo: 1')
self.assertNoResponse('bar--', 2)
self.assertResponse('karma bar', 'bar: -1')
finally:
conf.supybot.plugins.Karma.simpleOutput.setValue(orig)
def testSelfRating(self):
nick = self.nick
try:
orig = conf.supybot.plugins.Karma.allowSelfRating()
conf.supybot.plugins.Karma.allowSelfRating.setValue(False)
self.assertError('%s++' % nick)
self.assertResponse('karma %s' % nick,
'%s has neutral karma.' % nick)
conf.supybot.plugins.Karma.allowSelfRating.setValue(True)
self.assertNoResponse('%s++' % nick, 2)
self.assertRegexp('karma %s' % nick,
'Karma for [\'"]%s[\'"].*increased 1.*total.*1' % nick)
finally:
conf.supybot.plugins.Karma.allowSelfRating.setValue(orig)
def testKarmaOutputConfigurable(self):
def testSimpleOutput(self):
try:
orig = conf.supybot.plugins.Karma.simpleOutput()
conf.supybot.plugins.Karma.simpleOutput.setValue(True)
self.assertNoResponse('foo++', 2)
try:
orig = conf.supybot.plugins.Karma.response()
conf.supybot.plugins.Karma.response.setValue(True)
self.assertNotError('foo++')
finally:
conf.supybot.plugins.Karma.response.setValue(orig)
self.assertResponse('karma foo', 'foo: 1')
self.assertNoResponse('bar--', 2)
self.assertResponse('karma bar', 'bar: -1')
finally:
conf.supybot.plugins.Karma.simpleOutput.setValue(orig)
def testKarmaMostDisplayConfigurable(self):
self.assertNoResponse('foo++', 1)
self.assertNoResponse('foo++', 1)
self.assertNoResponse('bar++', 1)
self.assertNoResponse('bar--', 1)
self.assertNoResponse('bar--', 1)
self.assertNoResponse('foo--', 1)
self.assertNoResponse('foo--', 1)
self.assertNoResponse('foo--', 1)
self.assertNoResponse('foo--', 1)
try:
orig = conf.supybot.plugins.Karma.mostDisplay()
conf.supybot.plugins.Karma.mostDisplay.setValue(1)
self.assertRegexp('karma most active', '(?!bar)')
conf.supybot.plugins.Karma.mostDisplay.setValue(25)
self.assertRegexp('karma most active', 'bar')
finally:
conf.supybot.plugins.Karma.mostDisplay.setValue(orig)
def testSelfRating(self):
nick = self.nick
try:
orig = conf.supybot.plugins.Karma.allowSelfRating()
conf.supybot.plugins.Karma.allowSelfRating.setValue(False)
self.assertError('%s++' % nick)
self.assertResponse('karma %s' % nick,
'%s has neutral karma.' % nick)
conf.supybot.plugins.Karma.allowSelfRating.setValue(True)
self.assertNoResponse('%s++' % nick, 2)
self.assertRegexp('karma %s' % nick,
'Karma for [\'"]%s[\'"].*increased 1.*total.*1' % nick)
finally:
conf.supybot.plugins.Karma.allowSelfRating.setValue(orig)
def testKarmaOutputConfigurable(self):
self.assertNoResponse('foo++', 2)
try:
orig = conf.supybot.plugins.Karma.response()
conf.supybot.plugins.Karma.response.setValue(True)
self.assertNotError('foo++')
finally:
conf.supybot.plugins.Karma.response.setValue(orig)
def testKarmaMostDisplayConfigurable(self):
self.assertNoResponse('foo++', 1)
self.assertNoResponse('foo++', 1)
self.assertNoResponse('bar++', 1)
self.assertNoResponse('bar--', 1)
self.assertNoResponse('bar--', 1)
self.assertNoResponse('foo--', 1)
self.assertNoResponse('foo--', 1)
self.assertNoResponse('foo--', 1)
self.assertNoResponse('foo--', 1)
try:
orig = conf.supybot.plugins.Karma.mostDisplay()
conf.supybot.plugins.Karma.mostDisplay.setValue(1)
self.assertRegexp('karma most active', '(?!bar)')
conf.supybot.plugins.Karma.mostDisplay.setValue(25)
self.assertRegexp('karma most active', 'bar')
finally:
conf.supybot.plugins.Karma.mostDisplay.setValue(orig)
def testIncreaseKarmaWithNickNotCallingInvalidCommand(self):
self.assertSnarfNoResponse('%s: foo++' % self.irc.nick, 3)
def testIncreaseKarmaWithNickNotCallingInvalidCommand(self):
self.assertSnarfNoResponse('%s: foo++' % self.irc.nick, 3)
def testClear(self):
self.assertNoResponse('foo++', 1)
self.assertRegexp('karma foo', '1')
self.assertNotError('karma clear foo')
self.assertRegexp('karma foo', '0')
self.assertNotRegexp('karma foo', '1')
def testClear(self):
self.assertNoResponse('foo++', 1)
self.assertRegexp('karma foo', '1')
self.assertNotError('karma clear foo')
self.assertRegexp('karma foo', '0')
self.assertNotRegexp('karma foo', '1')
# def testNoKarmaDunno(self):
# self.assertNotError('load Infobot')
# self.assertNoResponse('foo++')
def testMultiWordKarma(self):
self.assertNoResponse('(foo bar)++', 1)
self.assertRegexp('karma "foo bar"', '1')
def testMultiWordKarma(self):
self.assertNoResponse('(foo bar)++', 1)
self.assertRegexp('karma "foo bar"', '1')
def testUnaddressedKarma(self):
karma = conf.supybot.plugins.Karma
resp = karma.response()
unaddressed = karma.allowUnaddressedKarma()
try:
karma.response.setValue(True)
karma.allowUnaddressedKarma.setValue(True)
for m in ('++', '--'):
self.assertRegexp('foo%s' % m, 'operation')
self.assertSnarfRegexp('foo%s' % m, 'operation')
#self.assertNoResponse('foo bar%s' % m)
#self.assertSnarfNoResponse('foo bar%s' % m)
self.assertRegexp('(foo bar)%s' % m, 'operation')
self.assertSnarfRegexp('(foo bar)%s' % m, 'operation')
finally:
karma.response.setValue(resp)
karma.allowUnaddressedKarma.setValue(unaddressed)
def testUnaddressedKarma(self):
karma = conf.supybot.plugins.Karma
resp = karma.response()
unaddressed = karma.allowUnaddressedKarma()
try:
karma.response.setValue(True)
karma.allowUnaddressedKarma.setValue(True)
for m in ('++', '--'):
self.assertRegexp('foo%s' % m, 'operation')
self.assertSnarfRegexp('foo%s' % m, 'operation')
#self.assertNoResponse('foo bar%s' % m)
#self.assertSnarfNoResponse('foo bar%s' % m)
self.assertRegexp('(foo bar)%s' % m, 'operation')
self.assertSnarfRegexp('(foo bar)%s' % m, 'operation')
finally:
karma.response.setValue(resp)
karma.allowUnaddressedKarma.setValue(unaddressed)
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 15:21+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"

View File

@ -48,5 +48,12 @@ conf.registerGlobalValue(Later, 'maximum',
conf.registerGlobalValue(Later, 'private',
registry.Boolean(True, _("""Determines whether users will be notified in
the first place in which they're seen, or in private.""")))
conf.registerGlobalValue(Later, 'tellOnJoin',
registry.Boolean(True, _("""Determines whether users will be notified upon
joining any channel the bot is in, or only upon sending a message.""")))
conf.registerGlobalValue(Later, 'messageExpiry',
registry.NonNegativeInteger(30, _("""Determines the maximum number of
days that a message will remain queued for a user. After this time elapses,
the message will be deleted. If this value is 0, there is no maximum.""")))
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

View File

@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: Supybot-fr\n"
"POT-Creation-Date: 2010-10-28 16:15+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\n"
"PO-Revision-Date: \n"
"Last-Translator: Valentin Lorentz <progval@gmail.com>\n"
"Language-Team: Supybot-fr <progval@gmail.com>\n"
@ -26,6 +26,19 @@ msgid ""
" the first place in which they're seen, or in private."
msgstr "Détermine si les utilisateurs seront notifiés au premier endroit où ils sont vus, ou en privé."
#: config.py:52
msgid ""
"Determines whether users will be notified upon\n"
" joining any channel the bot is in, or only upon sending a message."
msgstr "Détermine si les utilisateurs seront notifiés au premier endroit où ils sont vus, ou seulement lorsqu'ils envoient un message."
#: config.py:55
msgid ""
"Determines the maximum number of\n"
" days that a message will remain queued for a user. After this time elapses,\n"
" the message will be deleted. If this value is 0, there is no maximum."
msgstr "Détermine le nombre maximum de messages en attente d'un utilisateur. Après que ce temps se soit écoulé, le message sera supprimé. Si la valeur est 0, il n'y a pas de maximum."
#: plugin.py:46
msgid ""
"Used to do things later; currently, it only allows the sending of\n"
@ -42,7 +55,22 @@ msgstr "il y a %s"
msgid "just now"
msgstr "à l'instant"
#: plugin.py:107
#: plugin.py:106
msgid ""
"Validate nick according to the IRC RFC 2812 spec.\n"
"\n"
" Reference: http://tools.ietf.org/rfcmarkup?doc=2812#section-2.3.1\n"
"\n"
" Some irc clients' tab-completion feature appends 'address' characters\n"
" to nick, such as ':' or ','. We try correcting for that by trimming\n"
" a char off the end.\n"
"\n"
" If nick incorrigibly invalid, return False, otherwise,\n"
" return (possibly trimmed) nick.\n"
" "
msgstr ""
#: plugin.py:151
msgid ""
"<nick> <text>\n"
"\n"
@ -55,15 +83,15 @@ msgstr ""
"\n"
"Dit le <texte> à <nick> la prochaine fois qu'il est vu. <nick> peut contenir des jokers, et le premier nick correspondant recevra la note."
#: plugin.py:114
#: plugin.py:159
msgid "I can't send notes to myself."
msgstr "Je ne peux m'envoyer de notes à moi-même."
#: plugin.py:120
#: plugin.py:169
msgid "That person's message queue is already full."
msgstr "La file d'attente des messages de cette personne est déjà pleine."
#: plugin.py:125
#: plugin.py:174
msgid ""
"[<nick>]\n"
"\n"
@ -75,19 +103,19 @@ msgstr ""
"\n"
"Si le <nick> est donné, répond avec les notes en attente pour <nick> ; sinon, répond avec les nicks ayant des notes en attente."
#: plugin.py:136
#: plugin.py:185
msgid "I have no notes for that nick."
msgstr "Je n'ai pas de note pour ce nick."
#: plugin.py:141
#: plugin.py:190
msgid "I currently have notes waiting for %L."
msgstr "J'ai actuellement des notes en attente pour %L."
#: plugin.py:144
#: plugin.py:193
msgid "I have no notes waiting to be delivered."
msgstr "Je n'ai pas de note à délivrer."
#: plugin.py:149
#: plugin.py:198
msgid ""
"<nick>\n"
"\n"
@ -98,11 +126,11 @@ msgstr ""
"\n"
"Supprime les notes en attente pour <nick>."
#: plugin.py:158
#: plugin.py:207
msgid "There were no notes for %r"
msgstr "Il n'y a pas de note pour %r"
#: plugin.py:182
#: plugin.py:231
msgid "Sent %s: <%s> %s"
msgstr "Envoyé %s : <%s> %s"

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-28 16:15+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -28,6 +28,19 @@ msgid ""
" the first place in which they're seen, or in private."
msgstr ""
#: config.py:52
msgid ""
"Determines whether users will be notified upon\n"
" joining any channel the bot is in, or only upon sending a message."
msgstr ""
#: config.py:55
msgid ""
"Determines the maximum number of\n"
" days that a message will remain queued for a user. After this time elapses,\n"
" the message will be deleted. If this value is 0, there is no maximum."
msgstr ""
#: plugin.py:46
#, docstring
msgid ""
@ -45,7 +58,23 @@ msgstr ""
msgid "just now"
msgstr ""
#: plugin.py:107
#: plugin.py:106
#, docstring
msgid ""
"Validate nick according to the IRC RFC 2812 spec.\n"
"\n"
" Reference: http://tools.ietf.org/rfcmarkup?doc=2812#section-2.3.1\n"
"\n"
" Some irc clients' tab-completion feature appends 'address' characters\n"
" to nick, such as ':' or ','. We try correcting for that by trimming\n"
" a char off the end.\n"
"\n"
" If nick incorrigibly invalid, return False, otherwise,\n"
" return (possibly trimmed) nick.\n"
" "
msgstr ""
#: plugin.py:151
#, docstring
msgid ""
"<nick> <text>\n"
@ -56,15 +85,15 @@ msgid ""
" "
msgstr ""
#: plugin.py:114
#: plugin.py:159
msgid "I can't send notes to myself."
msgstr ""
#: plugin.py:120
#: plugin.py:169
msgid "That person's message queue is already full."
msgstr ""
#: plugin.py:125
#: plugin.py:174
#, docstring
msgid ""
"[<nick>]\n"
@ -74,19 +103,19 @@ msgid ""
" "
msgstr ""
#: plugin.py:136
#: plugin.py:185
msgid "I have no notes for that nick."
msgstr ""
#: plugin.py:141
#: plugin.py:190
msgid "I currently have notes waiting for %L."
msgstr ""
#: plugin.py:144
#: plugin.py:193
msgid "I have no notes waiting to be delivered."
msgstr ""
#: plugin.py:149
#: plugin.py:198
#, docstring
msgid ""
"<nick>\n"
@ -95,11 +124,11 @@ msgid ""
" "
msgstr ""
#: plugin.py:158
#: plugin.py:207
msgid "There were no notes for %r"
msgstr ""
#: plugin.py:182
#: plugin.py:231
msgid "Sent %s: <%s> %s"
msgstr ""

View File

@ -30,6 +30,7 @@
import csv
import time
import datetime
import supybot.log as log
import supybot.conf as conf
@ -41,7 +42,6 @@ import supybot.callbacks as callbacks
from supybot.i18n import PluginInternationalization, internationalizeDocstring
_ = PluginInternationalization('Later')
class Later(callbacks.Plugin):
"""Used to do things later; currently, it only allows the sending of
nick-based notes. Do note (haha!) that these notes are *not* private
@ -102,6 +102,50 @@ class Later(callbacks.Plugin):
self.wildcards.append(nick)
self._flushNotes()
def _validateNick(self, irc, nick):
"""Validate nick according to the IRC RFC 2812 spec.
Reference: http://tools.ietf.org/rfcmarkup?doc=2812#section-2.3.1
Some irc clients' tab-completion feature appends 'address' characters
to nick, such as ':' or ','. We try correcting for that by trimming
a char off the end.
If nick incorrigibly invalid, return False, otherwise,
return (possibly trimmed) nick.
"""
if not irc.isNick(nick):
if not irc.isNick(nick[:-1]):
return False
else:
return nick[:-1]
return nick
def _deleteExpired(self):
expiry = self.registryValue('messageExpiry')
curtime = time.time()
nickremovals=[]
for (nick, notes) in self._notes.iteritems():
removals = []
for (notetime, whence, text) in notes:
td = datetime.timedelta(seconds=(curtime - notetime))
if td.days > expiry:
removals.append((notetime, whence, text))
for note in removals:
notes.remove(note)
if len(notes) == 0:
nickremovals.append(nick)
for nick in nickremovals:
del self._notes[nick]
self._flushNotes()
## Note: we call _deleteExpired from 'tell'. This means that it's possible
## for expired notes to remain in the database for longer than the maximum,
## if no tell's are called.
## However, the whole point of this is to avoid crud accumulation in the
## database, so it's fine that we only delete expired notes when we try
## adding new ones.
@internationalizeDocstring
def tell(self, irc, msg, args, nick, text):
"""<nick> <text>
@ -110,11 +154,16 @@ class Later(callbacks.Plugin):
contain wildcard characters, and the first matching nick will be
given the note.
"""
self._deleteExpired()
if ircutils.strEqual(nick, irc.nick):
irc.error(_('I can\'t send notes to myself.'))
return
validnick = self._validateNick(irc, nick)
if validnick is False:
irc.error('That is an invalid IRC nick. Please check your input.')
return
try:
self._addNote(nick, msg.nick, text)
self._addNote(validnick, msg.nick, text)
irc.replySuccess()
except ValueError:
irc.error(_('That person\'s message queue is already full.'))
@ -180,8 +229,11 @@ class Later(callbacks.Plugin):
def _formatNote(self, when, whence, note):
return _('Sent %s: <%s> %s') % (self._timestamp(when), whence, note)
Later = internationalizeDocstring(Later)
def doJoin(self, irc, msg):
if self.registryValue('tellOnJoin'):
self.doPrivmsg(irc, msg)
Later = internationalizeDocstring(Later)
Class = Later

View File

@ -28,8 +28,9 @@
###
from supybot.test import *
import time
class LaterTestCase(PluginTestCase):
class LaterTestCase(ChannelPluginTestCase):
plugins = ('Later',)
def testLaterWorksTwice(self):
self.assertNotError('later tell foo bar')
@ -43,6 +44,35 @@ class LaterTestCase(PluginTestCase):
self.assertNotRegexp('later notes', 'bar.*foo')
self.assertRegexp('later notes', 'foo')
def testNickValidation(self):
origconf = conf.supybot.protocols.irc.strictRfc()
conf.supybot.protocols.irc.strictRfc.setValue('True')
self.assertError('later tell 1foo bar')
self.assertError('later tell foo$moo zoob')
self.assertNotError('later tell foo: baz')
self.assertRegexp('later notes', 'foo\.')
conf.supybot.protocols.irc.strictRfc.setValue(origconf)
def testNoteExpiry(self):
cb = self.irc.getCallback('Later')
# add a note 40 days in the past
cb._addNote('foo', 'test', 'some stuff', at=(time.time() - 3456000))
self.assertRegexp('later notes', 'foo')
self.assertNotError('later tell moo stuff')
self.assertNotRegexp('later notes', 'foo')
self.assertRegexp('later notes', 'moo')
def testNoteSend(self):
self.assertNotError('later tell foo stuff')
self.assertNotError('later tell bar more stuff')
self.assertRegexp('later notes', 'bar.*foo')
testPrefix = 'foo!bar@baz'
self.irc.feedMsg(ircmsgs.privmsg(self.channel, 'something',
prefix=testPrefix))
m = self.getMsg(' ')
self.failUnless(str(m).startswith('PRIVMSG foo :Sent just now: <test> stuff'))
self.assertNotRegexp('later notes', 'foo')
self.assertRegexp('later notes', 'bar')
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 15:35+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -36,7 +36,7 @@ msgid ""
" larger than supybot.plugins.Limiter.limit.minimumExcess."
msgstr ""
#: plugin.py:40
#: plugin.py:39
#, docstring
msgid ""
"In order to use this plugin, its config values need to be properly\n"

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 15:35+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"

View File

@ -0,0 +1,3 @@
The MessageParser plugin allows you to set custom regexp triggers, which will trigger the bot to respond if they match anywhere in the message. This is useful for those cases when you want a bot response even when the bot was not explicitly addressed by name or prefix character.
An updated page of this plugin's documentation is located here: http://sourceforge.net/apps/mediawiki/gribble/index.php?title=MessageParser_Plugin

View File

@ -0,0 +1,67 @@
###
# Copyright (c) 2010, Daniel Folkinshteyn
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
"""
MessageParser can be configured to run commands when a message matches a
given trigger.
"""
import supybot
import supybot.world as world
# Use this for the version of this plugin. You may wish to put a CVS keyword
# in here if you're keeping the plugin in CVS or some similar system.
__version__ = "0.1"
# XXX Replace this with an appropriate author or supybot.Author instance.
__author__ = supybot.authors.unknown
# This is a dictionary mapping supybot.Author instances to lists of
# contributions.
__contributors__ = {}
# This is a url where the most recent plugin package can be downloaded.
__url__ = '' # 'http://supybot.com/Members/yourname/MessageParser/download'
import config
import plugin
reload(plugin) # In case we're being reloaded.
# Add more reloads here if you add third-party modules and want them to be
# reloaded when this plugin is reloaded. Don't forget to import them as well!
if world.testing:
import test
Class = plugin.Class
configure = config.configure
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:

View File

@ -0,0 +1,80 @@
###
# Copyright (c) 2010, Daniel Folkinshteyn
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
import supybot.conf as conf
import supybot.registry as registry
try:
from supybot.i18n import PluginInternationalization
from supybot.i18n import internationalizeDocstring
_ = PluginInternationalization('MessageParser')
except:
# This are useless functions that's allow to run the plugin on a bot
# without the i18n plugin
_ = lambda x:x
internationalizeDocstring = lambda x:x
def configure(advanced):
# This will be called by supybot to configure this module. advanced is
# a bool that specifies whether the user identified himself as an advanced
# user or not. You should effect your configuration by manipulating the
# registry as appropriate.
from supybot.questions import expect, anything, something, yn
conf.registerPlugin('MessageParser', True)
MessageParser = conf.registerPlugin('MessageParser')
# This is where your configuration variables (if any) should go. For example:
# conf.registerGlobalValue(MessageParser, 'someConfigVariableName',
# registry.Boolean(False, """Help for someConfigVariableName."""))
conf.registerChannelValue(MessageParser, 'enable',
registry.Boolean(True, _("""Determines whether the
message parser is enabled. If enabled, will trigger on regexps
added to the regexp db.""")))
conf.registerChannelValue(MessageParser, 'keepRankInfo',
registry.Boolean(True, _("""Determines whether we keep updating the usage
count for each regexp, for popularity ranking.""")))
conf.registerChannelValue(MessageParser, 'rankListLength',
registry.Integer(20, _("""Determines the number of regexps returned
by the triggerrank command.""")))
conf.registerChannelValue(MessageParser, 'requireVacuumCapability',
registry.String('admin', _("""Determines the capability required (if any) to
vacuum the database.""")))
conf.registerChannelValue(MessageParser, 'requireManageCapability',
registry.String('admin; channel,op', _("""Determines the
capabilities required (if any) to manage the regexp database,
including add, remove, lock, unlock. Use 'channel,capab' for
channel-level capabilities.
Note that absence of an explicit anticapability means user has
capability.""")))
conf.registerChannelValue(MessageParser, 'listSeparator',
registry.String(', ', _("""Determines the separator used between rexeps when
shown by the list command.""")))
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:

View File

@ -0,0 +1 @@
# Stub so local is a module, used for third-party modules

View File

@ -0,0 +1,241 @@
msgid ""
msgstr ""
"Project-Id-Version: Gribble\n"
"POT-Creation-Date: 2011-02-26 11:47+CET\n"
"PO-Revision-Date: \n"
"Last-Translator: Valentin Lorentz <progval@gmail.com>\n"
"Language-Team: Supybot-fr <progval@gmail.com>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Français\n"
"X-Poedit-Country: France\n"
"X-Poedit-SourceCharset: Gribble\n"
#: config.py:49
msgid ""
"Determines whether the\n"
" message parser is enabled. If enabled, will trigger on regexps\n"
" added to the regexp db."
msgstr "Détermine si le parseur de messages est activé. S'il l'est, il réagira aux expressions régulières qui sont dans la base de données d'expressions régulières."
#: config.py:53
msgid ""
"Determines whether we keep updating the usage\n"
" count for each regexp, for popularity ranking."
msgstr "Détermine si on met à jour le compteur d'utilisation de chaque expression régulière, pour un classement de popularité"
#: config.py:56
msgid ""
"Determines the number of regexps returned\n"
" by the triggerrank command."
msgstr "Détermine le nombre d'expressions régulières retournées par la commande triggerrank"
#: config.py:59
msgid ""
"Determines the capability required (if any) to\n"
" vacuum the database."
msgstr "Détermine la capacité requise (s'il y en a une) pour faire un vacuum de la base de données."
#: config.py:62
msgid ""
"Determines the\n"
" capabilities required (if any) to manage the regexp database,\n"
" including add, remove, lock, unlock. Use 'channel,capab' for\n"
" channel-level capabilities.\n"
" Note that absence of an explicit anticapability means user has\n"
" capability."
msgstr "Détermine les capacités requises (s'il y en a) pour gérer la base de données d'expressions régulières, ce qui inclue l'ajout, la suppression, le verrouillage, et le déverrouillage. Utilisez 'canal,capa' pour des permissions par canal. Notez que l'absence de toute anti-capacité explicite signifit que l'utilisateur peut le faire."
#: config.py:69
msgid ""
"Determines the separator used between rexeps when\n"
" shown by the list command."
msgstr "Détermine le séparateur utilisé entre les expressions régulières affichées par la commande list."
#: plugin.py:75
msgid ""
"This plugin can set regexp triggers to activate the bot.\n"
" Use 'add' command to add regexp trigger, 'remove' to remove."
msgstr "Ce plugin peut définir les triggers pour activer le bot. Utilisez la commande 'add' pour ajouter un trigger et 'remove' pour en retirer un."
#: plugin.py:83
msgid "Create the database and connect to it."
msgstr ""
#: plugin.py:106
msgid "Use this to get a database for a specific channel."
msgstr ""
#: plugin.py:129
msgid "Run a command from message, as if command was sent over IRC."
msgstr ""
#: plugin.py:137
msgid ""
"Check if the user has any of the required capabilities to manage\n"
" the regexp database."
msgstr ""
#: plugin.py:179
msgid ""
"[<channel>] <regexp> <action>\n"
"\n"
" Associates <regexp> with <action>. <channel> is only\n"
" necessary if the message isn't sent on the channel\n"
" itself. Action is echoed upon regexp match, with variables $1, $2, \n"
" etc. being interpolated from the regexp match groups."
msgstr ""
"[<canal>] <expression régulière> <action>\n"
"\n"
"Associe l'<expression régulière> à l'<action>. <action> est affiché après la correspondance avec l'<expression régulière>, avec les variables $1, $2, etc, récupérés à partir des groupes de correspondance de l'<expression régulière>.<canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
#: plugin.py:201
msgid "Invalid python regexp: %s"
msgstr "Expression régulière Python invalide : %s"
#: plugin.py:213
msgid "That trigger is locked."
msgstr "Ce trigger est bloqué."
#: plugin.py:219
msgid ""
"[<channel>] [--id] <regexp>]\n"
"\n"
" Removes the trigger for <regexp> from the triggers database. \n"
" <channel> is only necessary if\n"
" the message isn't sent in the channel itself.\n"
" If option --id specified, will retrieve by regexp id, not content.\n"
" "
msgstr ""
"[<canal>] [--id] <expression régulière>\n"
"\n"
"Supprime le déclencheur pour l'<expression régulière> de la base de données des déclencheurs. Si l'option --id est spécifiée, l'id de l'<expression régulière> sera récupéré, et non le contenu."
#: plugin.py:241
#: plugin.py:271
#: plugin.py:294
#: plugin.py:322
#: plugin.py:352
msgid "There is no such regexp trigger."
msgstr "Cette expression régulière n'existe pas."
#: plugin.py:245
msgid "This regexp trigger is locked."
msgstr "Cette expression régulière est verrouillée"
#: plugin.py:257
msgid ""
"[<channel>] <regexp>\n"
"\n"
" Locks the <regexp> so that it cannot be\n"
" removed or overwritten to. <channel> is only necessary if the message isn't\n"
" sent in the channel itself.\n"
" "
msgstr ""
"[<canal>] <expression régulière>\n"
"\n"
"Verrouille l'<expression régulière>, ce qui fait que l'on ne puisse plus la supprimer ou la modifier. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
#: plugin.py:280
msgid ""
"[<channel>] <regexp>\n"
"\n"
" Unlocks the entry associated with <regexp> so that it can be\n"
" removed or overwritten. <channel> is only necessary if the message isn't\n"
" sent in the channel itself.\n"
" "
msgstr ""
"[<canal>] <expression régulière>\n"
"\n"
"Déverrouille l'<expression régulière>, ce qui fait que l'on peut à nouveau la supprimer ou la modifier. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
#: plugin.py:303
msgid ""
"[<channel>] [--id] <regexp>\n"
"\n"
" Looks up the value of <regexp> in the triggers database.\n"
" <channel> is only necessary if the message isn't sent in the channel \n"
" itself.\n"
" If option --id specified, will retrieve by regexp id, not content.\n"
" "
msgstr ""
"[<canal>] [--id] <expression régulière>\n"
"\n"
"Recherche la valeur de l'<expression régulière> de la base de données des déclencheurs. Si l'option --id est spécifiée, l'id de l'<expression régulière> sera récupéré, et non le contenu."
#: plugin.py:332
msgid ""
"[<channel>] [--id] <regexp>\n"
"\n"
" Display information about <regexp> in the triggers database.\n"
" <channel> is only necessary if the message isn't sent in the channel \n"
" itself.\n"
" If option --id specified, will retrieve by regexp id, not content.\n"
" "
msgstr ""
"[<canal>] [--id] <expression régulière>\n"
"\n"
"Affiche des informations à propos de l'<expression régulière> de la base de données des déclencheurs. Si l'option --id est spécifiée, l'id de l'<expression régulière> sera récupéré, et non le contenu."
#: plugin.py:355
msgid "The regexp id is %d, regexp is \"%s\", and action is \"%s\". It was added by user %s on %s, has been triggered %d times, and is %s."
msgstr "L'id de l'expression régulière est %d, l'expression régulière est \"%s\", et l'action est \"%s\". Elle a été ajoutée par l'utilisateur %s le %s, et a été utilisée %d fois, et est %s"
#: plugin.py:364
msgid "locked"
msgstr "verouillée"
#: plugin.py:364
msgid "not locked"
msgstr "non verrouillée"
#: plugin.py:371
msgid ""
"[<channel>]\n"
"\n"
" Lists regexps present in the triggers database.\n"
" <channel> is only necessary if the message isn't sent in the channel \n"
" itself. Regexp ID listed in paretheses.\n"
" "
msgstr ""
"[<canal>]\n"
"\n"
"Liste les expressions régulières présentes dans la base de données. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
#: plugin.py:384
#: plugin.py:410
msgid "There are no regexp triggers in the database."
msgstr "Il n'y a pas d'expression régulière dans ma base de données."
#: plugin.py:394
msgid ""
"[<channel>]\n"
" \n"
" Returns a list of top-ranked regexps, sorted by usage count \n"
" (rank). The number of regexps returned is set by the \n"
" rankListLength registry value. <channel> is only necessary if the \n"
" message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<canal>]\n"
"\n"
"Retourne une liste des expressions régulières les plus utilisées. Le nombre d'expressions régulières est définie par la variable de registre supybot.plugins.MessageParser.rankListLength. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
#: plugin.py:418
msgid ""
"[<channel>]\n"
" \n"
" Vacuums the database for <channel>.\n"
" See SQLite vacuum doc here: http://www.sqlite.org/lang_vacuum.html\n"
" <channel> is only necessary if the message isn't sent in \n"
" the channel itself.\n"
" First check if user has the required capability specified in plugin \n"
" config requireVacuumCapability.\n"
" "
msgstr ""
"[<canal>]\n"
"\n"
"Fait un vacuum de la base de données pour le <canal>.Lisez la documentation de SQLite sur cette fonctionnalité : http://www.sqlite.org/lang_vacuum.htmlVérifie d'abord si l'utilisateur a bien la permission spécifiée dans la variable de configuration supybot.plugins.requireVacuumCapability<canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."

View File

@ -0,0 +1,225 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR ORGANIZATION
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2011-02-26 11:47+CET\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=CHARSET\n"
"Content-Transfer-Encoding: ENCODING\n"
"Generated-By: pygettext.py 1.5\n"
#: config.py:49
msgid ""
"Determines whether the\n"
" message parser is enabled. If enabled, will trigger on regexps\n"
" added to the regexp db."
msgstr ""
#: config.py:53
msgid ""
"Determines whether we keep updating the usage\n"
" count for each regexp, for popularity ranking."
msgstr ""
#: config.py:56
msgid ""
"Determines the number of regexps returned\n"
" by the triggerrank command."
msgstr ""
#: config.py:59
msgid ""
"Determines the capability required (if any) to\n"
" vacuum the database."
msgstr ""
#: config.py:62
msgid ""
"Determines the\n"
" capabilities required (if any) to manage the regexp database,\n"
" including add, remove, lock, unlock. Use 'channel,capab' for\n"
" channel-level capabilities.\n"
" Note that absence of an explicit anticapability means user has\n"
" capability."
msgstr ""
#: config.py:69
msgid ""
"Determines the separator used between rexeps when\n"
" shown by the list command."
msgstr ""
#: plugin.py:75
#, docstring
msgid ""
"This plugin can set regexp triggers to activate the bot.\n"
" Use 'add' command to add regexp trigger, 'remove' to remove."
msgstr ""
#: plugin.py:83
#, docstring
msgid "Create the database and connect to it."
msgstr ""
#: plugin.py:106
#, docstring
msgid "Use this to get a database for a specific channel."
msgstr ""
#: plugin.py:129
#, docstring
msgid "Run a command from message, as if command was sent over IRC."
msgstr ""
#: plugin.py:137
#, docstring
msgid ""
"Check if the user has any of the required capabilities to manage\n"
" the regexp database."
msgstr ""
#: plugin.py:179
#, docstring
msgid ""
"[<channel>] <regexp> <action>\n"
"\n"
" Associates <regexp> with <action>. <channel> is only\n"
" necessary if the message isn't sent on the channel\n"
" itself. Action is echoed upon regexp match, with variables $1, $2, \n"
" etc. being interpolated from the regexp match groups."
msgstr ""
#: plugin.py:201
msgid "Invalid python regexp: %s"
msgstr ""
#: plugin.py:213
msgid "That trigger is locked."
msgstr ""
#: plugin.py:219
#, docstring
msgid ""
"[<channel>] [--id] <regexp>]\n"
"\n"
" Removes the trigger for <regexp> from the triggers database. \n"
" <channel> is only necessary if\n"
" the message isn't sent in the channel itself.\n"
" If option --id specified, will retrieve by regexp id, not content.\n"
" "
msgstr ""
#: plugin.py:241 plugin.py:271 plugin.py:294 plugin.py:322 plugin.py:352
msgid "There is no such regexp trigger."
msgstr ""
#: plugin.py:245
msgid "This regexp trigger is locked."
msgstr ""
#: plugin.py:257
#, docstring
msgid ""
"[<channel>] <regexp>\n"
"\n"
" Locks the <regexp> so that it cannot be\n"
" removed or overwritten to. <channel> is only necessary if the message isn't\n"
" sent in the channel itself.\n"
" "
msgstr ""
#: plugin.py:280
#, docstring
msgid ""
"[<channel>] <regexp>\n"
"\n"
" Unlocks the entry associated with <regexp> so that it can be\n"
" removed or overwritten. <channel> is only necessary if the message isn't\n"
" sent in the channel itself.\n"
" "
msgstr ""
#: plugin.py:303
#, docstring
msgid ""
"[<channel>] [--id] <regexp>\n"
"\n"
" Looks up the value of <regexp> in the triggers database.\n"
" <channel> is only necessary if the message isn't sent in the channel \n"
" itself.\n"
" If option --id specified, will retrieve by regexp id, not content.\n"
" "
msgstr ""
#: plugin.py:332
#, docstring
msgid ""
"[<channel>] [--id] <regexp>\n"
"\n"
" Display information about <regexp> in the triggers database.\n"
" <channel> is only necessary if the message isn't sent in the channel \n"
" itself.\n"
" If option --id specified, will retrieve by regexp id, not content.\n"
" "
msgstr ""
#: plugin.py:355
msgid "The regexp id is %d, regexp is \"%s\", and action is \"%s\". It was added by user %s on %s, has been triggered %d times, and is %s."
msgstr ""
#: plugin.py:364
msgid "locked"
msgstr ""
#: plugin.py:364
msgid "not locked"
msgstr ""
#: plugin.py:371
#, docstring
msgid ""
"[<channel>]\n"
"\n"
" Lists regexps present in the triggers database.\n"
" <channel> is only necessary if the message isn't sent in the channel \n"
" itself. Regexp ID listed in paretheses.\n"
" "
msgstr ""
#: plugin.py:384 plugin.py:410
msgid "There are no regexp triggers in the database."
msgstr ""
#: plugin.py:394
#, docstring
msgid ""
"[<channel>]\n"
" \n"
" Returns a list of top-ranked regexps, sorted by usage count \n"
" (rank). The number of regexps returned is set by the \n"
" rankListLength registry value. <channel> is only necessary if the \n"
" message isn't sent in the channel itself.\n"
" "
msgstr ""
#: plugin.py:418
#, docstring
msgid ""
"[<channel>]\n"
" \n"
" Vacuums the database for <channel>.\n"
" See SQLite vacuum doc here: http://www.sqlite.org/lang_vacuum.html\n"
" <channel> is only necessary if the message isn't sent in \n"
" the channel itself.\n"
" First check if user has the required capability specified in plugin \n"
" config requireVacuumCapability.\n"
" "
msgstr ""

View File

@ -0,0 +1,442 @@
###
# Copyright (c) 2010, Daniel Folkinshteyn
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
import supybot.utils as utils
from supybot.commands import *
import supybot.plugins as plugins
import supybot.ircutils as ircutils
import supybot.callbacks as callbacks
import supybot.conf as conf
import supybot.ircdb as ircdb
import re
import os
import time
try:
from supybot.i18n import PluginInternationalization
from supybot.i18n import internationalizeDocstring
_ = PluginInternationalization('MessageParser')
except:
# This are useless functions that's allow to run the plugin on a bot
# without the i18n plugin
_ = lambda x:x
internationalizeDocstring = lambda x:x
#try:
#import sqlite
#except ImportError:
#raise callbacks.Error, 'You need to have PySQLite installed to use this ' \
#'plugin. Download it at ' \
#'<http://code.google.com/p/pysqlite/>'
try:
import sqlite3
except ImportError:
from pysqlite2 import dbapi2 as sqlite3 # for python2.4
# these are needed cuz we are overriding getdb
import threading
import supybot.world as world
import supybot.log as log
class MessageParser(callbacks.Plugin, plugins.ChannelDBHandler):
"""This plugin can set regexp triggers to activate the bot.
Use 'add' command to add regexp trigger, 'remove' to remove."""
threaded = True
def __init__(self, irc):
callbacks.Plugin.__init__(self, irc)
plugins.ChannelDBHandler.__init__(self)
def makeDb(self, filename):
"""Create the database and connect to it."""
if os.path.exists(filename):
db = sqlite3.connect(filename)
db.text_factory = str
return db
db = sqlite3.connect(filename)
db.text_factory = str
cursor = db.cursor()
cursor.execute("""CREATE TABLE triggers (
id INTEGER PRIMARY KEY,
regexp TEXT UNIQUE ON CONFLICT REPLACE,
added_by TEXT,
added_at TIMESTAMP,
usage_count INTEGER,
action TEXT,
locked BOOLEAN
)""")
db.commit()
return db
# override this because sqlite3 doesn't have autocommit
# use isolation_level instead.
def getDb(self, channel):
"""Use this to get a database for a specific channel."""
currentThread = threading.currentThread()
if channel not in self.dbCache and currentThread == world.mainThread:
self.dbCache[channel] = self.makeDb(self.makeFilename(channel))
if currentThread != world.mainThread:
db = self.makeDb(self.makeFilename(channel))
else:
db = self.dbCache[channel]
db.isolation_level = None
return db
def _updateRank(self, channel, regexp):
if self.registryValue('keepRankInfo', channel):
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT usage_count
FROM triggers
WHERE regexp=?""", (regexp,))
old_count = cursor.fetchall()[0][0]
cursor.execute("UPDATE triggers SET usage_count=? WHERE regexp=?", (old_count + 1, regexp,))
db.commit()
def _runCommandFunction(self, irc, msg, command):
"""Run a command from message, as if command was sent over IRC."""
tokens = callbacks.tokenize(command)
try:
self.Proxy(irc.irc, msg, tokens)
except Exception, e:
log.exception('Uncaught exception in function called by MessageParser:')
def _checkManageCapabilities(self, irc, msg, channel):
"""Check if the user has any of the required capabilities to manage
the regexp database."""
capabilities = self.registryValue('requireManageCapability')
if capabilities:
for capability in re.split(r'\s*;\s*', capabilities):
if capability.startswith('channel,'):
capability = ircdb.makeChannelCapability(channel, capability[8:])
if capability and ircdb.checkCapability(msg.prefix, capability):
#print "has capability:", capability
return True
return False
else:
return True
def doPrivmsg(self, irc, msg):
channel = msg.args[0]
if not irc.isChannel(channel):
return
if self.registryValue('enable', channel):
if callbacks.addressed(irc.nick, msg): #message is direct command
return
actions = []
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("SELECT regexp, action FROM triggers")
results = cursor.fetchall()
if len(results) == 0:
return
for (regexp, action) in results:
for match in re.finditer(regexp, msg.args[1]):
if match is not None:
thisaction = action
self._updateRank(channel, regexp)
for (i, j) in enumerate(match.groups()):
thisaction = re.sub(r'\$' + str(i+1), match.group(i+1), thisaction)
actions.append(thisaction)
for action in actions:
self._runCommandFunction(irc, msg, action)
@internationalizeDocstring
def add(self, irc, msg, args, channel, regexp, action):
"""[<channel>] <regexp> <action>
Associates <regexp> with <action>. <channel> is only
necessary if the message isn't sent on the channel
itself. Action is echoed upon regexp match, with variables $1, $2,
etc. being interpolated from the regexp match groups."""
if not self._checkManageCapabilities(irc, msg, channel):
capabilities = self.registryValue('requireManageCapability')
irc.errorNoCapability(capabilities, Raise=True)
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("SELECT id, usage_count, locked FROM triggers WHERE regexp=?", (regexp,))
results = cursor.fetchall()
if len(results) != 0:
(id, usage_count, locked) = map(int, results[0])
else:
locked = 0
usage_count = 0
if not locked:
try:
re.compile(regexp)
except Exception, e:
irc.error(_('Invalid python regexp: %s') % (e,))
return
if ircdb.users.hasUser(msg.prefix):
name = ircdb.users.getUser(msg.prefix).name
else:
name = msg.nick
cursor.execute("""INSERT INTO triggers VALUES
(NULL, ?, ?, ?, ?, ?, ?)""",
(regexp, name, int(time.time()), usage_count, action, locked,))
db.commit()
irc.replySuccess()
else:
irc.error(_('That trigger is locked.'))
return
add = wrap(add, ['channel', 'something', 'something'])
@internationalizeDocstring
def remove(self, irc, msg, args, channel, optlist, regexp):
"""[<channel>] [--id] <regexp>]
Removes the trigger for <regexp> from the triggers database.
<channel> is only necessary if
the message isn't sent in the channel itself.
If option --id specified, will retrieve by regexp id, not content.
"""
if not self._checkManageCapabilities(irc, msg, channel):
capabilities = self.registryValue('requireManageCapability')
irc.errorNoCapability(capabilities, Raise=True)
db = self.getDb(channel)
cursor = db.cursor()
target = 'regexp'
for (option, arg) in optlist:
if option == 'id':
target = 'id'
sql = "SELECT id, locked FROM triggers WHERE %s=?" % (target,)
cursor.execute(sql, (regexp,))
results = cursor.fetchall()
if len(results) != 0:
(id, locked) = map(int, results[0])
else:
irc.error(_('There is no such regexp trigger.'))
return
if locked:
irc.error(_('This regexp trigger is locked.'))
return
cursor.execute("""DELETE FROM triggers WHERE id=?""", (id,))
db.commit()
irc.replySuccess()
remove = wrap(remove, ['channel',
getopts({'id': '',}),
'something'])
@internationalizeDocstring
def lock(self, irc, msg, args, channel, regexp):
"""[<channel>] <regexp>
Locks the <regexp> so that it cannot be
removed or overwritten to. <channel> is only necessary if the message isn't
sent in the channel itself.
"""
if not self._checkManageCapabilities(irc, msg, channel):
capabilities = self.registryValue('requireManageCapability')
irc.errorNoCapability(capabilities, Raise=True)
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("SELECT id FROM triggers WHERE regexp=?", (regexp,))
results = cursor.fetchall()
if len(results) == 0:
irc.error(_('There is no such regexp trigger.'))
return
cursor.execute("UPDATE triggers SET locked=1 WHERE regexp=?", (regexp,))
db.commit()
irc.replySuccess()
lock = wrap(lock, ['channel', 'text'])
@internationalizeDocstring
def unlock(self, irc, msg, args, channel, regexp):
"""[<channel>] <regexp>
Unlocks the entry associated with <regexp> so that it can be
removed or overwritten. <channel> is only necessary if the message isn't
sent in the channel itself.
"""
if not self._checkManageCapabilities(irc, msg, channel):
capabilities = self.registryValue('requireManageCapability')
irc.errorNoCapability(capabilities, Raise=True)
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("SELECT id FROM triggers WHERE regexp=?", (regexp,))
results = cursor.fetchall()
if len(results) == 0:
irc.error(_('There is no such regexp trigger.'))
return
cursor.execute("UPDATE triggers SET locked=0 WHERE regexp=?", (regexp,))
db.commit()
irc.replySuccess()
unlock = wrap(unlock, ['channel', 'text'])
@internationalizeDocstring
def show(self, irc, msg, args, channel, optlist, regexp):
"""[<channel>] [--id] <regexp>
Looks up the value of <regexp> in the triggers database.
<channel> is only necessary if the message isn't sent in the channel
itself.
If option --id specified, will retrieve by regexp id, not content.
"""
db = self.getDb(channel)
cursor = db.cursor()
target = 'regexp'
for (option, arg) in optlist:
if option == 'id':
target = 'id'
sql = "SELECT regexp, action FROM triggers WHERE %s=?" % (target,)
cursor.execute(sql, (regexp,))
results = cursor.fetchall()
if len(results) != 0:
(regexp, action) = results[0]
else:
irc.error(_('There is no such regexp trigger.'))
return
irc.reply("The action for regexp trigger \"%s\" is \"%s\"" % (regexp, action))
show = wrap(show, ['channel',
getopts({'id': '',}),
'something'])
@internationalizeDocstring
def info(self, irc, msg, args, channel, optlist, regexp):
"""[<channel>] [--id] <regexp>
Display information about <regexp> in the triggers database.
<channel> is only necessary if the message isn't sent in the channel
itself.
If option --id specified, will retrieve by regexp id, not content.
"""
db = self.getDb(channel)
cursor = db.cursor()
target = 'regexp'
for (option, arg) in optlist:
if option == 'id':
target = 'id'
sql = "SELECT * FROM triggers WHERE %s=?" % (target,)
cursor.execute(sql, (regexp,))
results = cursor.fetchall()
if len(results) != 0:
(id, regexp, added_by, added_at, usage_count,
action, locked) = results[0]
else:
irc.error(_('There is no such regexp trigger.'))
return
irc.reply(_("The regexp id is %d, regexp is \"%s\", and action is"
" \"%s\". It was added by user %s on %s, has been "
"triggered %d times, and is %s.") % (id,
regexp,
action,
added_by,
time.strftime(conf.supybot.reply.format.time(),
time.localtime(int(added_at))),
usage_count,
locked and _("locked") or _("not locked"),))
info = wrap(info, ['channel',
getopts({'id': '',}),
'something'])
@internationalizeDocstring
def list(self, irc, msg, args, channel):
"""[<channel>]
Lists regexps present in the triggers database.
<channel> is only necessary if the message isn't sent in the channel
itself. Regexp ID listed in paretheses.
"""
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("SELECT regexp, id FROM triggers")
results = cursor.fetchall()
if len(results) != 0:
regexps = results
else:
irc.reply(_('There are no regexp triggers in the database.'))
return
s = [ "\"%s\" (%d)" % (regexp[0], regexp[1]) for regexp in regexps ]
separator = self.registryValue('listSeparator', channel)
irc.reply(separator.join(s))
list = wrap(list, ['channel'])
@internationalizeDocstring
def rank(self, irc, msg, args, channel):
"""[<channel>]
Returns a list of top-ranked regexps, sorted by usage count
(rank). The number of regexps returned is set by the
rankListLength registry value. <channel> is only necessary if the
message isn't sent in the channel itself.
"""
numregexps = self.registryValue('rankListLength', channel)
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT regexp, usage_count
FROM triggers
ORDER BY usage_count DESC
LIMIT ?""", (numregexps,))
regexps = cursor.fetchall()
if len(regexps) == 0:
irc.reply(_('There are no regexp triggers in the database.'))
return
s = [ "#%d \"%s\" (%d)" % (i+1, regexp[0], regexp[1]) for i, regexp in enumerate(regexps) ]
irc.reply(", ".join(s))
rank = wrap(rank, ['channel'])
@internationalizeDocstring
def vacuum(self, irc, msg, args, channel):
"""[<channel>]
Vacuums the database for <channel>.
See SQLite vacuum doc here: http://www.sqlite.org/lang_vacuum.html
<channel> is only necessary if the message isn't sent in
the channel itself.
First check if user has the required capability specified in plugin
config requireVacuumCapability.
"""
capability = self.registryValue('requireVacuumCapability')
if capability:
if not ircdb.checkCapability(msg.prefix, capability):
irc.errorNoCapability(capability, Raise=True)
db = self.getDb(channel)
cursor = db.cursor()
cursor.execute("""VACUUM""")
db.commit()
irc.replySuccess()
vacuum = wrap(vacuum, ['channel'])
MessageParser = internationalizeDocstring(MessageParser)
Class = MessageParser
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79:

View File

@ -0,0 +1,174 @@
###
# Copyright (c) 2010, Daniel Folkinshteyn
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
from supybot.test import *
try:
import sqlite3
except ImportError:
from pysqlite2 import dbapi2 as sqlite3 # for python2.4
class MessageParserTestCase(ChannelPluginTestCase):
plugins = ('MessageParser','Utilities','User')
#utilities for the 'echo'
#user for register for testVacuum
def testAdd(self):
self.assertError('messageparser add') #no args
self.assertError('messageparser add "stuff"') #no action arg
self.assertNotError('messageparser add "stuff" "echo i saw some stuff"')
self.assertRegexp('messageparser show "stuff"', '.*i saw some stuff.*')
self.assertError('messageparser add "[a" "echo stuff"') #invalid regexp
self.assertError('messageparser add "(a" "echo stuff"') #invalid regexp
self.assertNotError('messageparser add "stuff" "echo i saw no stuff"') #overwrite existing regexp
self.assertRegexp('messageparser show "stuff"', '.*i saw no stuff.*')
try:
world.testing = False
origuser = self.prefix
self.prefix = 'stuff!stuff@stuff'
self.assertNotError('register nottester stuff', private=True)
self.assertError('messageparser add "aoeu" "echo vowels are nice"')
origconf = conf.supybot.plugins.MessageParser.requireManageCapability()
conf.supybot.plugins.MessageParser.requireManageCapability.setValue('')
self.assertNotError('messageparser add "aoeu" "echo vowels are nice"')
finally:
world.testing = True
self.prefix = origuser
conf.supybot.plugins.MessageParser.requireManageCapability.setValue(origconf)
def testShow(self):
self.assertNotError('messageparser add "stuff" "echo i saw some stuff"')
self.assertRegexp('messageparser show "nostuff"', 'there is no such regexp trigger')
self.assertRegexp('messageparser show "stuff"', '.*i saw some stuff.*')
self.assertRegexp('messageparser show --id 1', '.*i saw some stuff.*')
def testInfo(self):
self.assertNotError('messageparser add "stuff" "echo i saw some stuff"')
self.assertRegexp('messageparser info "nostuff"', 'there is no such regexp trigger')
self.assertRegexp('messageparser info "stuff"', '.*i saw some stuff.*')
self.assertRegexp('messageparser info --id 1', '.*i saw some stuff.*')
self.assertRegexp('messageparser info "stuff"', 'has been triggered 0 times')
self.feedMsg('this message has some stuff in it')
self.getMsg(' ')
self.assertRegexp('messageparser info "stuff"', 'has been triggered 1 times')
def testTrigger(self):
self.assertNotError('messageparser add "stuff" "echo i saw some stuff"')
self.feedMsg('this message has some stuff in it')
m = self.getMsg(' ')
self.failUnless(str(m).startswith('PRIVMSG #test :i saw some stuff'))
def testLock(self):
self.assertNotError('messageparser add "stuff" "echo i saw some stuff"')
self.assertNotError('messageparser lock "stuff"')
self.assertError('messageparser add "stuff" "echo some other stuff"')
self.assertError('messageparser remove "stuff"')
self.assertRegexp('messageparser info "stuff"', 'is locked')
def testUnlock(self):
self.assertNotError('messageparser add "stuff" "echo i saw some stuff"')
self.assertNotError('messageparser lock "stuff"')
self.assertError('messageparser remove "stuff"')
self.assertNotError('messageparser unlock "stuff"')
self.assertRegexp('messageparser info "stuff"', 'is not locked')
self.assertNotError('messageparser remove "stuff"')
def testRank(self):
self.assertRegexp('messageparser rank',
'There are no regexp triggers in the database\.')
self.assertNotError('messageparser add "stuff" "echo i saw some stuff"')
self.assertRegexp('messageparser rank', '#1 "stuff" \(0\)')
self.assertNotError('messageparser add "aoeu" "echo vowels are nice!"')
self.assertRegexp('messageparser rank', '#1 "stuff" \(0\), #2 "aoeu" \(0\)')
self.feedMsg('instead of asdf, dvorak has aoeu')
self.getMsg(' ')
self.assertRegexp('messageparser rank', '#1 "aoeu" \(1\), #2 "stuff" \(0\)')
def testList(self):
self.assertRegexp('messageparser list',
'There are no regexp triggers in the database\.')
self.assertNotError('messageparser add "stuff" "echo i saw some stuff"')
self.assertRegexp('messageparser list', '"stuff" \(1\)')
self.assertNotError('messageparser add "aoeu" "echo vowels are nice!"')
self.assertRegexp('messageparser list', '"stuff" \(1\), "aoeu" \(2\)')
def testRemove(self):
self.assertError('messageparser remove "stuff"')
self.assertNotError('messageparser add "stuff" "echo i saw some stuff"')
self.assertNotError('messageparser lock "stuff"')
self.assertError('messageparser remove "stuff"')
self.assertNotError('messageparser unlock "stuff"')
self.assertNotError('messageparser remove "stuff"')
self.assertNotError('messageparser add "stuff" "echo i saw some stuff"')
self.assertNotError('messageparser remove --id 1')
def testVacuum(self):
self.assertNotError('messageparser add "stuff" "echo i saw some stuff"')
self.assertNotError('messageparser remove "stuff"')
self.assertNotError('messageparser vacuum')
# disable world.testing since we want new users to not
# magically be endowed with the admin capability
try:
world.testing = False
original = self.prefix
self.prefix = 'stuff!stuff@stuff'
self.assertNotError('register nottester stuff', private=True)
self.assertError('messageparser vacuum')
orig = conf.supybot.plugins.MessageParser.requireVacuumCapability()
conf.supybot.plugins.MessageParser.requireVacuumCapability.setValue('')
self.assertNotError('messageparser vacuum')
finally:
world.testing = True
self.prefix = original
conf.supybot.plugins.MessageParser.requireVacuumCapability.setValue(orig)
def testKeepRankInfo(self):
orig = conf.supybot.plugins.MessageParser.keepRankInfo()
try:
conf.supybot.plugins.MessageParser.keepRankInfo.setValue(False)
self.assertNotError('messageparser add "stuff" "echo i saw some stuff"')
self.feedMsg('instead of asdf, dvorak has aoeu')
self.getMsg(' ')
self.assertRegexp('messageparser info "stuff"', 'has been triggered 0 times')
finally:
conf.supybot.plugins.MessageParser.keepRankInfo.setValue(orig)
self.feedMsg('this message has some stuff in it')
self.getMsg(' ')
self.assertRegexp('messageparser info "stuff"', 'has been triggered 1 times')
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:

View File

@ -199,7 +199,7 @@ msgstr ""
#: plugin.py:373
msgid "I couldn't find a message matching that criteria in my history of %s messages."
msgstr "Je ne peux trovuer de message correspondant à ce critère dans mon historique de %s messages."
msgstr "Je ne peux trouver de message correspondant à ce critère dans mon historique de %s messages."
#: plugin.py:388
msgid ""

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 15:35+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -45,15 +45,15 @@ msgid ""
" command"
msgstr ""
#: plugin.py:81
#: plugin.py:80
msgid "You've given me %s invalid commands within the last minute; I'm now ignoring you for %s."
msgstr ""
#: plugin.py:93
#: plugin.py:92
msgid "The %q plugin is loaded, but there is no command named %q in it. Try \"list %s\" to see the commands in the %q plugin."
msgstr ""
#: plugin.py:119
#: plugin.py:118
#, docstring
msgid ""
"[--private] [<plugin>]\n"
@ -64,19 +64,19 @@ msgid ""
" "
msgstr ""
#: plugin.py:144
#: plugin.py:143
msgid "There are no private plugins."
msgstr ""
#: plugin.py:146
#: plugin.py:145
msgid "There are no public plugins."
msgstr ""
#: plugin.py:153
#: plugin.py:152
msgid "That plugin exists, but has no commands. This probably means that it has some configuration variables that can be changed in order to modify its behavior. Try \"config list supybot.plugins.%s\" to see what configuration variables it has."
msgstr ""
#: plugin.py:164
#: plugin.py:163
#, docstring
msgid ""
"<string>\n"
@ -86,11 +86,11 @@ msgid ""
" "
msgstr ""
#: plugin.py:183
#: plugin.py:182
msgid "No appropriate commands were found."
msgstr ""
#: plugin.py:188
#: plugin.py:187
#, docstring
msgid ""
"[<plugin>] [<command>]\n"
@ -100,15 +100,15 @@ msgid ""
" "
msgstr ""
#: plugin.py:198
#: plugin.py:197
msgid "That command exists in the %L plugins. Please specify exactly which plugin command you want help with."
msgstr ""
#: plugin.py:205
#: plugin.py:204
msgid "There is no command %q."
msgstr ""
#: plugin.py:211
#: plugin.py:210
#, docstring
msgid ""
"takes no arguments\n"
@ -117,19 +117,19 @@ msgid ""
" "
msgstr ""
#: plugin.py:217
#: plugin.py:216
msgid "The newest version available online is %s."
msgstr ""
#: plugin.py:221
#: plugin.py:220
msgid "I couldn't fetch the newest version from the Supybot website."
msgstr ""
#: plugin.py:223
#: plugin.py:222
msgid "The current (running) version of this Supybot is %s. %s"
msgstr ""
#: plugin.py:230
#: plugin.py:229
#, docstring
msgid ""
"takes no arguments\n"
@ -138,11 +138,11 @@ msgid ""
" "
msgstr ""
#: plugin.py:234
#: plugin.py:233
msgid "My source is at http://supybot.com/"
msgstr ""
#: plugin.py:239
#: plugin.py:238
#, docstring
msgid ""
"[<nick>]\n"
@ -154,23 +154,31 @@ msgid ""
" "
msgstr ""
#: plugin.py:253
#: plugin.py:252
msgid "%s has no public mores."
msgstr ""
#: plugin.py:256
#: plugin.py:255
msgid "Sorry, I can't find any mores for %s"
msgstr ""
#: plugin.py:265
#: plugin.py:262
msgid "more message"
msgstr ""
#: plugin.py:264
msgid "more messages"
msgstr ""
#: plugin.py:268
msgid "You haven't asked me a command; perhaps you want to see someone else's more. To do so, call this command with that person's nick."
msgstr ""
#: plugin.py:269
#: plugin.py:272
msgid "That's all, there is no more."
msgstr ""
#: plugin.py:279
#: plugin.py:282
#, docstring
msgid ""
"[--{from,in,on,with,without,regexp} <value>] [--nolimit]\n"
@ -185,11 +193,11 @@ msgid ""
" "
msgstr ""
#: plugin.py:373
#: plugin.py:376
msgid "I couldn't find a message matching that criteria in my history of %s messages."
msgstr ""
#: plugin.py:388
#: plugin.py:391
#, docstring
msgid ""
"<nick> <text>\n"
@ -199,23 +207,23 @@ msgid ""
" "
msgstr ""
#: plugin.py:396
#: plugin.py:399
msgid "Dude, just give the command. No need for the tell."
msgstr ""
#: plugin.py:401
#: plugin.py:404
msgid "You just told me, why should I tell myself?"
msgstr ""
#: plugin.py:406
#: plugin.py:409
msgid "I haven't seen %s, I'll let you do the telling."
msgstr ""
#: plugin.py:411
#: plugin.py:414
msgid "%s wants me to tell you: %s"
msgstr ""
#: plugin.py:417
#: plugin.py:420
#, docstring
msgid ""
"takes no arguments\n"
@ -224,7 +232,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:421
#: plugin.py:424
msgid "pong"
msgstr ""

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-29 12:02+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -28,38 +28,38 @@ msgid ""
" when the 'most' command is called."
msgstr ""
#: plugin.py:292
#: plugin.py:289
#, docstring
msgid ""
"Add the help for \"@help MoobotFactoids\" here (assuming you don't implement a MoobotFactoids\n"
" command). This should describe *how* to use this plugin."
msgstr ""
#: plugin.py:349
#: plugin.py:346
msgid "%s is %s"
msgstr ""
#: plugin.py:368
#: plugin.py:365
msgid "Factoid %q is locked."
msgstr ""
#: plugin.py:375
#: plugin.py:372
msgid "Factoid %q not found."
msgstr ""
#: plugin.py:385
#: plugin.py:382
msgid "Missing an 'is' or '_is_'."
msgstr ""
#: plugin.py:401
#: plugin.py:398
msgid "Factoid %q already exists."
msgstr ""
#: plugin.py:435
#: plugin.py:432
msgid "%s, or %s"
msgstr ""
#: plugin.py:456
#: plugin.py:453
#, docstring
msgid ""
"[<channel>] <factoid key>\n"
@ -70,7 +70,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:469
#: plugin.py:466
#, docstring
msgid ""
"[<channel>] <factoid key>\n"
@ -81,39 +81,39 @@ msgid ""
" "
msgstr ""
#: plugin.py:480 plugin.py:520
#: plugin.py:477 plugin.py:517
msgid "No such factoid: %q"
msgstr ""
#: plugin.py:489
#: plugin.py:486
msgid "Created by %s on %s."
msgstr ""
#: plugin.py:495
#: plugin.py:492
msgid " Last modified by %s on %s."
msgstr ""
#: plugin.py:503
#: plugin.py:500
msgid " Last requested by %s on %s, requested %n."
msgstr ""
#: plugin.py:510
#: plugin.py:507
msgid " Locked by %s on %s."
msgstr ""
#: plugin.py:525
#: plugin.py:522
msgid "Factoid %q is already locked."
msgstr ""
#: plugin.py:528
#: plugin.py:525
msgid "Factoid %q is not locked."
msgstr ""
#: plugin.py:538
#: plugin.py:535
msgid "Cannot %s someone else's factoid unless you are an admin."
msgstr ""
#: plugin.py:550
#: plugin.py:547
#, docstring
msgid ""
"[<channel>] <factoid key>\n"
@ -124,7 +124,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:561
#: plugin.py:558
#, docstring
msgid ""
"[<channel>] <factoid key>\n"
@ -135,7 +135,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:572
#: plugin.py:569
#, docstring
msgid ""
"[<channel>] {popular|authored|recent}\n"
@ -148,51 +148,51 @@ msgid ""
" "
msgstr ""
#: plugin.py:594
#: plugin.py:591
msgid "author"
msgstr ""
#: plugin.py:596
#: plugin.py:593
msgid "authors"
msgstr ""
#: plugin.py:597
#: plugin.py:594
msgid "Most prolific %s: %L"
msgstr ""
#: plugin.py:599 plugin.py:611
#: plugin.py:596 plugin.py:608
msgid "There are no factoids in my database."
msgstr ""
#: plugin.py:606
#: plugin.py:603
msgid "latest factoid"
msgstr ""
#: plugin.py:608
#: plugin.py:605
msgid "latest factoids"
msgstr ""
#: plugin.py:609
#: plugin.py:606
msgid "%s: %L"
msgstr ""
#: plugin.py:618
#: plugin.py:615
msgid "requested factoid"
msgstr ""
#: plugin.py:620
#: plugin.py:617
msgid "requested factoids"
msgstr ""
#: plugin.py:621
#: plugin.py:618
msgid "Top %s: %L"
msgstr ""
#: plugin.py:623
#: plugin.py:620
msgid "No factoids have been requested from my database."
msgstr ""
#: plugin.py:627
#: plugin.py:624
#, docstring
msgid ""
"[<channel>] <author name>\n"
@ -204,15 +204,15 @@ msgid ""
" "
msgstr ""
#: plugin.py:640
#: plugin.py:637
msgid "No factoids by %q found."
msgstr ""
#: plugin.py:643
#: plugin.py:640
msgid "Author search for %q (%i found): %L"
msgstr ""
#: plugin.py:650
#: plugin.py:647
#, docstring
msgid ""
"[<channel>] <text>\n"
@ -223,15 +223,15 @@ msgid ""
" "
msgstr ""
#: plugin.py:658
#: plugin.py:655
msgid "No keys matching %q found."
msgstr ""
#: plugin.py:665
#: plugin.py:662
msgid "Key search for %q (%i found): %L"
msgstr ""
#: plugin.py:672
#: plugin.py:669
#, docstring
msgid ""
"[<channel>] <text>\n"
@ -242,15 +242,15 @@ msgid ""
" "
msgstr ""
#: plugin.py:680
#: plugin.py:677
msgid "No values matching %q found."
msgstr ""
#: plugin.py:683
#: plugin.py:680
msgid "Value search for %q (%i found): %L"
msgstr ""
#: plugin.py:690
#: plugin.py:687
#, docstring
msgid ""
"[<channel>] <factoid key>\n"
@ -260,7 +260,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:703
#: plugin.py:700
#, docstring
msgid ""
"[<channel>]\n"
@ -271,7 +271,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:711
#: plugin.py:708
msgid "No factoids in the database."
msgstr ""

View File

@ -100,19 +100,21 @@ class SqliteMoobotDB(object):
def _getDb(self, channel):
try:
import sqlite
import sqlite3
except ImportError:
raise callbacks.Error, \
'You need to have PySQLite installed to use this ' \
'plugin. Download it at ' \
'<http://code.google.com/p/pysqlite/>'
from pysqlite2 import dbapi2 as sqlite3 # for python2.4
if channel in self.dbs:
return self.dbs[channel]
filename = plugins.makeChannelFilename(self.filename, channel)
if os.path.exists(filename):
self.dbs[channel] = sqlite.connect(filename)
return self.dbs[channel]
db = sqlite.connect(filename)
db = sqlite3.connect(filename)
db.text_factory = str
self.dbs[channel] = db
return db
db = sqlite3.connect(filename)
db.text_factory = str
self.dbs[channel] = db
cursor = db.cursor()
cursor.execute("""CREATE TABLE factoids (
@ -135,11 +137,12 @@ class SqliteMoobotDB(object):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT fact FROM factoids
WHERE key LIKE %s""", key)
if cursor.rowcount == 0:
WHERE key LIKE ?""", (key,))
results = cursor.fetchall()
if len(results) == 0:
return None
else:
return cursor.fetchall()[0]
return results[0]
def getFactinfo(self, channel, key):
db = self._getDb(channel)
@ -149,63 +152,65 @@ class SqliteMoobotDB(object):
last_requested_by, last_requested_at,
requested_count, locked_by, locked_at
FROM factoids
WHERE key LIKE %s""", key)
if cursor.rowcount == 0:
WHERE key LIKE ?""", (key,))
results = cursor.fetchall()
if len(results) == 0:
return None
else:
return cursor.fetchone()
return results[0]
def randomFactoid(self, channel):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT fact, key FROM factoids
ORDER BY random() LIMIT 1""")
if cursor.rowcount == 0:
results = cursor.fetchall()
if len(results) == 0:
return None
else:
return cursor.fetchone()
return results[0]
def addFactoid(self, channel, key, value, creator_id):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("""INSERT INTO factoids VALUES
(%s, %s, %s, NULL, NULL, NULL, NULL,
NULL, NULL, %s, 0)""",
key, creator_id, int(time.time()), value)
(?, ?, ?, NULL, NULL, NULL, NULL,
NULL, NULL, ?, 0)""",
(key, creator_id, int(time.time()), value))
db.commit()
def updateFactoid(self, channel, key, newvalue, modifier_id):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("""UPDATE factoids
SET fact=%s, modified_by=%s,
modified_at=%s WHERE key LIKE %s""",
newvalue, modifier_id, int(time.time()), key)
SET fact=?, modified_by=?,
modified_at=? WHERE key LIKE ?""",
(newvalue, modifier_id, int(time.time()), key))
db.commit()
def updateRequest(self, channel, key, hostmask):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("""UPDATE factoids SET
last_requested_by = %s,
last_requested_at = %s,
last_requested_by = ?,
last_requested_at = ?,
requested_count = requested_count + 1
WHERE key = %s""",
hostmask, int(time.time()), key)
WHERE key = ?""",
(hostmask, int(time.time()), key))
db.commit()
def removeFactoid(self, channel, key):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("""DELETE FROM factoids WHERE key LIKE %s""",
key)
cursor.execute("""DELETE FROM factoids WHERE key LIKE ?""",
(key,))
db.commit()
def locked(self, channel, key):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute ("""SELECT locked_by FROM factoids
WHERE key LIKE %s""", key)
WHERE key LIKE ?""", (key,))
if cursor.fetchone()[0] is None:
return False
else:
@ -215,17 +220,17 @@ class SqliteMoobotDB(object):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("""UPDATE factoids
SET locked_by=%s, locked_at=%s
WHERE key LIKE %s""",
locker_id, int(time.time()), key)
SET locked_by=?, locked_at=?
WHERE key LIKE ?""",
(locker_id, int(time.time()), key))
db.commit()
def unlock(self, channel, key):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("""UPDATE factoids
SET locked_by=%s, locked_at=%s
WHERE key LIKE %s""", None, None, key)
SET locked_by=?, locked_at=?
WHERE key LIKE ?""", (None, None, key))
db.commit()
def mostAuthored(self, channel, limit):
@ -233,14 +238,14 @@ class SqliteMoobotDB(object):
cursor = db.cursor()
cursor.execute("""SELECT created_by, count(key) FROM factoids
GROUP BY created_by
ORDER BY count(key) DESC LIMIT %s""", limit)
ORDER BY count(key) DESC LIMIT ?""", (limit,))
return cursor.fetchall()
def mostRecent(self, channel, limit):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT key FROM factoids
ORDER BY created_at DESC LIMIT %s""", limit)
ORDER BY created_at DESC LIMIT ?""", (limit,))
return cursor.fetchall()
def mostPopular(self, channel, limit):
@ -248,43 +253,35 @@ class SqliteMoobotDB(object):
cursor = db.cursor()
cursor.execute("""SELECT key, requested_count FROM factoids
WHERE requested_count > 0
ORDER BY requested_count DESC LIMIT %s""", limit)
if cursor.rowcount == 0:
return []
else:
return cursor.fetchall()
ORDER BY requested_count DESC LIMIT ?""", (limit,))
results = cursor.fetchall()
return results
def getKeysByAuthor(self, channel, authorId):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT key FROM factoids WHERE created_by=%s
ORDER BY key""", authorId)
if cursor.rowcount == 0:
return []
else:
return cursor.fetchall()
cursor.execute("""SELECT key FROM factoids WHERE created_by=?
ORDER BY key""", (authorId,))
results = cursor.fetchall()
return results
def getKeysByGlob(self, channel, glob):
db = self._getDb(channel)
cursor = db.cursor()
glob = '%%%s%%' % glob
cursor.execute("""SELECT key FROM factoids WHERE key LIKE %s
ORDER BY key""", glob)
if cursor.rowcount == 0:
return []
else:
return cursor.fetchall()
cursor.execute("""SELECT key FROM factoids WHERE key LIKE ?
ORDER BY key""", (glob,))
results = cursor.fetchall()
return results
def getKeysByValueGlob(self, channel, glob):
db = self._getDb(channel)
cursor = db.cursor()
glob = '%%%s%%' % glob
cursor.execute("""SELECT key FROM factoids WHERE fact LIKE %s
ORDER BY key""", glob)
if cursor.rowcount == 0:
return []
else:
return cursor.fetchall()
cursor.execute("""SELECT key FROM factoids WHERE fact LIKE ?
ORDER BY key""", (glob,))
results = cursor.fetchall()
return results
MoobotDB = plugins.DB('MoobotFactoids', {'sqlite': SqliteMoobotDB})

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-16 12:52+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 16:53+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 16:58+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -27,7 +27,7 @@ msgid ""
" will check whether its nick ISON."
msgstr ""
#: plugin.py:41
#: plugin.py:40
#, docstring
msgid ""
"This module constantly tries to take whatever nick is configured as\n"
@ -35,7 +35,7 @@ msgid ""
" will do the rest."
msgstr ""
#: plugin.py:90
#: plugin.py:89
#, docstring
msgid "This is returned by the ISON command."
msgstr ""

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 18:28+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-29 13:54+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -15,7 +15,7 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n"
#: plugin.py:179
#: plugin.py:181
#, docstring
msgid ""
"<recipient>,[<recipient>,[...]] <text>\n"
@ -25,7 +25,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:195
#: plugin.py:197
#, docstring
msgid ""
"<id> <text>\n"
@ -34,7 +34,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:219
#: plugin.py:221
#, docstring
msgid ""
"<id>\n"
@ -44,7 +44,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:251
#: plugin.py:253
#, docstring
msgid ""
"<id>\n"
@ -54,7 +54,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:281
#: plugin.py:283
#, docstring
msgid ""
"[--{regexp} <value>] [--sent] [<glob>]\n"
@ -65,7 +65,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:320
#: plugin.py:322
#, docstring
msgid ""
"[--{old,sent}] [--{from,to} <user>]\n"
@ -77,7 +77,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:361
#: plugin.py:363
#, docstring
msgid ""
"takes no arguments\n"

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-29 14:47+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -15,7 +15,7 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n"
#: plugin.py:270
#: plugin.py:273
#, docstring
msgid ""
"<text>\n"
@ -25,7 +25,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:280
#: plugin.py:283
#, docstring
msgid ""
"<text>\n"
@ -35,7 +35,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:295
#: plugin.py:298
#, docstring
msgid ""
"[--remove] <command> [<plugin>]\n"
@ -47,7 +47,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:333
#: plugin.py:336
#, docstring
msgid ""
"<string to be sent to the server>\n"
@ -56,7 +56,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:347
#: plugin.py:350
#, docstring
msgid ""
"[<text>]\n"
@ -67,7 +67,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:363
#: plugin.py:366
#, docstring
msgid ""
"takes no arguments\n"
@ -77,7 +77,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:373
#: plugin.py:376
#, docstring
msgid ""
"[<level>]\n"
@ -89,7 +89,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:412
#: plugin.py:415
#, docstring
msgid ""
"[--deprecated] <plugin>\n"
@ -101,7 +101,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:447
#: plugin.py:450
#, docstring
msgid ""
"<plugin>\n"
@ -111,7 +111,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:476
#: plugin.py:479
#, docstring
msgid ""
"<plugin>\n"
@ -122,7 +122,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:500
#: plugin.py:503
#, docstring
msgid ""
"{add|remove} <capability>\n"
@ -133,7 +133,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:525
#: plugin.py:528
#, docstring
msgid ""
"[<plugin>] <command>\n"
@ -146,7 +146,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:552
#: plugin.py:555
#, docstring
msgid ""
"[<plugin>] <command>\n"
@ -157,7 +157,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:571
#: plugin.py:574
#, docstring
msgid ""
"<plugin> <command> <new name>\n"
@ -166,7 +166,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:588
#: plugin.py:591
#, docstring
msgid ""
"<plugin>\n"
@ -176,3 +176,11 @@ msgid ""
" "
msgstr ""
#: plugin.py:604
#, docstring
msgid ""
"takes no argument\n"
"\n"
" Reloads the locale of the bot."
msgstr ""

View File

@ -435,7 +435,7 @@ class Owner(callbacks.Plugin):
'to force it to load.' % name.capitalize())
return
except ImportError, e:
if name in str(e):
if str(e).endswith(' ' + name):
irc.error('No plugin named %s exists.' % utils.str.dqrepr(name))
else:
irc.error(str(e))

View File

@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: Supybot-fr\n"
"POT-Creation-Date: 2010-10-16 13:50+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\n"
"PO-Revision-Date: \n"
"Last-Translator: Valentin Lorentz <progval@gmail.com>\n"
"Language-Team: Supybot-fr <progval@gmail.com>\n"
@ -13,7 +13,7 @@ msgstr ""
"X-Poedit-Country: France\n"
"X-Poedit-SourceCharset: ASCII\n"
#: plugin.py:43
#: plugin.py:42
msgid ""
"This plugin exists to help users manage their plugins. Use 'plugin\n"
" list' to list the loaded plugins; use 'plugin help' to get the description\n"
@ -21,7 +21,7 @@ msgid ""
" command exists in."
msgstr "Ce plugin existe pour aider les utilisateurs à gérer leurs plugins. Utilisez 'list' pour liser les plugins chargés ; utilisez 'help' pour avoir de l'aide quant à d'autres plugins ; utilisez la commande 'plugin' elle-même pour déterminer dans quel plugin une commande existe."
#: plugin.py:49
#: plugin.py:48
msgid ""
"<plugin>\n"
"\n"
@ -33,11 +33,11 @@ msgstr ""
"\n"
"Retourne une description utile de comment utiliser le <plugin>, si le plugin en a une."
#: plugin.py:58
#: plugin.py:57
msgid "That plugin is loaded, but has no plugin help."
msgstr "Ce plugin est chargé mais n'a pas d'aide."
#: plugin.py:63
#: plugin.py:62
msgid ""
"takes no arguments\n"
"\n"
@ -48,34 +48,48 @@ msgstr ""
"\n"
"Retourne une liste des plugins actuellement chargés."
#: plugin.py:74
#: plugin.py:73
msgid ""
"<command>\n"
"\n"
" Returns the plugin(s) that <command> is in.\n"
" Returns the name of the plugin that would be used to call <command>.\n"
" \n"
" If it is not uniquely determined, returns list of all plugins that\n"
" contain <command>.\n"
" "
msgstr ""
"<commande>\n"
"\n"
"Retourne le(s) plugin(s) ayant la <commande>."
"Retourne le nom du plugin qui serait utilisé pour appeller la <commande>.Si il ne peut être déterminé (c'est à dire s'il y a un conflit, sans plugin par défaut), la liste de tous les plugins contenant cette commande sera renvoyée."
#: plugin.py:89
#: plugin.py:91
msgid "plugins"
msgstr "plugins"
#: plugin.py:91
#: plugin.py:93
msgid "plugin"
msgstr "plugin"
#: plugin.py:92
#: plugin.py:94
msgid "The %q command is available in the %L %s."
msgstr "La commande %q est disponibles dans le(s) plugin(s) %L%v."
#: plugin.py:95
#: plugin.py:97
msgid "There is no command %q."
msgstr "Il n'y a pas de commande %q."
#: plugin.py:100
#: plugin.py:113
msgid ""
"<command>\n"
"\n"
" Returns the names of all plugins that contain <command>.\n"
" "
msgstr ""
"<commande>\n"
"\n"
"Retourne les noms de tous les plugins contenant la <commande>."
#: plugin.py:135
msgid ""
"<plugin>\n"
"\n"
@ -87,15 +101,15 @@ msgstr ""
"\n"
"Retourne l'auteur du <plugin>. C'est la personne à qui vous devriez parler si vous avez des idées, suggestions, ou d'autres commentaires à propos d'un plugin donné."
#: plugin.py:106
#: plugin.py:141
msgid "That plugin does not seem to be loaded."
msgstr "Ce plugin ne semble pas être chargé."
#: plugin.py:112
#: plugin.py:147
msgid "That plugin doesn't have an author that claims it."
msgstr "Ce plugin n'a pas d'auteur."
#: plugin.py:117
#: plugin.py:152
msgid ""
"<plugin> [<nick>]\n"
"\n"
@ -109,7 +123,7 @@ msgstr ""
"\n"
"Renvoie une liste des personnes ayant contribué à un plugin donné. Si le <nick> est spécifié, les contributions de cette personne seront lisées. Note : <nick> est la partie entre parenthèses lors du listing des personnes."
#: plugin.py:125
#: plugin.py:160
msgid ""
"\n"
" Take an Authors object, and return only the name and nick values\n"
@ -117,7 +131,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:131
#: plugin.py:166
msgid ""
"\n"
" Take a list of long names and turn it into :\n"
@ -125,7 +139,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:138
#: plugin.py:173
msgid ""
"\n"
" Sort the list of 'long names' based on the number of contributions\n"
@ -133,7 +147,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:148
#: plugin.py:183
msgid ""
"\n"
" Build the list of author + contributors (if any) for the requested\n"
@ -141,39 +155,39 @@ msgid ""
" "
msgstr ""
#: plugin.py:152
#: plugin.py:187
msgid "The %s plugin"
msgstr "Le plugin s"
#: plugin.py:153
#: plugin.py:188
msgid "has not been claimed by an author"
msgstr "n'a aucun auteur"
#: plugin.py:154
#: plugin.py:189
msgid "and"
msgstr "et"
#: plugin.py:155
#: plugin.py:190
msgid "has no contributors listed."
msgstr "n'a pas de contributeur listé."
#: plugin.py:160
#: plugin.py:195
msgid "was written by %s"
msgstr "a été écrit par %s"
#: plugin.py:171
#: plugin.py:206
msgid "%s %h contributed to it."
msgstr "%s y %h contribué."
#: plugin.py:176
#: plugin.py:211
msgid "has no additional contributors listed."
msgstr "n'a pas d'autre contributeur listé."
#: plugin.py:178
#: plugin.py:213
msgid "but"
msgstr "mais"
#: plugin.py:181
#: plugin.py:216
msgid ""
"\n"
" Build the list of contributions (if any) for the requested person\n"
@ -181,39 +195,39 @@ msgid ""
" "
msgstr ""
#: plugin.py:195
#: plugin.py:230
msgid "The nick specified (%s) is not a registered contributor."
msgstr "Le nick spécifié(%s) n'est pas un contributeur enregistré."
#: plugin.py:201
#: plugin.py:236
msgid "The %s plugin does not have '%s' listed as a contributor."
msgstr "Le plugin %s n'a pas '%s' listé comme contributeur."
#: plugin.py:209
#: plugin.py:244
msgid "command"
msgstr "commande"
#: plugin.py:212
#: plugin.py:247
msgid "the %L %s"
msgstr "La/les commande(s) %L%v"
#: plugin.py:214
#: plugin.py:249
msgid "the %L"
msgstr "La/les %L"
#: plugin.py:217
#: plugin.py:252
msgid "%s wrote the %s plugin and also contributed %L."
msgstr "%s a écrit le plugin %s et a aussi contribué à %L"
#: plugin.py:220
#: plugin.py:255
msgid "%s contributed %L to the %s plugin."
msgstr "%s a contribué à %L et au plugin %s"
#: plugin.py:223
#: plugin.py:258
msgid "%s wrote the %s plugin"
msgstr "%s a écrit le plugin %s"
#: plugin.py:226
#: plugin.py:261
msgid "%s has no listed contributions for the %s plugin."
msgstr "%s n'a pas de contribution listée pour le plugin %s."

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-16 13:50+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -15,7 +15,7 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n"
#: plugin.py:43
#: plugin.py:42
#, docstring
msgid ""
"This plugin exists to help users manage their plugins. Use 'plugin\n"
@ -24,7 +24,7 @@ msgid ""
" command exists in."
msgstr ""
#: plugin.py:49
#: plugin.py:48
#, docstring
msgid ""
"<plugin>\n"
@ -34,11 +34,11 @@ msgid ""
" "
msgstr ""
#: plugin.py:58
#: plugin.py:57
msgid "That plugin is loaded, but has no plugin help."
msgstr ""
#: plugin.py:63
#: plugin.py:62
#, docstring
msgid ""
"takes no arguments\n"
@ -47,32 +47,44 @@ msgid ""
" "
msgstr ""
#: plugin.py:74
#: plugin.py:73
#, docstring
msgid ""
"<command>\n"
"\n"
" Returns the plugin(s) that <command> is in.\n"
" Returns the name of the plugin that would be used to call <command>.\n"
" \n"
" If it is not uniquely determined, returns list of all plugins that\n"
" contain <command>.\n"
" "
msgstr ""
#: plugin.py:89
#: plugin.py:91
msgid "plugins"
msgstr ""
#: plugin.py:91
#: plugin.py:93
msgid "plugin"
msgstr ""
#: plugin.py:92
#: plugin.py:94
msgid "The %q command is available in the %L %s."
msgstr ""
#: plugin.py:95
#: plugin.py:97
msgid "There is no command %q."
msgstr ""
#: plugin.py:100
#: plugin.py:113
#, docstring
msgid ""
"<command>\n"
"\n"
" Returns the names of all plugins that contain <command>.\n"
" "
msgstr ""
#: plugin.py:135
#, docstring
msgid ""
"<plugin>\n"
@ -82,15 +94,15 @@ msgid ""
" "
msgstr ""
#: plugin.py:106
#: plugin.py:141
msgid "That plugin does not seem to be loaded."
msgstr ""
#: plugin.py:112
#: plugin.py:147
msgid "That plugin doesn't have an author that claims it."
msgstr ""
#: plugin.py:117
#: plugin.py:152
#, docstring
msgid ""
"<plugin> [<nick>]\n"
@ -102,7 +114,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:125
#: plugin.py:160
#, docstring
msgid ""
"\n"
@ -111,7 +123,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:131
#: plugin.py:166
#, docstring
msgid ""
"\n"
@ -120,7 +132,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:138
#: plugin.py:173
#, docstring
msgid ""
"\n"
@ -129,7 +141,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:148
#: plugin.py:183
#, docstring
msgid ""
"\n"
@ -138,39 +150,39 @@ msgid ""
" "
msgstr ""
#: plugin.py:152
#: plugin.py:187
msgid "The %s plugin"
msgstr ""
#: plugin.py:153
#: plugin.py:188
msgid "has not been claimed by an author"
msgstr ""
#: plugin.py:154
#: plugin.py:189
msgid "and"
msgstr ""
#: plugin.py:155
#: plugin.py:190
msgid "has no contributors listed."
msgstr ""
#: plugin.py:160
#: plugin.py:195
msgid "was written by %s"
msgstr ""
#: plugin.py:171
#: plugin.py:206
msgid "%s %h contributed to it."
msgstr ""
#: plugin.py:176
#: plugin.py:211
msgid "has no additional contributors listed."
msgstr ""
#: plugin.py:178
#: plugin.py:213
msgid "but"
msgstr ""
#: plugin.py:181
#: plugin.py:216
#, docstring
msgid ""
"\n"
@ -179,39 +191,39 @@ msgid ""
" "
msgstr ""
#: plugin.py:195
#: plugin.py:230
msgid "The nick specified (%s) is not a registered contributor."
msgstr ""
#: plugin.py:201
#: plugin.py:236
msgid "The %s plugin does not have '%s' listed as a contributor."
msgstr ""
#: plugin.py:209
#: plugin.py:244
msgid "command"
msgstr ""
#: plugin.py:212
#: plugin.py:247
msgid "the %L %s"
msgstr ""
#: plugin.py:214
#: plugin.py:249
msgid "the %L"
msgstr ""
#: plugin.py:217
#: plugin.py:252
msgid "%s wrote the %s plugin and also contributed %L."
msgstr ""
#: plugin.py:220
#: plugin.py:255
msgid "%s contributed %L to the %s plugin."
msgstr ""
#: plugin.py:223
#: plugin.py:258
msgid "%s wrote the %s plugin"
msgstr ""
#: plugin.py:226
#: plugin.py:261
msgid "%s has no listed contributions for the %s plugin."
msgstr ""

View File

@ -72,7 +72,10 @@ class Plugin(callbacks.Plugin):
def plugin(self, irc, msg, args, command):
"""<command>
Returns the plugin(s) that <command> is in.
Returns the name of the plugin that would be used to call <command>.
If it is not uniquely determined, returns list of all plugins that
contain <command>.
"""
(maxL, cbs) = irc.findCallbacksForArgs(command)
L = []
@ -94,7 +97,39 @@ class Plugin(callbacks.Plugin):
irc.error(format(_('There is no command %q.'), command))
plugin = wrap(plugin, [many('something')])
def _findCallbacks(self, irc, command):
command = map(callbacks.canonicalName, command)
plugin_list = []
for cb in irc.callbacks:
if not hasattr(cb, 'getCommand'):
continue
commandlist = cb.getCommand(command)
if commandlist:
plugin_list.append(cb.name())
return plugin_list
@internationalizeDocstring
def plugins(self, irc, msg, args, command):
"""<command>
Returns the names of all plugins that contain <command>.
"""
L = self._findCallbacks(irc, command)
command = callbacks.formatCommand(command)
if L:
if irc.nested:
irc.reply(format('%L', L))
else:
if len(L) > 1:
plugin = 'plugins'
else:
plugin = 'plugin'
irc.reply(format('The %q command is available in the %L %s.',
command, L, plugin))
else:
irc.error(format('There is no command %q.', command))
plugins = wrap(plugins, [many('something')])
def author(self, irc, msg, args, cb):
"""<plugin>

View File

@ -30,11 +30,15 @@
from supybot.test import *
class PluginTestCase(PluginTestCase):
plugins = ('Plugin', 'Utilities')
plugins = ('Plugin', 'Utilities', 'Admin', 'Format')
def testPlugin(self):
self.assertRegexp('plugin plugin', 'available.*Plugin plugin')
self.assertResponse('echo [plugin plugin]', 'Plugin')
def testPlugins(self):
self.assertRegexp('plugins join', '(Format.*Admin|Admin.*Format)')
self.assertRegexp('plugins plugin', 'Plugin')
def testList(self):
self.assertRegexp('plugin list', 'Plugin.*Utilities')

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 18:33+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -21,7 +21,7 @@ msgid ""
" a praise when the praise is given."
msgstr ""
#: plugin.py:40
#: plugin.py:39
#, docstring
msgid ""
"Praise is a plugin for ... well, praising things. Feel free to add\n"
@ -31,11 +31,11 @@ msgid ""
" "
msgstr ""
#: plugin.py:54
#: plugin.py:53
msgid "Praises must contain $who."
msgstr ""
#: plugin.py:58
#: plugin.py:57
#, docstring
msgid ""
"[<channel>] [<id>] <who|what> [for <reason>]\n"
@ -46,15 +46,15 @@ msgid ""
" "
msgstr ""
#: plugin.py:74
#: plugin.py:73
msgid "There is no praise with id #%i."
msgstr ""
#: plugin.py:79
#: plugin.py:78
msgid "There are no praises in my database for %s."
msgstr ""
#: plugin.py:87
#: plugin.py:86
msgid " for "
msgstr ""

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 18:34+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 18:34+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2010-10-17 18:36+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\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"
@ -44,16 +44,16 @@ msgid ""
" random grabbing."
msgstr ""
#: plugin.py:57
#: plugin.py:66
msgid "%s (Said by: %s; grabbed by %s at %t)"
msgstr ""
#: plugin.py:210
#: plugin.py:226
#, docstring
msgid "Add the help for \"@help QuoteGrabs\" here."
msgstr ""
#: plugin.py:249
#: plugin.py:265
#, docstring
msgid ""
"[<channel>] <nick>\n"
@ -64,15 +64,15 @@ msgid ""
" "
msgstr ""
#: plugin.py:262
#: plugin.py:278
msgid "You can't quote grab yourself."
msgstr ""
#: plugin.py:269
#: plugin.py:285
msgid "I couldn't find a proper message to grab."
msgstr ""
#: plugin.py:274
#: plugin.py:290
#, docstring
msgid ""
"[<channel>] <number>\n"
@ -83,15 +83,15 @@ msgid ""
" "
msgstr ""
#: plugin.py:285
#: plugin.py:301
msgid "Nothing to ungrab."
msgstr ""
#: plugin.py:287
#: plugin.py:303
msgid "Invalid grab number."
msgstr ""
#: plugin.py:292
#: plugin.py:308
#, docstring
msgid ""
"[<channel>] <nick>\n"
@ -101,11 +101,11 @@ msgid ""
" "
msgstr ""
#: plugin.py:300
#: plugin.py:316
msgid "I couldn't find a matching quotegrab for %s."
msgstr ""
#: plugin.py:306
#: plugin.py:322
#, docstring
msgid ""
"[<channel>] <nick>\n"
@ -117,11 +117,11 @@ msgid ""
" "
msgstr ""
#: plugin.py:323
#: plugin.py:339
msgid "I couldn't find any quotegrabs for %s."
msgstr ""
#: plugin.py:329
#: plugin.py:345
#, docstring
msgid ""
"[<channel>] [<nick>]\n"
@ -132,15 +132,15 @@ msgid ""
" "
msgstr ""
#: plugin.py:339
#: plugin.py:355
msgid "Couldn't get a random quote for that nick."
msgstr ""
#: plugin.py:341
#: plugin.py:357
msgid "Couldn't get a random quote. Are there any grabbed quotes in the database?"
msgstr ""
#: plugin.py:347
#: plugin.py:363
#, docstring
msgid ""
"[<channel>] <id>\n"
@ -150,11 +150,11 @@ msgid ""
" "
msgstr ""
#: plugin.py:355
#: plugin.py:371
msgid "No quotegrab for id %s"
msgstr ""
#: plugin.py:361
#: plugin.py:377
#, docstring
msgid ""
"[<channel>] <text>\n"
@ -164,7 +164,7 @@ msgid ""
" "
msgstr ""
#: plugin.py:376
#: plugin.py:392
msgid "No quotegrabs matching %s"
msgstr ""

View File

@ -43,6 +43,15 @@ import supybot.callbacks as callbacks
from supybot.i18n import PluginInternationalization, internationalizeDocstring
_ = PluginInternationalization('QuoteGrabs')
try:
import sqlite3
except ImportError:
from pysqlite2 import dbapi2 as sqlite3 # for python2.4
import traceback
#sqlite3.register_converter('bool', bool)
class QuoteGrabsRecord(dbi.Record):
__fields__ = [
'by',
@ -67,29 +76,27 @@ class SqliteQuoteGrabsDB(object):
db.close()
def _getDb(self, channel):
try:
import sqlite
except ImportError:
raise callbacks.Error, 'You need to have PySQLite installed to ' \
'use QuoteGrabs. Download it at ' \
'<http://code.google.com/p/pysqlite/>'
filename = plugins.makeChannelFilename(self.filename, channel)
def p(s1, s2):
return int(ircutils.nickEqual(s1, s2))
# text_factory seems to only apply as an output adapter,
# so doesn't apply to created functions; so we use str()
return ircutils.nickEqual(str(s1), str(s2))
if filename in self.dbs:
return self.dbs[filename]
if os.path.exists(filename):
self.dbs[filename] = sqlite.connect(filename,
converters={'bool': bool})
self.dbs[filename].create_function('nickeq', 2, p)
return self.dbs[filename]
db = sqlite.connect(filename, converters={'bool': bool})
db = sqlite3.connect(filename)
db.text_factory = str
db.create_function('nickeq', 2, p)
self.dbs[filename] = db
return db
db = sqlite3.connect(filename)
db.text_factory = str
db.create_function('nickeq', 2, p)
self.dbs[filename] = db
self.dbs[filename].create_function('nickeq', 2, p)
cursor = db.cursor()
cursor.execute("""CREATE TABLE quotegrabs (
id INTEGER PRIMARY KEY,
nick TEXT,
nick BLOB,
hostmask TEXT,
added_by TEXT,
added_at TIMESTAMP,
@ -102,10 +109,11 @@ class SqliteQuoteGrabsDB(object):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT id, nick, quote, hostmask, added_at, added_by
FROM quotegrabs WHERE id = %s""", id)
if cursor.rowcount == 0:
FROM quotegrabs WHERE id = ?""", (id,))
results = cursor.fetchall()
if len(results) == 0:
raise dbi.NoRecordError
(id, by, quote, hostmask, at, grabber) = cursor.fetchone()
(id, by, quote, hostmask, at, grabber) = results[0]
return QuoteGrabsRecord(id, by=by, text=quote, hostmask=hostmask,
at=int(at), grabber=grabber)
@ -114,46 +122,50 @@ class SqliteQuoteGrabsDB(object):
cursor = db.cursor()
if nick:
cursor.execute("""SELECT quote FROM quotegrabs
WHERE nickeq(nick, %s)
WHERE nickeq(nick, ?)
ORDER BY random() LIMIT 1""",
nick)
(nick,))
else:
cursor.execute("""SELECT quote FROM quotegrabs
ORDER BY random() LIMIT 1""")
if cursor.rowcount == 0:
results = cursor.fetchall()
if len(results) == 0:
raise dbi.NoRecordError
return cursor.fetchone()[0]
return results[0][0]
def list(self, channel, nick):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT id, quote FROM quotegrabs
WHERE nickeq(nick, %s)
ORDER BY id DESC""", nick)
if cursor.rowcount == 0:
WHERE nickeq(nick, ?)
ORDER BY id DESC""", (nick,))
results = cursor.fetchall()
if len(results) == 0:
raise dbi.NoRecordError
return [QuoteGrabsRecord(id, text=quote)
for (id, quote) in cursor.fetchall()]
for (id, quote) in results]
def getQuote(self, channel, nick):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT quote FROM quotegrabs
WHERE nickeq(nick, %s)
ORDER BY id DESC LIMIT 1""", nick)
if cursor.rowcount == 0:
WHERE nickeq(nick, ?)
ORDER BY id DESC LIMIT 1""", (nick,))
results = cursor.fetchall()
if len(results) == 0:
raise dbi.NoRecordError
return cursor.fetchone()[0]
return results[0][0]
def select(self, channel, nick):
db = self._getDb(channel)
cursor = db.cursor()
cursor.execute("""SELECT added_at FROM quotegrabs
WHERE nickeq(nick, %s)
ORDER BY id DESC LIMIT 1""", nick)
if cursor.rowcount == 0:
WHERE nickeq(nick, ?)
ORDER BY id DESC LIMIT 1""", (nick,))
results = cursor.fetchall()
if len(results) == 0:
raise dbi.NoRecordError
return cursor.fetchone()[0]
return results[0][0]
def add(self, channel, msg, by):
db = self._getDb(channel)
@ -161,14 +173,15 @@ class SqliteQuoteGrabsDB(object):
text = ircmsgs.prettyPrint(msg)
# Check to see if the latest quotegrab is identical
cursor.execute("""SELECT quote FROM quotegrabs
WHERE nick=%s
ORDER BY id DESC LIMIT 1""", msg.nick)
if cursor.rowcount != 0:
if text == cursor.fetchone()[0]:
WHERE nick=?
ORDER BY id DESC LIMIT 1""", (msg.nick,))
results = cursor.fetchall()
if len(results) != 0:
if text == results[0][0]:
return
cursor.execute("""INSERT INTO quotegrabs
VALUES (NULL, %s, %s, %s, %s, %s)""",
msg.nick, msg.prefix, by, int(time.time()), text)
VALUES (NULL, ?, ?, ?, ?, ?)""",
(msg.nick, msg.prefix, by, int(time.time()), text,))
db.commit()
def remove(self, channel, grab=None):
@ -179,14 +192,16 @@ class SqliteQuoteGrabsDB(object):
# strictly unnecessary -- the DELETE operation would "succeed"
# anyway, but it's silly to just keep saying 'OK' no matter what,
# so...
cursor.execute("""SELECT * FROM quotegrabs WHERE id = %s""", grab)
if cursor.rowcount == 0:
cursor.execute("""SELECT * FROM quotegrabs WHERE id = ?""", (grab,))
results = cursor.fetchall()
if len(results) == 0:
raise dbi.NoRecordError
cursor.execute("""DELETE FROM quotegrabs WHERE id = %s""", grab)
cursor.execute("""DELETE FROM quotegrabs WHERE id = ?""", (grab,))
else:
cursor.execute("""SELECT * FROM quotegrabs WHERE id = (SELECT MAX(id)
FROM quotegrabs)""")
if cursor.rowcount == 0:
results = cursor.fetchall()
if len(results) == 0:
raise dbi.NoRecordError
cursor.execute("""DELETE FROM quotegrabs WHERE id = (SELECT MAX(id)
FROM quotegrabs)""")
@ -197,14 +212,15 @@ class SqliteQuoteGrabsDB(object):
cursor = db.cursor()
text = '%' + text + '%'
cursor.execute("""SELECT id, nick, quote FROM quotegrabs
WHERE quote LIKE %s
ORDER BY id DESC""", text)
if cursor.rowcount == 0:
WHERE quote LIKE ?
ORDER BY id DESC""", (text,))
results = cursor.fetchall()
if len(results) == 0:
raise dbi.NoRecordError
return [QuoteGrabsRecord(id, text=quote, by=nick)
for (id, nick, quote) in cursor.fetchall()]
for (id, nick, quote) in results]
QuoteGrabsDB = plugins.DB('QuoteGrabs', {'sqlite': SqliteQuoteGrabsDB})
QuoteGrabsDB = plugins.DB('QuoteGrabs', {'sqlite3': SqliteQuoteGrabsDB})
class QuoteGrabs(callbacks.Plugin):
"""Add the help for "@help QuoteGrabs" here."""

View File

@ -72,6 +72,12 @@ conf.registerChannelValue(RSS, 'showLinks',
along with the title of the feed when the rss command is called.
supybot.plugins.RSS.announce.showLinks affects whether links will be
listed when a feed is automatically announced.""")))
conf.registerGlobalValue(RSS, 'defaultNumberOfHeadlines',
registry.PositiveInteger(1, """Indicates how many headlines an rss feed
will output by default, if no number is provided."""))
conf.registerChannelValue(RSS, 'initialAnnounceHeadlines',
registry.PositiveInteger(5, """Indicates how many headlines an rss feed
will output when it is first added to announce for a channel."""))
conf.registerGroup(RSS, 'announce')
conf.registerChannelValue(RSS.announce, 'showLinks',

View File

@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: Supybot-fr\n"
"POT-Creation-Date: 2010-10-19 19:27+CEST\n"
"POT-Creation-Date: 2011-02-26 09:49+CET\n"
"PO-Revision-Date: \n"
"Last-Translator: Valentin Lorentz <progval@gmail.com>\n"
"Language-Team: Supybot-fr <progval@gmail.com>\n"
@ -15,20 +15,20 @@ msgstr ""
#: config.py:50
msgid ""
"Determines whether the bot will bold the title of the feed when it\n"
" announces new news."
"Determines whether the bot will bold the title of the feed when\n"
" it announces new news."
msgstr "Détermine si le bot mettera en gras le titre des flux lorsqu'il annoncera des news."
#: config.py:53
msgid ""
"Determines what string is used\n"
" to separate headlines in new feeds."
"Determines what string is\n"
" used to separate headlines in new feeds."
msgstr "Détermine quelle chaîne est utilisé pour séparer les titres dans les nouveaux flux."
#: config.py:56
msgid ""
"Determines what prefix\n"
" is prepended (if any) to the new news item announcements made in the\n"
"Determines what\n"
" prefix is prepended (if any) to the new news item announcements made in the\n"
" channel."
msgstr "Détermine quel préfixe (s'il y en a un) est utilisé pour annoncer les news sur le canal."
@ -60,14 +60,14 @@ msgid ""
" listed when a feed is automatically announced."
msgstr "Détermine si le bot listera le lien de chaque flus avec son titre, lorsque la commande rss est appelée. supybot.plugins.RSS.announce.showLinks affecte si les liens sont affichés lorsqu'un flux est annoncé automatiquement."
#: config.py:78
#: config.py:84
msgid ""
"Determines whether the bot will list the link\n"
" along with the title of the feed when a feed is automatically\n"
" announced."
msgstr "Détermine si le bot listera le lien de chaque flux avec le titre lorsqu'un flux est automatiquement annoncé."
#: plugin.py:63
#: plugin.py:62
msgid ""
"This plugin is useful both for announcing updates to RSS feeds in a\n"
" channel, and for retrieving the headlines of RSS feeds via command. Use\n"
@ -75,7 +75,7 @@ msgid ""
" command to determine what feeds should be announced in a given channel."
msgstr "Ce plugin est utile pour annoncer des flux RSS sur un canal, et pour récupérer les en-tête des flux RSS via une commande. Utilisez la commande \"add\" pour ajouter des flux au plugin, et utilisez la commande \"annonce\" pour détermine quels flux pourront être annoncés sur un canal donné."
#: plugin.py:311
#: plugin.py:316
msgid ""
"<name> <url>\n"
"\n"
@ -87,7 +87,7 @@ msgstr ""
"\n"
"Ajoute un commande à ce plugin qui permet de regarder le flux situé à l'<url>."
#: plugin.py:322
#: plugin.py:327
msgid ""
"<name>\n"
"\n"
@ -99,15 +99,27 @@ msgstr ""
"\n"
"Supprime le flux des flux qui peuvent être lus grâce à une commande."
#: plugin.py:328
#: plugin.py:333
msgid "That's not a valid RSS feed command name."
msgstr "Ce n'est pas une commande de flux RSS valide"
#: plugin.py:346
#: plugin.py:344
msgid ""
"[<channel>]\n"
"\n"
" Returns the list of feeds announced in <channel>. <channel> is\n"
" only necessary if the message isn't sent in the channel itself.\n"
" "
msgstr ""
"[<canal>]\n"
"\n"
"Retourne la liste des flux annoncés sur le <canal>. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
#: plugin.py:351
msgid "I am currently not announcing any feeds."
msgstr "Je n'annonce actuellement aucun flux."
#: plugin.py:351
#: plugin.py:356
msgid ""
"[<channel>] <name|url> [<name|url> ...]\n"
"\n"
@ -121,7 +133,7 @@ msgstr ""
"\n"
"Ajoute la liste de flux à la liste actuelle des flux annoncés sur le <canal>. Vous devez indiquer le <nom> du flux si il est déjà enregistré, ou l'<url> dans le cas contraire. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
#: plugin.py:369
#: plugin.py:374
msgid ""
"[<channel>] <name|url> [<name|url> ...]\n"
"\n"
@ -135,7 +147,7 @@ msgstr ""
"\n"
"Supprime la liste de flux de la liste actuelle des flux annoncés sur le <canal>. Vous devez indiquer le <nom> du flux si il est déjà enregistré, ou l'<url> dans le cas contraire. <canal> n'est nécessaire que si le message n'est pas envoyé sur le canal lui-même."
#: plugin.py:387
#: plugin.py:392
msgid ""
"<url> [<number of headlines>]\n"
"\n"
@ -147,11 +159,11 @@ msgstr ""
"\n"
"Récupère le titre des éléments du flux RSS donné. si le <nombre de lignes> est donné, ne retourne que ce nombre de lignes d'en-tête."
#: plugin.py:400
#: plugin.py:405
msgid "Couldn't get RSS feed."
msgstr "Ne peut récupérer le flux RSS."
#: plugin.py:413
#: plugin.py:420
msgid ""
"<url|feed>\n"
"\n"
@ -163,11 +175,11 @@ msgstr ""
"\n"
"Retourne des informations sur le flux RSS donné : le titre, l'URL, la description, et la dernière mise à jour."
#: plugin.py:426
#: plugin.py:433
msgid "I couldn't retrieve that RSS feed."
msgstr "Je ne peux récupérer ce flux RSS."
#: plugin.py:439
#: plugin.py:446
msgid "Title: %s; URL: %u; Description: %s; Last updated: %s."
msgstr "Titre : %s , URL : %u ; description : %s ; dernière mise à jour : %s."

Some files were not shown because too many files have changed in this diff Show More