@@ -3,7 +3,7 @@ layout: base | |||||
--- | --- | ||||
<div id="header"> | <div id="header"> | ||||
<h1 class="naked">Hi, I'm <span class="text-highlight">Earwig</span>!</h1> | |||||
<h1 id="header">Hi, I'm <span class="color-highlight">Earwig</span>!</h1> | |||||
</div> | </div> | ||||
<div id="content"> | <div id="content"> | ||||
<table> | <table> | ||||
@@ -51,7 +51,7 @@ layout: base | |||||
{{ content }} | {{ content }} | ||||
</td> | </td> | ||||
<td id="right-box"> | <td id="right-box"> | ||||
<h2 class="topless">Posts</h2> | |||||
<h2 id="posts">Posts</h2> | |||||
<ul> | <ul> | ||||
{% for post in site.posts %} | {% for post in site.posts %} | ||||
<li class="post">{{ post.date | date: "%b %d, %Y" }}: <a class="invert" href="{{ post.url }}">{{ post.title }}</a><br /><span class="description">{{ post.description }}</span></li> | <li class="post">{{ post.date | date: "%b %d, %Y" }}: <a class="invert" href="{{ post.url }}">{{ post.title }}</a><br /><span class="description">{{ post.description }}</span></li> | ||||
@@ -3,10 +3,12 @@ layout: base | |||||
--- | --- | ||||
<div id="header"> | <div id="header"> | ||||
<h1 class="naked"><a class="incognito" href="/">{{ page.title }}</a></h1> | |||||
<h1 id="header"><a class="incognito" href="/">{{ page.title }}</a></h1> | |||||
</div> | </div> | ||||
<div id="content"> | <div id="content"> | ||||
<span class="description"><span class="text-highlight">Date:</span> {{ page.date | date: "%b %d, %Y" }}</span> | |||||
<div id="post-info" class="description"> | |||||
<span class="color-highlight">Date:</span> {{ page.date | date: "%b %d, %Y" }} | |||||
</div> | |||||
<div id="post"> | <div id="post"> | ||||
{{ content }} | {{ content }} | ||||
</div> | </div> | ||||
@@ -0,0 +1,134 @@ | |||||
--- | |||||
layout: post | |||||
title: EarwigBot Progress: Wiki Toolset | |||||
description: YAWTF (Yet Another Wiki Tools Framework, or Yet Another... WTF?) | |||||
--- | |||||
So I've been spending the past week and a half working on EarwigBot's new | |||||
wikitools framework thing (to avoid confusion with Mr.Z-man's | |||||
`python-wikitools` package, I'm referring to it as "EarwigBot's Wiki Toolset" | |||||
in the docs, even though it's `wiki.tools` internally). Basically, it's the | |||||
interface between EarwigBot and the MediaWiki API. | |||||
As Josh put it, this is "the thing that actually makes it work". | |||||
So, now you can do this (from within Python's interpreter, a wiki bot task, or | |||||
an IRC command): | |||||
{% highlight pycon %} | |||||
>>> from wiki import tools | |||||
>>> site = tools.get_site() | |||||
>>> print site.name() | |||||
enwiki | |||||
>>> print site.project() | |||||
wikipedia | |||||
>>> print site.lang() | |||||
en | |||||
>>> print site.domain() | |||||
en.wikipedia.org | |||||
{% endhighlight %} | |||||
Our `config.json` file stores site information, along with our chosen "default | |||||
site". Pretty neat, huh? "But what can it actually do?" I hear you ask? Well, | |||||
for example, we can get information about users: | |||||
{% highlight pycon %} | |||||
>>> user = site.get_user("The Earwig") | |||||
>>> print user.editcount() | |||||
11079 | |||||
>>> print user.groups() | |||||
[u'*', u'user', u'autoconfirmed', u'abusefilter', u'sysop'] | |||||
>>> reg = user.registration() | |||||
>>> import time | |||||
>>> print time.strftime("%a, %d %b %Y %H:%M:%S", reg) | |||||
Thu, 03 Jul 2008 21:51:34 | |||||
{% endhighlight %} | |||||
and pages as well, with intelligent namespace logic: | |||||
{% highlight pycon %} | |||||
>>> page = site.get_page("Wikipedia:Articles for creation") | |||||
>>> print page.url() | |||||
http://en.wikipedia.org/wiki/Wikipedia:Articles_for_creation | |||||
>>> print page.namespace() | |||||
4 | |||||
>>> print site.namespace_id_to_name(4) | |||||
Wikipedia | |||||
>>> print site.namespace_id_to_name(4, all=True) | |||||
[u'Wikipedia', u'Project', u'WP'] | |||||
>>> print page.is_talkpage() | |||||
False | |||||
>>> talkpage = page.toggle_talk() | |||||
>>> print talkpage.title() | |||||
Wikipedia talk:Articles for creation | |||||
>>> print talkpage.is_talkpage() | |||||
True | |||||
{% endhighlight %} | |||||
and with support for redirect following: | |||||
{% highlight pycon %} | |||||
>>> page = site.get_page("Main page") | |||||
>>> print page.is_redirect() | |||||
True | |||||
>>> print page.get() | |||||
#REDIRECT [[Main Page]] | |||||
[[Category:Protected redirects]] | |||||
[[Category:Main Page| ]] | |||||
>>> print page.get_redirect_target() | |||||
Main Page | |||||
>>> page = site.get_page("Main page", follow_redirects=True) | |||||
>>> print page.is_redirect() | |||||
False # would only be True if "Main page" is a double redirect | |||||
>>> print page.get() | |||||
<!-- BANNER ACROSS TOP OF PAGE --> | |||||
{| id="mp-topbanner" style="width:100%; background:#f9f9f9; margin:1.2em 0 6px 0; border:1px solid #ddd;" | |||||
| style="width:61%; color:#000;" | | |||||
... | |||||
{% endhighlight %} | |||||
Of course, a Wiki Toolset would be nothing without login! Our username and | |||||
password are stored (encrypted with Blowfish) in the bot's `config.json` file, | |||||
and we login automatically whenever we create a new Site object – unless | |||||
we're already logged in, of course, and we know that based on whether we have | |||||
valid login cookies. | |||||
{% highlight pycon %} | |||||
>>> user = site.get_user() # gets the logged-in user | |||||
>>> print user.name() | |||||
EarwigBot | |||||
{% endhighlight %} | |||||
Cookies are stored in a special `.cookies` file in the project root (with no | |||||
access given to other users, of course). We support both per-project login and | |||||
CentralAuth, meaning I can do... | |||||
{% highlight pycon %} | |||||
>>> es = tools.get_site("eswiki") | |||||
>>> print es.get_user().name() | |||||
EarwigBot | |||||
{% endhighlight %} | |||||
without making additional logins. One thing I strove for when designing the | |||||
toolset was as minimal API usage as possible – we accept gzipped data, we | |||||
don't make API queries unless they're actually requested, and we combine | |||||
queries whenever possible. Of course, I'm probably doing it all wrong, but | |||||
it seems to be working so far. | |||||
So... yeah. Carry on then! | |||||
:—earwig |
@@ -5,30 +5,31 @@ body { | |||||
background-color: #E0E0E0; | background-color: #E0E0E0; | ||||
} | } | ||||
.text-highlight { | |||||
.color-highlight { | |||||
color: #040; | color: #040; | ||||
} | } | ||||
.description { | |||||
font-size: 12px; | |||||
} | |||||
.topless { | |||||
margin-top: 0px; | |||||
padding-top: 0px; | |||||
.highlight { /* syntax highlighter */ | |||||
background: #f2f2f2; | |||||
border: 1px solid #e8e8e8; | |||||
border-radius: 10px; | |||||
padding-left: 16px; | |||||
line-height: 1.35em; | |||||
font-size: 13px; | |||||
} | } | ||||
.naked { | |||||
margin-top: 0px; | |||||
padding-top: 0px; | |||||
margin-bottom: 0px; | |||||
padding-bottom: 0px; | |||||
.description { | |||||
font-size: 12px; | |||||
} | } | ||||
h1, h2 { | h1, h2 { | ||||
text-align: center; | text-align: center; | ||||
} | } | ||||
pre { | |||||
white-space: pre-wrap; | |||||
} | |||||
div.project { | div.project { | ||||
border: 1px solid #CCC; | border: 1px solid #CCC; | ||||
border-radius: 5px; | border-radius: 5px; | ||||
@@ -47,12 +48,12 @@ div.project-body { | |||||
} | } | ||||
td.about-l { | td.about-l { | ||||
padding: 3px 6px 3px 6px; | |||||
padding: 4px 12px 4px 12px; | |||||
border-left: 1px solid #CCC; | border-left: 1px solid #CCC; | ||||
} | } | ||||
td.about-r { | td.about-r { | ||||
padding: 3px 6px 3px 6px; | |||||
padding: 4px 12px 4px 12px; | |||||
border-right: 1px solid #CCC; | border-right: 1px solid #CCC; | ||||
} | } | ||||
@@ -65,17 +66,29 @@ td.dark-l { background-color: #DADADA; } | |||||
td.light-r { background-color: #F7F7F7; } | td.light-r { background-color: #F7F7F7; } | ||||
td.dark-r { background-color: #E5E5E5; } | td.dark-r { background-color: #E5E5E5; } | ||||
h1#header { | |||||
margin-top: 0px; | |||||
padding-top: 0px; | |||||
margin-bottom: 0px; | |||||
padding-bottom: 0px; | |||||
} | |||||
h2#posts { | |||||
margin-top: 0px; | |||||
padding-top: 0px; | |||||
} | |||||
div#container { | div#container { | ||||
width: 800px; | width: 800px; | ||||
margin: 16px auto 16px auto; | |||||
margin: 20px auto 32px auto; | |||||
border: 1px solid #999; | border: 1px solid #999; | ||||
border-radius: 10px; | border-radius: 10px; | ||||
background-color: #FFF; | background-color: #FFF; | ||||
} | } | ||||
div#header { | div#header { | ||||
margin: 12px 12px 20px 12px; | |||||
padding: 15px 0px 15px 0px; | |||||
margin: 16px 16px 20px 16px; | |||||
padding: 30px 0px 30px 0px; | |||||
border: 1px solid #999; | border: 1px solid #999; | ||||
border-radius: 10px 10px 0px 0px; | border-radius: 10px 10px 0px 0px; | ||||
background-color: #DED; | background-color: #DED; | ||||
@@ -88,10 +101,14 @@ div#content { | |||||
div#footer { | div#footer { | ||||
font-size: 11px; | font-size: 11px; | ||||
text-align: center; | text-align: center; | ||||
padding: 6px 2px 6px 2px; | |||||
padding: 9px 4px 12px 4px; | |||||
color: #222; | color: #222; | ||||
} | } | ||||
div#post-info { | |||||
margin-left: 24px; | |||||
} | |||||
div#post { | div#post { | ||||
line-height: 1.5em; | line-height: 1.5em; | ||||
} | } | ||||
@@ -1,10 +1,9 @@ | |||||
/* | /* | ||||
This file has been shamelessly stolen from: | |||||
This file has been shamelessly stolen from (with modifications): | |||||
https://github.com/mojombo/tpw/blob/master/css/syntax.css | https://github.com/mojombo/tpw/blob/master/css/syntax.css | ||||
...which is released under the MIT license. | ...which is released under the MIT license. | ||||
*/ | */ | ||||
.highlight { background: #ffffff; } | |||||
.highlight .c { color: #999988; font-style: italic } /* Comment */ | .highlight .c { color: #999988; font-style: italic } /* Comment */ | ||||
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ | .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ | ||||
.highlight .k { font-weight: bold } /* Keyword */ | .highlight .k { font-weight: bold } /* Keyword */ | ||||
@@ -20,7 +19,7 @@ This file has been shamelessly stolen from: | |||||
.highlight .gh { color: #999999 } /* Generic.Heading */ | .highlight .gh { color: #999999 } /* Generic.Heading */ | ||||
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ | .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ | ||||
.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ | .highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ | ||||
.highlight .go { color: #888888 } /* Generic.Output */ | |||||
.highlight .go { color: #666666 } /* Generic.Output */ | |||||
.highlight .gp { color: #555555 } /* Generic.Prompt */ | .highlight .gp { color: #555555 } /* Generic.Prompt */ | ||||
.highlight .gs { font-weight: bold } /* Generic.Strong */ | .highlight .gs { font-weight: bold } /* Generic.Strong */ | ||||
.highlight .gu { color: #aaaaaa } /* Generic.Subheading */ | .highlight .gu { color: #aaaaaa } /* Generic.Subheading */ | ||||