| @@ -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. | ||||