A Python robot that edits Wikipedia and interacts with people over IRC https://en.wikipedia.org/wiki/User:EarwigBot
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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