A Python robot that edits Wikipedia and interacts with people over IRC https://en.wikipedia.org/wiki/User:EarwigBot
25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

180 satır
5.9 KiB

  1. #! /usr/bin/env python
  2. # Copyright (C) 2009-2015 Ben Kurtovic <ben.kurtovic@gmail.com>
  3. #
  4. # Permission is hereby granted, free of charge, to any person obtaining a copy
  5. # of this software and associated documentation files (the "Software"), to deal
  6. # in the Software without restriction, including without limitation the rights
  7. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. # copies of the Software, and to permit persons to whom the Software is
  9. # furnished to do so, subject to the following conditions:
  10. #
  11. # The above copyright notice and this permission notice shall be included in
  12. # all copies or substantial portions of the Software.
  13. #
  14. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. # SOFTWARE.
  21. """
  22. usage: :command:`earwigbot [-h] [-v] [-d | -q] [-t NAME] [PATH] ...`
  23. This is EarwigBot's command-line utility, enabling you to easily start the bot
  24. or run specific tasks.
  25. .. glossary::
  26. ``PATH``
  27. path to the bot's working directory, which will be created if it doesn't
  28. exist; current directory assumed if not specified
  29. ``-h``, ``--help``
  30. show this help message and exit
  31. ``-v``, ``--version``
  32. show program's version number and exit
  33. ``-d``, ``--debug``
  34. print all logs, including ``DEBUG``-level messages
  35. ``-q``, ``--quiet``
  36. don't print any logs except warnings and errors
  37. ``-t NAME``, ``--task NAME``
  38. given the name of a task, the bot will run it instead of the main bot and
  39. then exit
  40. ``TASK_ARGS``
  41. with --task, will pass any remaining arguments to the task's
  42. :py:meth:`.Task.run` method
  43. """
  44. import logging
  45. from argparse import REMAINDER, Action, ArgumentParser
  46. from os import path
  47. from time import sleep
  48. from earwigbot import __version__
  49. from earwigbot.bot import Bot
  50. __all__ = ["main"]
  51. class _StoreTaskArg(Action):
  52. """A custom argparse action to read remaining command-line arguments."""
  53. def __call__(self, parser, namespace, values, option_string=None):
  54. kwargs = {}
  55. name = None
  56. for value in values:
  57. if value.startswith("-") and "=" in value:
  58. key, value = value.split("=", 1)
  59. self.insert(kwargs, key.lstrip("-"), value)
  60. elif name:
  61. if value.startswith("-"):
  62. if name not in kwargs:
  63. kwargs[name] = True
  64. name = value.lstrip("-")
  65. else:
  66. self.insert(kwargs, name, value)
  67. name = None
  68. else:
  69. if value.startswith("-"):
  70. name = value.lstrip("-")
  71. if name and name not in kwargs:
  72. kwargs[name] = True
  73. namespace.task_args = kwargs
  74. def insert(self, kwargs, key, value):
  75. """Add a key/value pair to kwargs; support multiple values per key."""
  76. if key in kwargs:
  77. try:
  78. kwargs[key].append(value)
  79. except AttributeError:
  80. kwargs[key] = [kwargs[key], value]
  81. else:
  82. kwargs[key] = value
  83. def main():
  84. """Main entry point for the command-line utility."""
  85. version = f"EarwigBot v{__version__}"
  86. desc = """This is EarwigBot's command-line utility, enabling you to easily
  87. start the bot or run specific tasks."""
  88. parser = ArgumentParser(description=desc)
  89. parser.add_argument(
  90. "path",
  91. nargs="?",
  92. metavar="PATH",
  93. default=path.curdir,
  94. help="""path to the bot's working directory, which will
  95. be created if it doesn't exist; current
  96. directory assumed if not specified""",
  97. )
  98. parser.add_argument("-v", "--version", action="version", version=version)
  99. logger = parser.add_mutually_exclusive_group()
  100. logger.add_argument(
  101. "-d",
  102. "--debug",
  103. action="store_true",
  104. help="print all logs, including DEBUG-level messages",
  105. )
  106. logger.add_argument(
  107. "-q",
  108. "--quiet",
  109. action="store_true",
  110. help="don't print any logs except warnings and errors",
  111. )
  112. parser.add_argument(
  113. "-t",
  114. "--task",
  115. metavar="NAME",
  116. help="""given the name of a task, the bot will run it
  117. instead of the main bot and then exit""",
  118. )
  119. parser.add_argument(
  120. "task_args",
  121. nargs=REMAINDER,
  122. action=_StoreTaskArg,
  123. metavar="TASK_ARGS",
  124. help="""with --task, will pass these arguments to the
  125. task's run() method""",
  126. )
  127. args = parser.parse_args()
  128. if not args.task and args.task_args:
  129. unrecognized = " ".join(args.task_args)
  130. parser.error(f"unrecognized arguments: {unrecognized}")
  131. level = logging.INFO
  132. if args.debug:
  133. level = logging.DEBUG
  134. elif args.quiet:
  135. level = logging.WARNING
  136. print(version)
  137. print()
  138. bot = Bot(path.abspath(args.path), level=level)
  139. if args.task:
  140. thread = bot.tasks.start(args.task, **args.task_args)
  141. if not thread:
  142. return
  143. try:
  144. while thread.is_alive(): # Keep it alive; it's a daemon
  145. sleep(1)
  146. except KeyboardInterrupt:
  147. pass
  148. finally:
  149. if thread.is_alive():
  150. bot.tasks.logger.warn("The task will be killed")
  151. else:
  152. try:
  153. bot.run()
  154. except KeyboardInterrupt:
  155. pass
  156. finally:
  157. if bot.is_running:
  158. bot.stop()
  159. if __name__ == "__main__":
  160. main()