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:
@ -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 :).
|
||||
|
@ -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'],
|
||||
|
@ -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"))
|
||||
|
@ -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('·')
|
||||
for i in range(npages):
|
||||
print('<a href="factoids.cgi?db=%s&search=%s&order=%s&page=%s">%d</a> ·' % (database, search, order_by, i, i+1))
|
||||
|
||||
print('<br />Order by<br />·');
|
||||
print(' <a href="factoids.cgi?db=%s&search=%s&order=%s&page=0">%s</a> ·' % (database, search, 'name ASC', 'Name +'))
|
||||
print(' <a href="factoids.cgi?db=%s&search=%s&order=%s&page=0">%s</a> ·' % (database, search, 'name DESC', 'Name -'))
|
||||
print(' <a href="factoids.cgi?db=%s&search=%s&order=%s&page=0">%s</a> ·' % (database, search, 'popularity ASC', 'Popularity +'))
|
||||
print(' <a href="factoids.cgi?db=%s&search=%s&order=%s&page=0">%s</a> ·' % (database, search, 'popularity DESC', 'Popularity -'))
|
||||
print(' <a href="factoids.cgi?db=%s&search=%s&order=%s&page=0">%s</a> ·' % (database, search, 'added ASC', 'Date added +'))
|
||||
print(' <a href="factoids.cgi?db=%s&search=%s&order=%s&page=0">%s</a> ·' % (database, search, 'added DESC', 'Date added -'))
|
||||
plink = ' <a href="factoids.cgi?db=%s&search=%s&order=%%s&page=%%s">%%s</a>' % (database, search)
|
||||
npages = int(math.ceil(float(total) / NUM_PER_PAGE))
|
||||
print(' ·\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(' ·\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('&','&').replace('<','<').replace('>','>').replace('\n','<br />')
|
||||
x = x.replace('&','&').replace('<','<').replace('>','>').replace('"','"')
|
||||
return url_re.sub(link, x)
|
||||
def link(match):
|
||||
url = match.group('url')
|
||||
txt = url
|
||||
if len(txt) > 30:
|
||||
txt = txt[:20] + '…' + txt[-10:]
|
||||
url = txt = match.group('url')
|
||||
# if len(txt) > 30:
|
||||
# txt = '%s…%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>''')
|
||||
|
||||
|
@ -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> ·
|
||||
<a href="https://launchpad.net/ubuntu-bots">Launchpad</a> ·
|
||||
<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> ·
|
||||
<a href="http://help.ubuntu.com/">help.ubuntu.com</a><br />
|
||||
More factoids: <a href="factoids.cgi?db=ubuntu">Ubuntu</a> ·
|
||||
<a href="factoids.cgi?db=buntudot">buntudot</a> ·
|
||||
<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 />
|
||||
©2006 Dennis Kaarsemaker<br/>
|
||||
Edited by Terence Simpson
|
||||
</p>
|
||||
</p>
|
||||
<p>
|
||||
©2006-2007 Dennis Kaarsemaker<br />
|
||||
©2008-2009 Terence Simpson<br />
|
||||
©2018 Krytarik Raido
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
|
@ -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
@ -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:
|
||||
|
5
bot.css
5
bot.css
@ -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;
|
||||
|
37
commoncgi.py
37
commoncgi.py
@ -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('&','&').replace('<','<').replace('>','>').replace('"','"')
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
Encyclopedia/logs.tmpl
|
Reference in New Issue
Block a user