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.

103 lines
3.2 KiB

  1. # -*- coding: utf-8 -*-
  2. """
  3. EarwigBot's Wiki Task Manager
  4. This package provides the wiki bot "tasks" EarwigBot runs. Here in __init__,
  5. you can find some functions used to load and run these tasks.
  6. """
  7. import time
  8. import traceback
  9. import threading
  10. import os
  11. import config
  12. __all__ = ["load", "schedule", "start", "get_all"]
  13. # Store loaded tasks as a dict where the key is the task name and the value is
  14. # an instance of the task class:
  15. _tasks = {}
  16. def _load_task(f):
  17. """Look in a given file for the task class."""
  18. global _tasks
  19. module = f[:-3] # strip .py from end
  20. try:
  21. exec "from wiki.tasks import %s as m" % module
  22. except: # importing the file failed for some reason...
  23. print "Couldn't load task file %s:" % f
  24. traceback.print_exc()
  25. return
  26. try:
  27. task_class = m.Task
  28. except:
  29. print "Couldn't find or get task class in file %s:" % f
  30. traceback.print_exc()
  31. return
  32. task_name = task_class.task_name
  33. _tasks[task_name] = task_class()
  34. print "Added task %s from bot/tasks/%s..." % (task_name, f)
  35. def _wrapper(task, **kwargs):
  36. """Wrapper for task classes: run the task and catch any errors."""
  37. try:
  38. task.run(**kwargs)
  39. except:
  40. error = "Task '{0}' raised an exception and had to stop:"
  41. print error.format(task.task_name)
  42. traceback.print_exc()
  43. else:
  44. print "Task '{0}' finished without error.".format(task.task_name)
  45. def load():
  46. """Load all valid task classes from bot/tasks/, and add them to the _tasks
  47. variable."""
  48. files = os.listdir(os.path.join("bot", "tasks"))
  49. files.sort() # alphabetically sort all files in wiki/tasks/
  50. for f in files:
  51. if not os.path.isfile(os.path.join("bot", "tasks", f)):
  52. continue # ignore non-files
  53. if f.startswith("_") or not f.endswith(".py"):
  54. continue # ignore non-python files or files beginning with an _
  55. load_class_from_file(f)
  56. print "Found %s tasks: %s." % (len(_tasks), ', '.join(_tasks.keys()))
  57. def schedule(now=time.gmtime()):
  58. """Start all tasks that are supposed to be run at a given time."""
  59. tasks = config.schedule(now.tm_min, now.tm_hour, now.tm_mday, now.tm_mon,
  60. now.tm_wday) # get list of tasks to run this turn
  61. for task in tasks:
  62. if isinstance(task, list): # they've specified kwargs
  63. start(task[0], **task[1]) # so pass those to start_task
  64. else: # otherwise, just pass task_name
  65. start(task)
  66. def start(task_name, **kwargs):
  67. """Start a given task in a new thread. Pass args to the task's run()
  68. function."""
  69. print "Starting task '{0}' in a new thread...".format(task_name)
  70. try:
  71. task = _tasks[task_name]
  72. except KeyError:
  73. error = "Couldn't find task '{0}': bot/tasks/{0}.py does not exist."
  74. print error.format(task_name)
  75. return
  76. task_thread = threading.Thread(target=lambda: _wrapper(task, **kwargs))
  77. start_time = time.strftime("%b %d %H:%M:%S")
  78. task_thread.name = "{0} ({1})".format(task_name, start_time)
  79. # Stop bot task threads automagically if the main bot stops:
  80. task_thread.daemon = True
  81. task_thread.start()
  82. def get_all():
  83. """Return our dict of all loaded tasks."""
  84. return _tasks