A Python robot that edits Wikipedia and interacts with people over IRC https://en.wikipedia.org/wiki/User:EarwigBot
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

268 行
10 KiB

  1. # -*- coding: utf-8 -*-
  2. #
  3. # Copyright (C) 2009-2012 by Ben Kurtovic <ben.kurtovic@verizon.net>
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a copy
  6. # of this software and associated documentation files (the "Software"), to deal
  7. # in the Software without restriction, including without limitation the rights
  8. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. # copies of the Software, and to permit persons to whom the Software is
  10. # furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. # SOFTWARE.
  22. from time import gmtime, strptime
  23. from earwigbot.wiki.constants import *
  24. from earwigbot.wiki.exceptions import UserNotFoundError
  25. from earwigbot.wiki.page import Page
  26. __all__ = ["User"]
  27. class User(object):
  28. """
  29. EarwigBot's Wiki Toolset: User Class
  30. Represents a User on a given Site. Has methods for getting a bunch of
  31. information about the user, such as editcount and user rights, methods for
  32. returning the user's userpage and talkpage, etc.
  33. Attributes:
  34. name -- the user's username
  35. exists -- True if the user exists, or False if they do not
  36. userid -- an integer ID representing the user
  37. blockinfo -- information about any current blocks on the user
  38. groups -- a list of the user's groups
  39. rights -- a list of the user's rights
  40. editcount -- the number of edits made by the user
  41. registration -- the time the user registered as a time.struct_time
  42. emailable -- True if you can email the user, False if you cannot
  43. gender -- the user's gender ("male", "female", or "unknown")
  44. Public methods:
  45. reload -- forcibly reload the user's attributes
  46. get_userpage -- returns a Page object representing the user's userpage
  47. get_talkpage -- returns a Page object representing the user's talkpage
  48. """
  49. def __init__(self, site, name):
  50. """Constructor for new User instances.
  51. Takes two arguments, a Site object (necessary for doing API queries),
  52. and the name of the user, preferably without "User:" in front, although
  53. this prefix will be automatically removed by the API if given.
  54. You can also use site.get_user() instead, which returns a User object,
  55. and is preferred.
  56. We won't do any API queries yet for basic information about the user -
  57. save that for when the information is requested.
  58. """
  59. self._site = site
  60. self._name = name
  61. def __repr__(self):
  62. """Returns the canonical string representation of the User."""
  63. return "User(name={0!r}, site={1!r})".format(self._name, self._site)
  64. def __str__(self):
  65. """Returns a nice string representation of the User."""
  66. return '<User "{0}" of {1}>'.format(self.name(), str(self._site))
  67. def _get_attribute(self, attr, force):
  68. """Internally used to get an attribute by name.
  69. We'll call _load_attributes() to get this (and all other attributes)
  70. from the API if it is not already defined. If `force` is True, we'll
  71. re-load them even if they've already been loaded.
  72. Raises UserNotFoundError if a nonexistant user prevents us from
  73. returning a certain attribute.
  74. """
  75. if not hasattr(self, attr) or force:
  76. self._load_attributes()
  77. if self._exists is False:
  78. e = "User '{0}' does not exist.".format(self._name)
  79. raise UserNotFoundError(e)
  80. return getattr(self, attr)
  81. def _load_attributes(self):
  82. """Internally used to load all attributes from the API.
  83. Normally, this is called by _get_attribute() when a requested attribute
  84. is not defined. This defines it.
  85. """
  86. params = {"action": "query", "list": "users", "ususers": self._name,
  87. "usprop": "blockinfo|groups|rights|editcount|registration|emailable|gender"}
  88. result = self._site._api_query(params)
  89. res = result["query"]["users"][0]
  90. # normalize our username in case it was entered oddly
  91. self._name = res["name"]
  92. try:
  93. self._userid = res["userid"]
  94. except KeyError: # userid is missing, so user does not exist
  95. self._exists = False
  96. return
  97. self._exists = True
  98. try:
  99. self._blockinfo = {
  100. "by": res["blockedby"],
  101. "reason": res["blockreason"],
  102. "expiry": res["blockexpiry"]
  103. }
  104. except KeyError:
  105. self._blockinfo = False
  106. self._groups = res["groups"]
  107. try:
  108. self._rights = res["rights"].values()
  109. except AttributeError:
  110. self._rights = res["rights"]
  111. self._editcount = res["editcount"]
  112. reg = res["registration"]
  113. try:
  114. self._registration = strptime(reg, "%Y-%m-%dT%H:%M:%SZ")
  115. except TypeError:
  116. # Sometimes the API doesn't give a date; the user's probably really
  117. # old. There's nothing else we can do!
  118. self._registration = gmtime(0)
  119. try:
  120. res["emailable"]
  121. except KeyError:
  122. self._emailable = False
  123. else:
  124. self._emailable = True
  125. self._gender = res["gender"]
  126. def name(self, force=False):
  127. """Returns the user's name.
  128. If `force` is True, we will load the name from the API and return that.
  129. This could potentially return a "normalized" version of the name - for
  130. example, without a "User:" prefix or without underscores. Unlike other
  131. attribute getters, this will never make an API query without `force`.
  132. Note that if another attribute getter, like exists(), has already been
  133. called, then the username has already been normalized.
  134. """
  135. if force:
  136. self._load_attributes()
  137. return self._name
  138. def exists(self, force=False):
  139. """Returns True if the user exists, or False if they do not.
  140. Makes an API query if `force` is True or if we haven't made one
  141. already.
  142. """
  143. if not hasattr(self, "_exists") or force:
  144. self._load_attributes()
  145. return self._exists
  146. def userid(self, force=False):
  147. """Returns an integer ID used by MediaWiki to represent the user.
  148. Raises UserNotFoundError if the user does not exist. Makes an API query
  149. if `force` is True or if we haven't made one already.
  150. """
  151. return self._get_attribute("_userid", force)
  152. def blockinfo(self, force=False):
  153. """Returns information about a current block on the user.
  154. If the user is not blocked, returns False. If they are, returns a dict
  155. with three keys: "by" is the blocker's username, "reason" is the reason
  156. why they were blocked, and "expiry" is when the block expires.
  157. Raises UserNotFoundError if the user does not exist. Makes an API query
  158. if `force` is True or if we haven't made one already.
  159. """
  160. return self._get_attribute("_blockinfo", force)
  161. def groups(self, force=False):
  162. """Returns a list of groups this user is in, including "*".
  163. Raises UserNotFoundError if the user does not exist. Makes an API query
  164. if `force` is True or if we haven't made one already.
  165. """
  166. return self._get_attribute("_groups", force)
  167. def rights(self, force=False):
  168. """Returns a list of this user's rights.
  169. Raises UserNotFoundError if the user does not exist. Makes an API query
  170. if `force` is True or if we haven't made one already.
  171. """
  172. return self._get_attribute("_rights", force)
  173. def editcount(self, force=False):
  174. """Returns the number of edits made by the user.
  175. Raises UserNotFoundError if the user does not exist. Makes an API query
  176. if `force` is True or if we haven't made one already.
  177. """
  178. return self._get_attribute("_editcount", force)
  179. def registration(self, force=False):
  180. """Returns the time the user registered as a time.struct_time object.
  181. Raises UserNotFoundError if the user does not exist. Makes an API query
  182. if `force` is True or if we haven't made one already.
  183. """
  184. return self._get_attribute("_registration", force)
  185. def emailable(self, force=False):
  186. """Returns True if the user can be emailed, or False if they cannot.
  187. Raises UserNotFoundError if the user does not exist. Makes an API query
  188. if `force` is True or if we haven't made one already.
  189. """
  190. return self._get_attribute("_emailable", force)
  191. def gender(self, force=False):
  192. """Returns the user's gender.
  193. Can return either "male", "female", or "unknown", if they did not
  194. specify it.
  195. Raises UserNotFoundError if the user does not exist. Makes an API query
  196. if `force` is True or if we haven't made one already.
  197. """
  198. return self._get_attribute("_gender", force)
  199. def get_userpage(self):
  200. """Returns a Page object representing the user's userpage.
  201. No checks are made to see if it exists or not. Proper site namespace
  202. conventions are followed.
  203. """
  204. prefix = self._site.namespace_id_to_name(NS_USER)
  205. pagename = ':'.join((prefix, self._name))
  206. return Page(self._site, pagename)
  207. def get_talkpage(self):
  208. """Returns a Page object representing the user's talkpage.
  209. No checks are made to see if it exists or not. Proper site namespace
  210. conventions are followed.
  211. """
  212. prefix = self._site.namespace_id_to_name(NS_USER_TALK)
  213. pagename = ':'.join((prefix, self._name))
  214. return Page(self._site, pagename)