Encyclopedia: General overhaul.

* Improve input handling.
* Enable multi-message output.
* Improve request logging.
* Add real delete function.
* Improve factoid web page.
* Extend factoid info output.
* Count calls of aliases towards
  their own popularity.
* Show info on deleted factoids too.
* Improve factoid search algorithm.
* Sort factoid search by popularity.
* Improve command name structure.
* Various other and minor improvements.
* Update default configuration.
This commit is contained in:
Krytarik Raido 2018-05-22 00:45:04 +02:00
parent 669293de1e
commit 3368b2d6fe
11 changed files with 749 additions and 944 deletions

View File

@ -1,12 +1,12 @@
Factoid plugin
Note: This plugin used to have package lookup, this was mooved to the
Note: This plugin used to have package lookup, this was moved to the
PackageInfo plugin.
Pick a name for your database. A lowercase-only name without spaces is probably
best, this example wil use myfactoids as name. Then create a directory to store
your databases in (somewere in $botdir/data would be best).
If you choose to enable this plugin during supybot-wizard the database will be
created for you. If noy, you can create the database manually.
best, this example will use myfactoids as name. Then create a directory to store
your databases in (somewhere in $botdir/data would be best).
If you choose to enable this plugin during supybot-wizard, the database will be
created for you. If not, you can create the database manually.
In the new directory create an SQLite 3 database with the following command:
sqlite3 myfactoids.db
@ -15,30 +15,31 @@ Then copy/paste in the below 2 tables:
CREATE TABLE facts (
id INTEGER PRIMARY KEY,
author TEXT NOT NULL,
name TEXT NOT NULL,
added TEXT NOT NULL,
value TEXT NOT NULL,
author TEXT NOT NULL,
added TEXT NOT NULL,
popularity INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE log (
id INTEGER PRIMARY KEY,
author TEXT NOT NULL,
name TEXT NOT NULL,
added TEXT NOT NULL,
oldvalue TEXT NOT NULL
oldvalue TEXT NOT NULL,
author TEXT NOT NULL,
added TEXT NOT NULL
);
If you want to create more databases, repeat these last two steps.
When the databases exist, you need to configure the bots to actually use them.
To do that, set the global value supybot.plugins.encyclopedia.datadir to the new
dirand the channel value supybot.plugins.encyclopedia.database to the name of
the database (without the .db suffix).
directory and the channel value supybot.plugins.encyclopedia.database to the name
of the database (without the .db suffix).
Documentation on adding/editing factoids can be found on
https://ubottu.com/devel/wiki/Plugins#Encyclopedia
To give people edit access, let them register with your bot and use the command:
@addeditor nickname_here
(replace @ with your prefix char). Similarly you can use removeeditor :).

View File

