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.

__init__.py 3.1 KiB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  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"]
  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 (wiki.tasks.task_file.Task())
  15. _tasks = dict()
  16. def _load_class_from_file(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. print "Task '{0}' raised an exception and had to stop:".format(task.task_name)
  41. traceback.print_exc()
  42. else:
  43. print "Task '{0}' finished without error.".format(task.task_name)
  44. def load():
  45. """Load all valid task classes from bot/tasks/, and add them to the
  46. _tasks variable."""
  47. files = os.listdir(os.path.join("bot", "tasks"))
  48. files.sort() # alphabetically sort all files in wiki/tasks/
  49. for f in files:
  50. if not os.path.isfile(os.path.join("bot", "tasks", f)):
  51. continue # ignore non-files
  52. if f.startswith("_") or not f.endswith(".py"):
  53. continue # ignore non-python files or files beginning with an _
  54. load_class_from_file(f)
  55. print "Found %s tasks: %s." % (len(_tasks), ', '.join(_tasks.keys()))
  56. def schedule(now=time.gmtime()):
  57. """Start all tasks that are supposed to be run at a given time."""
  58. tasks = config.schedule(now.tm_min, now.tm_hour, now.tm_mday, now.tm_mon,
  59. now.tm_wday) # get list of tasks to run this turn
  60. for task in tasks:
  61. if isinstance(task, list): # they've specified kwargs
  62. start(task[0], **task[1]) # so pass those to start_task
  63. else: # otherwise, just pass task_name
  64. start(task)
  65. def start(task_name, **kwargs):
  66. """Start a given task in a new thread. Pass args to the task's run()
  67. function."""
  68. print "Starting task '{0}' in a new thread...".format(task_name)
  69. try:
  70. task = _tasks[task_name]
  71. except KeyError:
  72. print ("Couldn't find task '{0}': bot/tasks/{0}.py does not exist.").format(task_name)
  73. return
  74. task_thread = threading.Thread(target=lambda: _wrapper(task, **kwargs))
  75. task_thread.name = "{0} ({1})".format(task_name, time.strftime("%b %d %H:%M:%S"))
  76. # stop bot task threads automagically if the main bot stops
  77. task_thread.daemon = True
  78. task_thread.start()