A Python robot that edits Wikipedia and interacts with people over IRC https://en.wikipedia.org/wiki/User:EarwigBot
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

154 строки
7.0 KiB

  1. # -*- coding: utf-8 -*-
  2. # Report the status of AFC submissions, either as an automatic message on join or a request via !status.
  3. import json
  4. import re
  5. import urllib
  6. from irc.classes import BaseCommand
  7. class AFCStatus(BaseCommand):
  8. def get_hooks(self):
  9. return ["join", "msg"]
  10. def get_help(self, command):
  11. return "Get the number of pending AfC submissions, open redirect requests, and open file upload requests."
  12. def check(self, data):
  13. if data.is_command and data.command in ["status", "count", "num", "number", "afc_status"]:
  14. return True
  15. try:
  16. if data.line[1] == "JOIN" and data.chan == "#wikipedia-en-afc":
  17. return True
  18. except IndexError:
  19. pass
  20. return False
  21. def process(self, data):
  22. if data.line[1] == "JOIN":
  23. notice = self.get_join_notice()
  24. self.connection.notice(data.nick, notice)
  25. return
  26. if data.args:
  27. action = data.args[0].lower()
  28. if action.startswith("sub") or action == "s":
  29. subs = self.count_submissions()
  30. self.connection.reply(data, "there are currently %s pending AfC submissions." % subs)
  31. elif action.startswith("redir") or action == "r":
  32. redirs = self.count_redirects()
  33. self.connection.reply(data, "there are currently %s open redirect requests." % redirs)
  34. elif action.startswith("file") or action == "f":
  35. files = self.count_redirects()
  36. self.connection.reply(data, "there are currently %s open file upload requests." % files)
  37. elif action.startswith("agg") or action == "a":
  38. try:
  39. agg_num = int(data.args[1])
  40. except IndexError:
  41. agg_data = (self.count_submissions(), self.count_redirects(), self.count_files())
  42. agg_num = self.get_aggregate_number(agg_data)
  43. except ValueError:
  44. self.connection.reply(data, "\x0303%s\x0301 isn't a number!" % data.args[1])
  45. return
  46. aggregate = self.get_aggregate(agg_num)
  47. self.connection.reply(data, "aggregate is currently %s (AfC %s)." % (agg_num, aggregate))
  48. elif action.startswith("join") or action == "j":
  49. notice = self.get_join_notice()
  50. self.connection.reply(data, notice)
  51. else:
  52. self.connection.reply(data, "unknown argument: \x0303%s\x0301. Valid args are 'subs', 'redirs', 'files', 'agg', and 'join'." % data.args[0])
  53. else:
  54. subs = self.count_submissions()
  55. redirs = self.count_redirects()
  56. files = self.count_files()
  57. self.connection.reply(data, "there are currently %s pending submissions, %s open redirect requests, and %s open file upload requests."
  58. % (subs, redirs, files))
  59. def get_join_notice(self):
  60. subs = self.count_submissions()
  61. redirs = self.count_redirects()
  62. files = self.count_files()
  63. agg_num = self.get_aggregate_number((subs, redirs, files))
  64. aggregate = self.get_aggregate(agg_num)
  65. return ("\x02Current status:\x0F Articles for Creation %s (\x0302AFC\x0301: \x0305%s\x0301; \x0302AFC/R\x0301: \x0305%s\x0301; \x0302FFU\x0301: \x0305%s\x0301)"
  66. % (aggregate, subs, redirs, files))
  67. def count_submissions(self):
  68. """Returns the number of open AFC submissions (count of CAT:PEND)."""
  69. params = {'action': 'query', 'list': 'categorymembers', 'cmlimit':'500', 'format': 'json'}
  70. params['cmtitle'] = "Category:Pending_AfC_submissions"
  71. data = urllib.urlencode(params)
  72. raw = urllib.urlopen("http://en.wikipedia.org/w/api.php", data).read()
  73. res = json.loads(raw)
  74. subs = len(res['query']['categorymembers'])
  75. subs -= 2 # remove [[Wikipedia:Articles for creation/Redirects]] and [[Wikipedia:Files for upload]], which aren't real submissions
  76. return subs
  77. def count_redirects(self):
  78. """Returns the number of open redirect submissions. Calculated as the
  79. total number of submissions minus the closed ones."""
  80. content = self.get_page("Wikipedia:Articles_for_creation/Redirects")
  81. total = len(re.findall("^\s*==(.*?)==\s*$", content, re.MULTILINE))
  82. closed = content.lower().count("{{afc-c|b}}")
  83. redirs = total - closed
  84. return redirs
  85. def count_files(self):
  86. """Returns the number of open WP:FFU (Files For Upload) requests.
  87. Calculated as the total number of requests minus the closed ones."""
  88. content = self.get_page("Wikipedia:Files_for_upload")
  89. total = len(re.findall("^\s*==(.*?)==\s*$", content, re.MULTILINE))
  90. closed = content.lower().count("{{ifu-c|b}}")
  91. files = total - closed
  92. return files
  93. def get_page(self, pagename):
  94. """Simple method to return the content of the page 'pagename'. Will be
  95. a part of wiki/tools/ when I finish that."""
  96. params = {'action': 'query', 'prop': 'revisions', 'rvprop':'content', 'rvlimit':'1', 'format': 'json'}
  97. params['titles'] = pagename
  98. data = urllib.urlencode(params)
  99. raw = urllib.urlopen("http://en.wikipedia.org/w/api.php", data).read()
  100. res = json.loads(raw)
  101. pageid = res['query']['pages'].keys()[0]
  102. content = res['query']['pages'][pageid]['revisions'][0]['*']
  103. return content
  104. def get_aggregate(self, num):
  105. """Returns a human-readable AFC status based on the number of pending
  106. AFC submissions, open redirect requests, and open FFU requests. This
  107. does not match {{AFC status}} directly because my algorithm factors in
  108. WP:AFC/R and WP:FFU while the template only looks at the main
  109. submissions. My reasoning is that AFC/R and FFU are still part of
  110. the project, so even if there are no pending submissions, a backlog at
  111. FFU (for example) indicates that our work is *not* done and the
  112. project-wide backlog is most certainly *not* clear."""
  113. if num == 0:
  114. return "is \x02\x0303clear\x0301\x0F"
  115. elif num < 125: # < 25 subs
  116. return "is \x0303almost clear\x0301"
  117. elif num < 200: # < 40 subs
  118. return "is \x0312normal\x0301"
  119. elif num < 275: # < 55 subs
  120. return "is \x0307lightly backlogged\x0301"
  121. elif num < 350: # < 70 subs
  122. return "is \x0304backlogged\x0301"
  123. elif num < 500: # < 100 subs
  124. return "is \x02\x0304heavily backlogged\x0301\x0F"
  125. else: # >= 100 subs
  126. return "is \x02\x1F\x0304severely backlogged\x0301\x0F"
  127. def get_aggregate_number(self, (subs, redirs, files)):
  128. """Returns an 'aggregate number' based on the real number of pending
  129. submissions in CAT:PEND (subs), open redirect submissions in WP:AFC/R
  130. (redirs), and open files-for-upload requests in WP:FFU (files)."""
  131. num = (subs * 5) + (redirs * 2) + (files * 2)
  132. return num