@@ -220,6 +220,8 @@ provide the following attributes: | |||||
user, ``False`` if you cannot | user, ``False`` if you cannot | ||||
- :py:attr:`~earwigbot.wiki.user.User.gender`: the user's gender (``"male"``, | - :py:attr:`~earwigbot.wiki.user.User.gender`: the user's gender (``"male"``, | ||||
``"female"``, or ``"unknown"``) | ``"female"``, or ``"unknown"``) | ||||
- :py:attr:`~earwigbot.wiki.user.User.is_ip`: ``True`` if the user is an IP | |||||
address, IPv4 or IPv6, otherwise ``False`` | |||||
and the following methods: | and the following methods: | ||||
@@ -28,7 +28,7 @@ from time import mktime, sleep, time | |||||
import oursql | import oursql | ||||
from earwigbot import exceptions | |||||
from earwigbot import constants, exceptions | |||||
from earwigbot.tasks import Task | from earwigbot.tasks import Task | ||||
class DRNClerkBot(Task): | class DRNClerkBot(Task): | ||||
@@ -642,6 +642,12 @@ class DRNClerkBot(Task): | |||||
log = u"Trying to notify [[{0}]] with '{1}'" | log = u"Trying to notify [[{0}]] with '{1}'" | ||||
self.logger.debug(log.format(target, template)) | self.logger.debug(log.format(target, template)) | ||||
page = site.get_page(target) | page = site.get_page(target) | ||||
if page.namespace == constants.NS_USER_TALK: | |||||
user = site.get_user(target.split(":", 1)[1:]) | |||||
if not user.exists and not user.is_ip: | |||||
log = u"Skipping [[{0}]]; user does not exist and is not an IP" | |||||
self.logger.info(log.format(target)) | |||||
continue | |||||
try: | try: | ||||
text = page.get() | text = page.get() | ||||
except exceptions.PageNotFoundError: | except exceptions.PageNotFoundError: | ||||
@@ -695,7 +701,7 @@ class DRNClerkBot(Task): | |||||
def compile_row(self, case): | def compile_row(self, case): | ||||
"""Generate a single row of the chart from a dict via the database.""" | """Generate a single row of the chart from a dict via the database.""" | ||||
data = "|t={case_title}|d={title}|s={case_status}" | |||||
data = u"|t={case_title}|d={title}|s={case_status}" | |||||
data += "|cu={case_file_user}|cs={file_sortkey}|ct={file_time}" | data += "|cu={case_file_user}|cs={file_sortkey}|ct={file_time}" | ||||
if case["case_volunteer_user"]: | if case["case_volunteer_user"]: | ||||
data += "|vu={case_volunteer_user}|vs={volunteer_sortkey}|vt={volunteer_time}" | data += "|vu={case_volunteer_user}|vs={volunteer_sortkey}|vt={volunteer_time}" | ||||
@@ -22,6 +22,7 @@ | |||||
from logging import getLogger, NullHandler | from logging import getLogger, NullHandler | ||||
from time import gmtime, strptime | from time import gmtime, strptime | ||||
from socket import AF_INET, AF_INET6, error as socket_error, inet_pton | |||||
from earwigbot.exceptions import UserNotFoundError | from earwigbot.exceptions import UserNotFoundError | ||||
from earwigbot.wiki import constants | from earwigbot.wiki import constants | ||||
@@ -51,6 +52,7 @@ class User(object): | |||||
- :py:attr:`registration`: the time the user registered | - :py:attr:`registration`: the time the user registered | ||||
- :py:attr:`emailable`: ``True`` if you can email the user, or ``False`` | - :py:attr:`emailable`: ``True`` if you can email the user, or ``False`` | ||||
- :py:attr:`gender`: the user's gender ("male"/"female"/"unknown") | - :py:attr:`gender`: the user's gender ("male"/"female"/"unknown") | ||||
- :py:attr:`is_ip`: ``True`` if this is an IP address, or ``False`` | |||||
*Public methods:* | *Public methods:* | ||||
@@ -269,6 +271,22 @@ class User(object): | |||||
""" | """ | ||||
return self._get_attribute("_gender") | return self._get_attribute("_gender") | ||||
@property | |||||
def is_ip(self): | |||||
"""``True`` if the user is an IP address, or ``False`` otherwise. | |||||
This tests for IPv4 and IPv6 using :py:func:`socket.inet_pton` on the | |||||
username. No API queries are made. | |||||
""" | |||||
try: | |||||
inet_pton(AF_INET, self.name) | |||||
except socket_error: | |||||
try: | |||||
inet_pton(AF_INET6, self.name) | |||||
except socket_error: | |||||
return False | |||||
return True | |||||
def reload(self): | def reload(self): | ||||
"""Forcibly reload the user's attributes. | """Forcibly reload the user's attributes. | ||||