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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  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 os
  8. import sys
  9. import threading
  10. import time
  11. import traceback
  12. from classes import BaseTask
  13. import config
  14. __all__ = ["load", "schedule", "start", "get_all"]
  15. # Base directory when searching for tasks:
  16. base_dir = os.path.join(config.root_dir, "bot", "tasks")
  17. # Store loaded tasks as a dict where the key is the task name and the value is
  18. # an instance of the task class:
  19. _tasks = {}
  20. def _load_task(filename):
  21. """Try to load a specific task from a module, identified by file name."""
  22. global _tasks
  23. # Strip .py from the end of the filename and join with our package name:
  24. name = ".".join(("tasks", filename[:-3]))
  25. try:
  26. __import__(name)
  27. except:
  28. print "Couldn't load file {0}:".format(filename)
  29. traceback.print_exc()
  30. return
  31. task = sys.modules[name].Task()
  32. if not isinstance(task, BaseTask):
  33. return
  34. _tasks[task.name] = task
  35. print "Added task {0}...".format(task.name)
  36. def _wrapper(task, **kwargs):
  37. """Wrapper for task classes: run the task and catch any errors."""
  38. try:
  39. task.run(**kwargs)
  40. except:
  41. error = "Task '{0}' raised an exception and had to stop:"
  42. print error.format(task.name)
  43. traceback.print_exc()
  44. else:
  45. print "Task '{0}' finished without error.".format(task.name)
  46. def load():
  47. """Load all valid tasks from bot/tasks/, into the _tasks variable."""
  48. files = os.listdir(base_dir)
  49. files.sort()
  50. for filename in files:
  51. if filename.startswith("_") or not filename.endswith(".py"):
  52. continue
  53. try:
  54. _load_task(filename)
  55. except AttributeError:
  56. pass # The file is doesn't contain a task, so just move on
  57. print "Found {0} tasks: {1}.".format(len(_tasks), ', '.join(_tasks.keys()))
  58. def schedule(now=time.gmtime()):
  59. """Start all tasks that are supposed to be run at a given time."""
  60. # Get list of tasks to run this turn:
  61. tasks = config.schedule(now.tm_min, now.tm_hour, now.tm_mday, now.tm_mon,
  62. now.tm_wday)
  63. for task in tasks:
  64. if isinstance(task, list): # they've specified kwargs
  65. start(task[0], **task[1]) # so pass those to start_task
  66. else: # otherwise, just pass task_name
  67. start(task)
  68. def start(task_name, **kwargs):
  69. """Start a given task in a new thread. Pass args to the task's run()
  70. function."""
  71. print "Starting task '{0}' in a new thread...".format(task_name)
  72. try:
  73. task = _tasks[task_name]
  74. except KeyError:
  75. error = "Couldn't find task '{0}': bot/tasks/{0}.py does not exist."
  76. print error.format(task_name)
  77. return
  78. task_thread = threading.Thread(target=lambda: _wrapper(task, **kwargs))
  79. start_time = time.strftime("%b %d %H:%M:%S")
  80. task_thread.name = "{0} ({1})".format(task_name, start_time)
  81. # Stop bot task threads automagically if the main bot stops:
  82. task_thread.daemon = True
  83. task_thread.start()
  84. def get_all():
  85. """Return our dict of all loaded tasks."""
  86. return _tasks