Browse Source

Merge branch 'feature/statisticsbot' into develop

tags/v0.1^2
Ben Kurtovic 12 years ago
parent
commit
092b1f22c6
10 changed files with 154 additions and 24 deletions
  1. +3
    -0
      .gitignore
  2. +58
    -0
      bot/classes/base_task.py
  3. +1
    -1
      bot/commands/rights.py
  4. +8
    -5
      bot/config.py
  5. +8
    -8
      bot/rules.py
  6. +1
    -0
      bot/tasks/afc_copyvios.py
  7. +1
    -0
      bot/tasks/afc_dailycats.py
  8. +72
    -6
      bot/tasks/afc_statistics.py
  9. +1
    -2
      bot/wiki/category.py
  10. +1
    -2
      bot/wiki/page.py

+ 3
- 0
.gitignore View File

@@ -7,6 +7,9 @@ config.json
# Ignore cookies file:
.cookies

# Ignore statistics file:
statistics.txt

# Ignore OS X's crud:
.DS_Store



+ 58
- 0
bot/classes/base_task.py View File

@@ -1,8 +1,12 @@
# -*- coding: utf-8 -*-

import config
import wiki

class BaseTask(object):
"""A base class for bot tasks that edit Wikipedia."""
name = None
number = 0

def __init__(self):
"""Constructor for new tasks.
@@ -25,3 +29,57 @@ class BaseTask(object):
(e.g. start('mytask', action='save')).
"""
pass

def make_summary(self, comment):
"""Makes an edit summary by filling in variables in a config value.

config.wiki["summary"] is used, where $2 is replaced by the main
summary body, given as a method arg, and $1 is replaced by the task
number.

If the config value is not found, we just return the arg as-is.
"""
try:
summary = config.wiki["summary"]
except KeyError:
return comment
return summary.replace("$1", self.number).replace("$2", comment)

def shutoff_enabled(self, site=None):
"""Returns whether on-wiki shutoff is enabled for this task.

We check a certain page for certain content. This is determined by
our config file: config.wiki["shutoff"]["page"] is used as the title,
with $1 replaced by our username and $2 replaced by the task number,
and config.wiki["shutoff"]["disabled"] is used as the content.

If the page has that content or the page does not exist, then shutoff
is "disabled", meaning the bot is supposed to run normally, and we
return False. If the page's content is something other than what we
expect, shutoff is enabled, and we return True.

If a site is not provided, we'll try to use self.site if it's set.
Otherwise, we'll use our default site.
"""
if not site:
try:
site = self.site
except AttributeError:
site = wiki.get_site()

try:
cfg = config.wiki["shutoff"]
except KeyError:
return False
title = cfg.get("page", "User:$1/Shutoff/Task $2")
username = site.get_user().name()
title = title.replace("$1", username).replace("$2", self.number)
page = site.get_page(title)

try:
content = page.get()
except wiki.PageNotFoundError:
return False
if content == cfg.get("disabled", "run"):
return False
return True

+ 1
- 1
bot/commands/rights.py View File

@@ -4,7 +4,7 @@ from classes import BaseCommand
import wiki

class Command(BaseCommand):
"""Retrieve a list of rights for a given name."""
"""Retrieve a list of rights for a given username."""
name = "rights"

def check(self, data):


+ 8
- 5
bot/config.py View File

@@ -8,10 +8,11 @@ including encrypting and decrypting passwords and making a new config file from
scratch at the inital bot run.

Usually you'll just want to do "from core import config" and access config data
from within config's four global variables and one function:
from within config's five global variables and one function:

* config.components - a list of enabled components
* config.wiki - a dict of information about wiki-editing
* config.tasks - a dict of information for bot tasks
* config.irc - a dict of information about IRC
* config.metadata - a dict of miscellaneous information
* config.schedule() - returns a list of tasks scheduled to run at a given time
@@ -34,8 +35,8 @@ config_path = path.join(root_dir, "config.json")

_config = None # Holds data loaded from our config file

# Set our four easy-config-access global variables to None
components, wiki, irc, metadata = None, None, None, None
# Set our five easy-config-access global variables to None
components, wiki, tasks, irc, metadata = None, None, None, None, None

def _load():
"""Load data from our JSON config file (config.json) into _config."""
@@ -68,8 +69,9 @@ def load():
First, check if we have a valid config file, and if not, notify the user.
If there is no config file at all, offer to make one, otherwise exit.

Store data from our config file in four global variables (components, wiki,
irc, metadata) for easy access (as well as the internal _config variable).
Store data from our config file in five global variables (components, wiki,
tasks, irc, metadata) for easy access (as well as the internal _config
variable).

If everything goes well, return True if stored passwords are
encrypted in the file, or False if they are not.
@@ -88,6 +90,7 @@ def load():

components = _config.get("components", [])
wiki = _config.get("wiki", {})
tasks = _config.get("tasks", {})
irc = _config.get("irc", {})
metadata = _config.get("metadata", {})



+ 8
- 8
bot/rules.py View File

