Additional IRC commands and bot tasks for EarwigBot 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.

205 lines
8.6 KiB

  1. # Copyright (C) 2009-2014 Ben Kurtovic <ben.kurtovic@gmail.com>
  2. #
  3. # Permission is hereby granted, free of charge, to any person obtaining a copy
  4. # of this software and associated documentation files (the "Software"), to deal
  5. # in the Software without restriction, including without limitation the rights
  6. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. # copies of the Software, and to permit persons to whom the Software is
  8. # furnished to do so, subject to the following conditions:
  9. #
  10. # The above copyright notice and this permission notice shall be included in
  11. # all copies or substantial portions of the Software.
  12. #
  13. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  19. # SOFTWARE.
  20. import re
  21. from earwigbot.commands import Command
  22. class AfCStatus(Command):
  23. """Get the number of pending AfC submissions, open redirect requests, and
  24. open file upload requests."""
  25. name = "status"
  26. commands = ["status", "count", "num", "number"]
  27. hooks = ["join", "msg"]
  28. def setup(self):
  29. try:
  30. self.ignore_list = self.config.commands[self.name]["ignoreList"]
  31. except KeyError:
  32. try:
  33. ignores = self.config.tasks["afc_statistics"]["ignoreList"]
  34. self.ignore_list = ignores
  35. except KeyError:
  36. self.ignore_list = []
  37. def check(self, data):
  38. if data.is_command and data.command in self.commands:
  39. return True
  40. try:
  41. if data.line[1] == "JOIN" and data.chan == "#wikipedia-en-afc":
  42. if data.nick != self.config.irc["frontend"]["nick"]:
  43. return True
  44. except IndexError:
  45. pass
  46. return False
  47. def process(self, data):
  48. self.site = self.bot.wiki.get_site()
  49. if data.line[1] == "JOIN":
  50. status = " ".join(("\x02Current status:\x0f", self.get_status()))
  51. self.notice(data.nick, status)
  52. return
  53. if data.args:
  54. action = data.args[0].lower()
  55. if action.startswith("sub") or action == "s":
  56. subs = self.count_submissions()
  57. msg = "There are \x0305{0}\x0f pending AfC submissions (\x0302WP:AFC\x0f)."
  58. self.reply(data, msg.format(subs))
  59. elif action.startswith("redir") or action == "r":
  60. redirs = self.count_redirects()
  61. msg = "There are \x0305{0}\x0f open redirect requests (\x0302WP:AFC/R\x0f)."
  62. self.reply(data, msg.format(redirs))
  63. elif action.startswith("file") or action == "f":
  64. files = self.count_redirects()
  65. msg = "There are \x0305{0}\x0f open file upload requests (\x0302WP:FFU\x0f)."
  66. self.reply(data, msg.format(files))
  67. elif action.startswith("agg") or action == "a":
  68. try:
  69. agg_num = int(data.args[1])
  70. except IndexError:
  71. agg_data = (
  72. self.count_submissions(),
  73. self.count_redirects(),
  74. self.count_files(),
  75. )
  76. agg_num = self.get_aggregate_number(agg_data)
  77. except ValueError:
  78. msg = "\x0303{0}\x0f isn't a number!"
  79. self.reply(data, msg.format(data.args[1]))
  80. return
  81. aggregate = self.get_aggregate(agg_num)
  82. msg = "Aggregate is \x0305{0}\x0f (AfC {1})."
  83. self.reply(data, msg.format(agg_num, aggregate))
  84. elif action.startswith("g13_e") or action.startswith("g13e"):
  85. g13_eli = self.count_g13_eligible()
  86. msg = "There are \x0305{0}\x0f CSD:G13-eligible pages."
  87. self.reply(data, msg.format(g13_eli))
  88. elif action.startswith("g13_a") or action.startswith("g13a"):
  89. g13_noms = self.count_g13_active()
  90. msg = "There are \x0305{0}\x0f active CSD:G13 nominations."
  91. self.reply(data, msg.format(g13_noms))
  92. elif action.startswith("nocolor") or action == "n":
  93. self.reply(data, self.get_status(color=False))
  94. else:
  95. msg = (
  96. "Unknown argument: \x0303{0}\x0f. Valid args are "
  97. + "'subs', 'redirs', 'files', 'agg', 'nocolor', "
  98. + "'g13_eligible', 'g13_active'."
  99. )
  100. self.reply(data, msg.format(data.args[0]))
  101. else:
  102. self.reply(data, self.get_status())
  103. def get_status(self, color=True):
  104. subs = self.count_submissions()
  105. redirs = self.count_redirects()
  106. files = self.count_files()
  107. agg_num = self.get_aggregate_number((subs, redirs, files))
  108. aggregate = self.get_aggregate(agg_num)
  109. if color:
  110. msg = "Articles for creation {0} (\x0302AFC\x0f: \x0305{1}\x0f; \x0302AFC/R\x0f: \x0305{2}\x0f; \x0302FFU\x0f: \x0305{3}\x0f)."
  111. else:
  112. msg = "Articles for creation {0} (AFC: {1}; AFC/R: {2}; FFU: {3})."
  113. return msg.format(aggregate, subs, redirs, files)
  114. def count_g13_eligible(self):
  115. """
  116. Returns the number of G13 Eligible AfC Submissions (count of
  117. Category:G13 eligible AfC submissions)
  118. """
  119. return self.site.get_category("G13 eligible AfC submissions").pages
  120. def count_g13_active(self):
  121. """
  122. Returns the number of active CSD:G13 nominations ( count of
  123. Category:Candidates for speedy deletion as abandoned AfC submissions)
  124. """
  125. catname = "Candidates for speedy deletion as abandoned AfC submissions"
  126. return self.site.get_category(catname).pages
  127. def count_submissions(self):
  128. """Returns the number of open AfC submissions (count of CAT:PEND)."""
  129. minus = len(self.ignore_list)
  130. return self.site.get_category("Pending AfC submissions").pages - minus
  131. def count_redirects(self):
  132. """Returns the number of open redirect submissions. Calculated as the
  133. total number of submissions minus the closed ones."""
  134. title = "Wikipedia:Articles for creation/Redirects and categories"
  135. content = self.site.get_page(title).get()
  136. total = len(re.findall("^\s*==(.*?)==\s*$", content, re.MULTILINE))
  137. closed = content.lower().count("{{afc-c|b}}")
  138. redirs = total - closed
  139. return redirs
  140. def count_files(self):
  141. """Returns the number of open WP:FFU (Files For Upload) requests.
  142. Calculated as the total number of requests minus the closed ones."""
  143. content = self.site.get_page("Wikipedia:Files for upload").get()
  144. total = len(re.findall("^\s*==(.*?)==\s*$", content, re.MULTILINE))
  145. closed = content.lower().count("{{ifu-c|b}}")
  146. files = total - closed
  147. return files
  148. def get_aggregate(self, num):
  149. """Returns a human-readable AfC status based on the number of pending
  150. AfC submissions, open redirect requests, and open FFU requests. This
  151. does not match {{AfC status}} directly because the algorithm factors in
  152. WP:AFC/R and WP:FFU while the template only looks at the main
  153. submissions. The reasoning is that AFC/R and FFU are still part of
  154. the project, so even if there are no pending submissions, a backlog at
  155. FFU (for example) indicates that our work is *not* done and the
  156. project-wide backlog is most certainly *not* clear."""
  157. if num == 0:
  158. return "is \x02\x0303clear\x0f"
  159. elif num <= 200:
  160. return "is \x0303almost clear\x0f"
  161. elif num <= 400:
  162. return "is \x0312normal\x0f"
  163. elif num <= 600:
  164. return "is \x0307lightly backlogged\x0f"
  165. elif num <= 900:
  166. return "is \x0304backlogged\x0f"
  167. elif num <= 1200:
  168. return "is \x02\x0304heavily backlogged\x0f"
  169. else:
  170. return "is \x02\x1f\x0304severely backlogged\x0f"
  171. def get_aggregate_number(self, arg):
  172. """Returns an 'aggregate number' based on the real number of pending
  173. submissions in CAT:PEND (subs), open redirect submissions in WP:AFC/R
  174. (redirs), and open files-for-upload requests in WP:FFU (files)."""
  175. (subs, redirs, files) = arg
  176. num = subs + (redirs / 2) + (files / 2)
  177. return num