@ -24,7 +24,7 @@ import supybot
import supybot.world as world
from imp import reload
__version__ = "2.5"
__version__ = "2.6"
__author__ = supybot.Author("Krytarik Raido", "krytarik", "krytarik@tuxgarage.com")
__contributors__ = {
supybot.Author("Dennis Kaarsemaker", "Seveas", "dennis@kaarsemaker.net"): ['Original Author'],

View File

@ -20,10 +20,7 @@ import supybot.registry as registry
def configure(advanced):
from supybot.questions import yn, something, output
from supybot.utils.str import format
import os
import sqlite3
import re
import os, re, sqlite3
def anything(prompt, default=None):
"""Because supybot is pure fail"""
@ -40,8 +37,8 @@ def configure(advanced):
ignores = set([])
output("This plugin can be configured to always ignore certain factoid requests, this is useful when you want another plugin to handle them")
output("For instance, the PackageInfo plugin responds to !info and !find, so those should be ignored in Encyclopedia to allow this to work")
ignores_i = anything("Which factoid requets should the bot always ignore?", default=', '.join(Encyclopedia.ignores._default))
for name in re.split(r',?\s', ignores_i):
ignores_i = anything("Which factoid requests should the bot always ignore?", default=', '.join(Encyclopedia.ignores._default))
for name in re.split(r'[,\s]+', ignores_i):
ignores.add(name.lower())
curStable = something("What is short name of the current stable release?", default=Encyclopedia.curStable._default)
@ -70,16 +67,16 @@ def configure(advanced):
curLTSLong = Encyclopedia.curLTSLong._default
curLTSNum = Encyclopedia.curLTSNum._default
relaychannel = anything("What channel/nick should the bot forward alter messages to?", default=Encyclopedia.relaychannel._default)
relaychannel = anything("What channel/nick should the bot forward edit messages to?", default=Encyclopedia.relaychannel._default)
output("What message should the bot reply with when a factoid can not be found?")
notfoundmsg = something("If you include a '%s' in the message, it will be replaced with the requested factoid", default=Encyclopedia.notfoundmsg._default)
alert = set([])
output("When certain factoids are called an alert can be forwarded to a channel/nick")
output("Which factoids should the bot forward alert calls for?")
alert = set([])
alert_i = anything("Separate types by spaces or commas:", default=', '.join(Encyclopedia.alert._default))
for name in re.split(r',?\s+', alert_i):
for name in re.split(r'[,\s]+', alert_i):
alert.add(name.lower())
remotedb = anything("Location of a remote database to sync with (used with @sync)", default=Encyclopedia.remotedb._default)
remotedb = anything("Location of a remote database to sync with (used with @sync):", default=Encyclopedia.remotedb._default)
privateNotFound = yn("Should the bot reply in private when a factoid is not found, as opposed to in the channel?", default=Encyclopedia.privateNotFound._default)
Encyclopedia.enabled.setValue(enabled)
@ -124,19 +121,19 @@ def configure(advanced):
try:
cur.execute("""CREATE TABLE facts (
id INTEGER PRIMARY KEY,
author TEXT NOT NULL,
name TEXT NOT NULL,
added TEXT NOT NULL,
value TEXT NOT NULL,
author TEXT NOT NULL,
added TEXT NOT NULL,
popularity INTEGER NOT NULL DEFAULT 0
)""")
cur.execute("""CREATE TABLE log (
id INTEGER PRIMARY KEY,
author TEXT NOT NULL,
name TEXT NOT NULL,
added TEXT NOT NULL,
oldvalue TEXT NOT NULL
oldvalue TEXT NOT NULL,
author TEXT NOT NULL,
added TEXT NOT NULL
)""")
except:
@ -150,7 +147,7 @@ def configure(advanced):
Encyclopedia = conf.registerPlugin('Encyclopedia')
conf.registerChannelValue(Encyclopedia, 'enabled',
registry.Boolean(True, "Enable Encyclopedia"))
registry.Boolean(True, 'Enable Encyclopedia'))
conf.registerChannelValue(Encyclopedia, 'database',
registry.String('ubuntu', 'Name of database to use'))
@ -159,11 +156,11 @@ conf.registerChannelValue(Encyclopedia, 'relaychannel',
registry.String('#ubuntu-ops', 'Relay channel for unauthorized edits'))
conf.registerGlobalValue(Encyclopedia, 'editchannel',
registry.SpaceSeparatedListOfStrings(['#ubuntu-ops'],
'Channels where edits of restricted editors are allowed.'))
registry.SpaceSeparatedListOfStrings(['#ubuntu-ops'],
'Channels where edits of restricted editors are allowed'))
conf.registerGlobalValue(Encyclopedia, 'notfoundmsg',
registry.String('Factoid %s not found', 'Reply when factoid isn\'t found'))
registry.String('Factoid %s not found', 'Reply when factoid is not found'))
conf.registerChannelValue(Encyclopedia,'prefixchar',
registry.String('!','Prefix character for factoid display/editing'))
@ -172,38 +169,38 @@ conf.registerGlobalValue(Encyclopedia, 'datadir',
conf.Directory(conf.supybot.directories.data(), 'Path to dir containing factoid databases', private=True))
conf.registerChannelValue(Encyclopedia, 'alert',
registry.SpaceSeparatedListOfStrings(['ops', 'op', 'kops', 'calltheops'], 'factoid name(s) used for alerts', private=True))
registry.SpaceSeparatedListOfStrings(['ops', 'op', 'kops', 'calltheops'], 'Factoid names used for alerts', private=True))
conf.registerChannelValue(Encyclopedia, 'remotedb',
registry.String('http://ubottu.com/ubuntu.db', 'Remote location of the master database', private=True))
registry.String('https://ubottu.com/ubuntu.db', 'Remote location of the master database', private=True))
conf.registerChannelValue(Encyclopedia, 'ignores',
registry.SpaceSeparatedListOfStrings(['find', 'info'], 'factoid name(s) to ignore', private=True))
registry.SpaceSeparatedListOfStrings(['info', 'depends', 'find'], 'Factoid names to ignore', private=True))
conf.registerChannelValue(Encyclopedia, 'privateNotFound',
registry.Boolean(False, "If set to True, send notfoundmsg in private rather than in the channel"))
conf.registerChannelValue(Encyclopedia, 'forcedFactoid',
registry.Boolean(False, "If True, factoids in kick's reason will be sent to the user in private"))
registry.Boolean(False, "If set to True, factoids in kick reason will be sent to the user in private"))
conf.registerGlobalValue(Encyclopedia, 'curStable',
registry.String('Natty', "Current stable release"))
registry.String('Artful', "Current stable release"))
conf.registerGlobalValue(Encyclopedia, 'curStableLong',
registry.String('Natty Narwhal', "Current stable release"))
registry.String('Artful Aardvark', "Current stable release"))
conf.registerGlobalValue(Encyclopedia, 'curStableNum',
registry.String('11.04', "Current stable release"))
registry.String('17.10', "Current stable release"))
conf.registerGlobalValue(Encyclopedia, 'curDevel',
registry.String('Oneiric', "Current development release"))
registry.String('Bionic', "Current development release"))
conf.registerGlobalValue(Encyclopedia, 'curDevelLong',
registry.String('Oneiric Ocelot', "Current development release"))
registry.String('Bionic Beaver', "Current development release"))
conf.registerGlobalValue(Encyclopedia, 'curDevelNum',
registry.String('11.10', "Current development release"))
registry.String('18.04', "Current development release"))
conf.registerGlobalValue(Encyclopedia, 'curLTS',
registry.String('Lucid', "Current LTS release"))
registry.String('Xenial', "Current LTS release"))
conf.registerGlobalValue(Encyclopedia, 'curLTSLong',
registry.String('Lucid Lynx', "Current LTS release"))
registry.String('Xenial Xerus', "Current LTS release"))
conf.registerGlobalValue(Encyclopedia, 'curLTSNum',
registry.String('10.04', "Current LTS release"))
registry.String('16.04', "Current LTS release"))