@@ -36,8 +36,8 @@ def process(rc):
chans.update(("##earwigbot", "#wikipedia-en-afc"))
if r_page.search(page_name):
tasks.start("afc_statistics", action="process_edit", page=rc.page)
tasks.start("afc_copyvios", action="process_edit", page=rc.page)
tasks.start("afc_statistics", action="edit", page=rc.page)
tasks.start("afc_copyvios", action="edit", page=rc.page)
chans.add("#wikipedia-en-afc")
elif r_ffu.match(page_name):
@@ -49,20 +49,20 @@ def process(rc):
elif rc.flags == "move" and (r_move1.match(comment) or
r_move2.match(comment)):
p = r_moved_pages.findall(rc.comment)[0]
tasks.start("afc_statistics", action="process_move", pages=p)
tasks.start("afc_copyvios", action="process_move", pages=p)
tasks.start("afc_statistics", action="move", page=p)
tasks.start("afc_copyvios", action="move", page=p)
chans.add("#wikipedia-en-afc")
elif rc.flags == "delete" and r_delete.match(comment):
p = r_deleted_page.findall(rc.comment)[0]
tasks.start("afc_statistics", action="process_delete", page=p)
tasks.start("afc_copyvios", action="process_delete", page=p)
tasks.start("afc_statistics", action="delete", page=p)
tasks.start("afc_copyvios", action="delete", page=p)
chans.add("#wikipedia-en-afc")
elif rc.flags == "restore" and r_restore.match(comment):
p = r_restored_page.findall(rc.comment)[0]
tasks.start("afc_statistics", action="process_restore", page=p)
tasks.start("afc_copyvios", action="process_restore", page=p)
tasks.start("afc_statistics", action="restore", page=p)
tasks.start("afc_copyvios", action="restore", page=p)
chans.add("#wikipedia-en-afc")
elif rc.flags == "protect" and r_protect.match(comment):


+ 1
- 0
bot/tasks/afc_copyvios.py View File

@@ -6,6 +6,7 @@ class Task(BaseTask):
"""A task to check newly-edited [[WP:AFC]] submissions for copyright
violations."""
name = "afc_copyvios"
number = 1

def __init__(self):
pass


+ 1
- 0
bot/tasks/afc_dailycats.py View File

@@ -5,6 +5,7 @@ from classes import BaseTask
class Task(BaseTask):
""" A task to create daily categories for [[WP:AFC]]."""
name = "afc_dailycats"
number = 3

def __init__(self):
pass


+ 72
- 6
bot/tasks/afc_statistics.py View File

@@ -1,17 +1,83 @@
# -*- coding: utf-8 -*-

import time
import re
from os import path

from classes import BaseTask
import config
import wiki

class Task(BaseTask):
"""A task to generate statistics for [[WP:AFC]] and save them to
[[Template:AFC_statistics]]."""
"""A task to generate statistics for WikiProject Articles for Creation.

Statistics are stored in the file indicated by self.filename,
"statistics.txt" in the bot's root directory being the default. They are
updated live while watching the recent changes IRC feed.

The bot saves its statistics once an hour, on the hour, to self.pagename.
In the live bot, this is "Template:AFC statistics".
"""
name = "afc_statistics"
number = 2

def __init__(self):
pass
self.filename = path.join(config.root_dir, "statistics.txt")
self.cfg = config.tasks.get(self.name, {})
self.pagename = cfg.get("page", "Template:AFC statistics")
default = "Updating statistics for [[WP:WPAFC|WikiProject Articles for creation]]."
self.summary = self.make_summary(cfg.get("summary", default))

def run(self, **kwargs):
time.sleep(5)
print kwargs
self.site = wiki.get_site()
action = kwargs.get("action")
if not action:
return
if action == "save":
self.save()
return

page = kwargs.get("page")
if page:
methods = {
"edit": self.process_edit,
"move": self.process_move,
"delete": self.process_delete,
"restore": self.process_restore,
}
method = methods.get(action)
if method:
method(page)

def save(self):
if self.shutoff_enabled():
return
try:
with open(self.filename) as fp:
statistics = fp.read()
except IOError:
pass

page = self.site.get_page(self.pagename)
text = page.get()
newtext = re.sub("(<!-- stat begin -->)(.*?)(<!-- stat end -->)",
statistics.join(("\\1", "\\3")), text,
flags=re.DOTALL)
if newtext == text:
return # Don't edit the page if we're not adding anything

newtext = re.sub("(<!-- sig begin -->)(.*?)(<!-- sig end -->)",
"\\1~~~ at ~~~~~\\3", newtext)
page.edit(newtext, self.summary, minor=True)

def process_edit(self, page):
pass

def process_move(self, page):
pass

def process_delete(self, page):
pass

def process_restore(self, page):
pass

+ 1
- 2
bot/wiki/category.py View File

@@ -18,8 +18,7 @@ class Category(Page):

def __repr__(self):
"""Returns the canonical string representation of the Category."""
res = ", ".join(("Category(title={0!r}", "follow_redirects={1!r}",
"site={2!r})"))
res = "Category(title={0!r}, follow_redirects={1!r}, site={2!r})"
return res.format(self._title, self._follow_redirects, self._site)

def __str__(self):


+ 1
- 2
bot/wiki/page.py View File

@@ -83,8 +83,7 @@ class Page(object):

def __repr__(self):
"""Returns the canonical string representation of the Page."""
res = ", ".join(("Page(title={0!r}", "follow_redirects={1!r}",
"site={2!r})"))
res = "Page(title={0!r}, follow_redirects={1!r}, site={2!r})"
return res.format(self._title, self._follow_redirects, self._site)

def __str__(self):


Loading…
Cancel
Save