View File

@ -15,183 +15,130 @@
#
###
import sys
import sys, sqlite3
# This needs to be set to the location of the commoncgi.py file
sys.path.append('/var/www/bot')
from commoncgi import *
import sqlite3
### Variables
NUM_PER_PAGE=50.0
NUM_PER_PAGE = 50
# Directory containing the factoid database
datadir = '/home/bot/'
# Database filename (without the .db extention)
default_database = 'ubuntu'
database = 'ubuntu'
### Nothing below this line should be edited unless you know what you're doing ###
databases = [x for x in os.listdir(datadir)]
# Initialize
database = default_database
order_by = 'popularity DESC'
page = 0
order_url = 'popularity|DESC'
order_by = order_url.replace('|',' ')
page = 1
search = ''
factoids = []
total = 0
class Factoid:
def __init__(self, name, value, author, added, popularity):
self.name, self.value, self._author, self._added, self.popularity = (name, value, author, added, popularity)
@property
def author(self):
if '!' in self._author:
return self._author[:self._author.find('!')]
return self._author
@property
def added(self):
if '.' in self._added:
return self._added[:self._added.find('.')]
return self._added
def __iter__(self):
yield self.name
yield self.value
yield self.author
yield self.added
yield self.popularity
self.name, self.value, self.author, self.added, self.popularity = name, value, author, added, popularity
class Log:
def __init__(self, author, added):
self._author, self._added = (author, added)
@property
def author(self):
if '!' in self._author:
return self._author[:self._author.find('!')]
return self._author
@property
def added(self):
if '.' in self._added:
return self._added[:self._added.find('.')]
return self._added
self.author, self.added = author, added
# Read POST
if 'db' in form:
if 'db' in form and form['db'].value in databases:
database = form['db'].value
if database not in databases:
database = default_database
con = sqlite3.connect(os.path.join(datadir, '%s.db' % database))
cur = con.cursor()
try: page = int(form['page'].value)
except: pass
if 'order' in form:
if form['order'].value in ('added DESC', 'added ASC', 'name DESC', 'name ASC', 'popularity DESC','popularity ASC'):
order_by = form['order'].value
try:
page = int(form['page'].value)
except:
pass
if 'order' in form and form['order'].value in ('added|DESC', 'added|ASC', 'name|DESC', 'name|ASC', 'popularity|DESC', 'popularity|ASC'):
order_url = form['order'].value
order_by = order_url.replace('|',' ')
if 'search' in form:
search = form['search'].value
# Select factoids
if search:
keys = [utils.web.urlunquote(x.strip()) for x in search.split() if len(x.strip()) >=2][:5]
values = []
if not keys:
keys = ['']
query1 = "SELECT name, value, author, added, popularity FROM facts WHERE name NOT LIKE '%%-also' AND ("
query2 = "SELECT COUNT(*) FROM facts WHERE "
bogus = False
keys = utils.web.urlunquote(search).split()[:5]
qterms, values = '', []
for k in keys:
values.extend(('%%%s%%' % k, '%%%s%%' % k))
if bogus:
query1 += ' OR '
query2 += ' OR '
query1 += 'name LIKE ? OR value LIKE ?'
query2 += 'name LIKE ? OR value LIKE ?'
bogus=True
cur.execute(query1 + ') ORDER BY %s LIMIT %d, %d' % (order_by, NUM_PER_PAGE * page, NUM_PER_PAGE), values)
if qterms:
qterms += ' AND '
qterms += '(name LIKE ? OR value LIKE ? OR value LIKE ?)'
values.extend(['%%%s%%' % k.lower(), '%%%s%%' % k, '%%%s%%' % k.lower()])
cur.execute("SELECT name, value, author, added, popularity FROM facts WHERE name NOT LIKE '%%-also' AND %s ORDER BY %s LIMIT %d, %d" %
(qterms, order_by, NUM_PER_PAGE * (page - 1), NUM_PER_PAGE), values)
factoids = [Factoid(*x) for x in cur.fetchall()]
cur.execute(query2, values)
cur.execute("SELECT COUNT(*) FROM facts WHERE name NOT LIKE '%%-also' AND %s" % qterms, values)
total = cur.fetchall()[0][0]
else:
cur.execute("SELECT name, value, author, added, popularity FROM facts WHERE value NOT LIKE '<alias>%%' AND name NOT LIKE '%%-also' ORDER BY %s LIMIT %d, %d" %
(order_by, NUM_PER_PAGE * page, NUM_PER_PAGE))
(order_by, NUM_PER_PAGE * (page - 1), NUM_PER_PAGE))
factoids = [Factoid(*x) for x in cur.fetchall()]
cur.execute("SELECT COUNT(*) FROM facts WHERE value NOT LIKE '<alias>%%'")
cur.execute("SELECT COUNT(*) FROM facts WHERE value NOT LIKE '<alias>%%' AND name NOT LIKE '%%-also'")
total = cur.fetchall()[0][0]
# Pagination links
npages = int(math.ceil(total / float(NUM_PER_PAGE)))
print('&middot;')
for i in range(npages):
print('<a href="factoids.cgi?db=%s&search=%s&order=%s&page=%s">%d</a> &middot;' % (database, search, order_by, i, i+1))
print('<br />Order by<br />&middot;');
print(' <a href="factoids.cgi?db=%s&search=%s&order=%s&page=0">%s</a> &middot;' % (database, search, 'name ASC', 'Name +'))
print(' <a href="factoids.cgi?db=%s&search=%s&order=%s&page=0">%s</a> &middot;' % (database, search, 'name DESC', 'Name -'))
print(' <a href="factoids.cgi?db=%s&search=%s&order=%s&page=0">%s</a> &middot;' % (database, search, 'popularity ASC', 'Popularity +'))
print(' <a href="factoids.cgi?db=%s&search=%s&order=%s&page=0">%s</a> &middot;' % (database, search, 'popularity DESC', 'Popularity -'))
print(' <a href="factoids.cgi?db=%s&search=%s&order=%s&page=0">%s</a> &middot;' % (database, search, 'added ASC', 'Date added +'))
print(' <a href="factoids.cgi?db=%s&search=%s&order=%s&page=0">%s</a> &middot;' % (database, search, 'added DESC', 'Date added -'))
plink = ' <a href="factoids.cgi?db=%s&amp;search=%s&amp;order=%%s&amp;page=%%s">%%s</a>' % (database, search)
npages = int(math.ceil(float(total) / NUM_PER_PAGE))
print(' &middot;\n'.join(list(map(lambda x: plink % (order_url, x, x) if x != page else str(x), range(1, npages+1)))))
print('''
<table cellspacing="0">
print(' <br />Order by<br />');
print(' &middot;\n'.join([plink % ('name|ASC', 1, 'Name +'), plink % ('name|DESC', 1, 'Name -'),
plink % ('popularity|ASC', 1, 'Popularity +'), plink % ('popularity|DESC', 1, 'Popularity -'),
plink % ('added|ASC', 1, 'Date added +'), plink % ('added|DESC', 1, 'Date added -')]))
print('''\
<table style="border-collapse: collapse;">
<thead>
<tr>
<th style="width: 10%;">Factoid</th>
<th style="width: 70%;">Value</th>
<th style="width: 20%;">Author</th>
<th style="width: 15%;">Factoid</th>
<th style="width: 68%;">Value</th>
<th style="width: 17%;">Author</th>
</tr>
</thead>
<tbody>''')
url_re = re.compile('(?P<url>(https?://\S+|www\S+))')
url_re = re.compile('(?P<url>(https?://|www\.)\S+)')
def q(x):
x = str(x).replace('&','&amp;').replace('<','&lt;').replace('>','&gt;').replace('\n','<br />')
x = x.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;').replace('"','&quot;')
return url_re.sub(link, x)
def link(match):
url = match.group('url')
txt = url
if len(txt) > 30:
txt = txt[:20] + '&hellip;' + txt[-10:]
url = txt = match.group('url')
# if len(txt) > 30:
# txt = '%s&hellip;%s' % (txt[:20], txt[-10:])
return '<a href="%s">%s</a>' % (url, txt)
i = 0
for fact in factoids:
name = fact.name
cur.execute("SELECT name FROM facts WHERE value LIKE ?", ('<alias>_%s' % fact.name,))
name = ' '.join([fact.name] + [x[0] for x in cur.fetchall()])
cur.execute("SELECT value FROM facts WHERE name = ?", ('%s-also' % fact.name,))
more = cur.fetchall()
if len(more):
name += ' $hr$' + ' $hr$'.join([x[0] for x in more])
cur.execute("SELECT name FROM facts WHERE value = ?", ('<alias> %s' % fact.name,))
name += ' \n' + ' \n'.join([x[0] for x in cur.fetchall()])
data = [ q(x) for x in fact ]
value = ' '.join([fact.value] + [x[0] for x in cur.fetchall()])
data = ["Added by %s" % fact.author[:fact.author.find('!')], "Date: %s" % fact.added[:fact.added.rfind('.')]]
cur.execute("SELECT author, added FROM log WHERE name = ? ORDER BY id DESC LIMIT 1", (fact.name,))
edit = [Log(*x) for x in cur.fetchall()]
# edit = [Log(author, added) for (author, added) in cur.fetchall()]
if edit:
log = edit[0]
data[3] += "<br />Last edited by %s<br />Last modified: %s" % (q(log.author), q(log.added))
else:
data[3] += "<br />Never edited"
data[0] = name
sys.stdout.write(' <tr')
if i % 2: sys.stdout.write(' class="bg2"')
i += 1
print('''>
<td>%s</td>
<td>%s</td>
<td>%s<br />
Added on: %s<br />
Requested %s times</td>
</tr>''' % tuple(data))
data.extend(["Last edited by %s" % log.author[:log.author.find('!')], "Date: %s" % log.added[:log.added.rfind('.')]])
data.append("Requested %s times" % fact.popularity)
print('''
print('''\
<tr%s>
<td>%s</td>
<td>%s</td>
<td>%s</td>
</tr>''' % (' class="bg2"' if i % 2 else '', q(name), q(value), '<br />\n '.join(data)))
i += 1
print('''\
</tbody>
</table>''')

View File

@ -8,7 +8,7 @@
<link rel="stylesheet" href="bot.css" />
<script type="text/javascript">
var DHTML = (document.getElementById || document.all || document.layers);
function getObj(name) {
if (document.getElementById) {
this.obj = document.getElementById(name);
@ -36,32 +36,31 @@
c.style.display = 'inline';
}
}
}
</script>
</head>
<body>
<div class="main">
<h1>Ubotu factoids</h1>
%e
<h1>Ubottu factoids</h1>
<div class="errors">
%e
</div>
<p>
<a href="/">Home</a> &middot;
<a href="https://launchpad.net/ubuntu-bots">Launchpad</a> &middot;
<a href="ubuntu.db">Ubuntu database file</a>
</p>
<form action="factoids.cgi" method="GET">
<input class="input" type="text" name="search" />
<input class="input" type="submit" value="Search">
</form>
<p>
More help: <a href="http://wiki.ubuntu.com/">wiki.ubuntu.com</a> &middot;
<a href="http://help.ubuntu.com/">help.ubuntu.com</a><br />
More factoids: <a href="factoids.cgi?db=ubuntu">Ubuntu</a> &middot;
<a href="factoids.cgi?db=buntudot">buntudot</a> &middot;
<a href="factoids.cgi?db=gnewsense">GNewSense</a><br />
<form action="factoids.cgi" method="GET">
<input class="input" type="text" name="search" />
<input class="input" type="submit" value="Search">
</form>
<p>
%s
</p>
<p>
<a href="ubuntu.db">Ubuntu factoid database file</a><br />
&copy;2006 Dennis Kaarsemaker<br/>
Edited by Terence Simpson
</p>
</p>
<p>
&copy;2006-2007 Dennis Kaarsemaker<br />
&copy;2008-2009 Terence Simpson<br />
&copy;2018 Krytarik Raido
</p>
</div>
</body>

View File

@ -1,13 +0,0 @@
<html>
<head>
<title>Bot logs</title>
<link rel="stylesheet" href="/bot.css" />
<link rel="shortcut icon" href="favicon.ico" type="image/png" />
</head>
<body>
<div class="home">
<h1>Logs of unauthorized edits and bot actions</h1>
%s
</div>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -4,17 +4,17 @@
# Copyright (c) 2010 Elián Hanisch
# Copyright (c) 2018 Krytarik Raido
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
###
@ -24,7 +24,6 @@ import supybot.conf as conf
Econf = conf.supybot.plugins.Encyclopedia
Econf.prefixchar.set('@')
class EncyclopediaTestCase(ChannelPluginTestCase):
plugins = ('Encyclopedia',)
@ -35,7 +34,7 @@ class EncyclopediaTestCase(ChannelPluginTestCase):
def createDB(self):
import sqlite3, os
dbfile = os.path.join(Econf.datadir(), '%s.db' %Econf.database())
dbfile = os.path.join(Econf.datadir(), '%s.db' % Econf.database())
try:
os.remove(dbfile)
except:
@ -44,18 +43,18 @@ class EncyclopediaTestCase(ChannelPluginTestCase):
cur = db.cursor()
cur.execute("""CREATE TABLE facts (
id INTEGER PRIMARY KEY,
author TEXT NOT NULL,
name TEXT NOT NULL,
added TEXT NOT NULL,
value TEXT NOT NULL,
author TEXT NOT NULL,
added TEXT NOT NULL,
popularity INTEGER NOT NULL DEFAULT 0
)""")
cur.execute("""CREATE TABLE log (
id INTEGER PRIMARY KEY,
author TEXT NOT NULL,
name TEXT NOT NULL,
added TEXT NOT NULL,
oldvalue TEXT NOT NULL
oldvalue TEXT NOT NULL,
author TEXT NOT NULL,
added TEXT NOT NULL
)""")
db.commit()
db.close()
@ -84,13 +83,13 @@ class EncyclopediaTestCase(ChannelPluginTestCase):
world.testing = False
self.prefix = 'user!user@home.com'
try:
self.assertResponse('test-#ubuntu-se is <reply> blah',
self.assertResponse('test-#ubuntu-se is <reply> blah',
'Your edit request has been forwarded to #ubuntu-ops. Thank you ' \
'for your attention to detail')
self.assertEqual(self.irc.takeMsg().args[1],
'In #test, user said: @test-#ubuntu-se is <reply> blah')
# test in private, it shouldn't use the prefix char.
self.assertResponse('test-#ubuntu-se is <reply> blah',
self.assertResponse('test-#ubuntu-se is <reply> blah',
'Your edit request has been forwarded to #ubuntu-ops. Thank you ' \
'for your attention to detail', private=True, usePrefixChar=False)
self.assertEqual(self.irc.takeMsg().args[1],
@ -99,5 +98,4 @@ class EncyclopediaTestCase(ChannelPluginTestCase):
world.testing = True
# vim:set shiftwidth=4 softtabstop=4 tabstop=4 expandtab textwidth=100:

View File

@ -5,11 +5,9 @@ body {
color: #000066;
font-size: 10px;
}
tbody {
font-weight: normal;
font-weight: normal;
}
div.home {
margin: 20px auto;
width: 300px;
@ -53,7 +51,6 @@ table {
padding-top: 1em;
clear: both;
}
div.main {
margin: 20px;
border: 2px solid #000066;

View File

@ -15,7 +15,7 @@
#
###
import cgi, cgitb, sys, os, re, math
import cgi, cgitb, sys, os, re, math, codecs
import supybot.utils as utils
if sys.version_info < (3,0):
@ -39,12 +39,12 @@ if 'tz' in cookie:
class IOWrapper:
'''Class to wrap default IO, used with templates'''
def __init__(self):
self.buf = []
self.buf = ''
def write(self, val):
self.buf.append(val)
self.buf += val
def getvalue(self):
return self.buf
sys.stdout = IOWrapper()
sys.stderr = IOWrapper()
@ -52,8 +52,13 @@ def send_page(template):
'''Sends a template page and exit'''
data = sys.stdout.getvalue()
errdata = sys.stderr.getvalue()
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
# Ensure Unicode output
if sys.version_info < (3,1):
sys.stdout = codecs.getwriter('utf-8')(sys.__stdout__)
else:
sys.stdout = codecs.getwriter('utf-8')(sys.__stdout__.detach())
print("Content-Type: text/html")
print(cookie)
print("")
@ -61,17 +66,19 @@ def send_page(template):
fd = open(template)
tmpl = fd.read()
fd.close()
sys.stdout.write(tmpl[:tmpl.find('%e')])
for e in errdata:
sys.stdout.write(e)
sys.stdout.write(tmpl[tmpl.find('%e')+2:tmpl.find('%s')])
# print tmpl[:tmpl.find('%s')]
for d in data:
sys.stdout.write(d)
sys.stdout.write(tmpl[tmpl.find('%s')+2:])
estart = tmpl.find('%e')
sstart = tmpl.find('%s')
if sys.version_info < (3,0):
page = u'{}{}{}{}{}'.format(tmpl[:estart], errdata,
tmpl[estart+2:sstart], data, tmpl[sstart+2:])
else:
page = '{}{}{}{}{}'.format(tmpl[:estart], errdata,
tmpl[estart+2:sstart], data, tmpl[sstart+2:])
print(page)
sys.exit(0)
def q(txt):
'''Simple HTML entity quoting'''
return txt.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;').replace('"','&quot;')

View File

@ -1 +0,0 @@
Encyclopedia/logs.tmpl