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.

main.py 5.4 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. #! /usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. """
  4. EarwigBot's Core
  5. This (should) not be run directly; the wrapper in "earwigbot.py" is preferred,
  6. but it should work fine alone, as long as you enter the password-unlock key at
  7. the initial hidden prompt.
  8. The core is essentially responsible for starting the various bot components
  9. (irc, scheduler, etc) and making sure they are all happy. An explanation of the
  10. different components follows:
  11. EarwigBot has three components that can run independently of each other: an IRC
  12. front-end, an IRC watcher, and a wiki scheduler.
  13. * The IRC front-end runs on a normal IRC server and expects users to interact
  14. with it/give it commands.
  15. * The IRC watcher runs on a wiki recent-changes server and listens for edits.
  16. Users cannot interact with this part of the bot.
  17. * The wiki scheduler runs wiki-editing bot tasks in separate threads at
  18. user-defined times through a cron-like interface.
  19. There is a "priority" system here:
  20. 1. If the IRC frontend is enabled, it will run on the main thread, and the IRC
  21. watcher and wiki scheduler (if enabled) will run on separate threads.
  22. 2. If the wiki scheduler is enabled, it will run on the main thread, and the
  23. IRC watcher (if enabled) will run on a separate thread.
  24. 3. If the IRC watcher is enabled, it will run on the main (and only) thread.
  25. Else, the bot will stop, as no components are enabled.
  26. """
  27. import threading
  28. import time
  29. import traceback
  30. import sys
  31. import os
  32. script_dir = os.path.dirname(os.path.abspath(__file__))
  33. root_dir = os.path.split(script_dir)[0] # the bot's "root" directory relative
  34. # to its different components
  35. sys.path.append(root_dir) # make sure we look in the root dir for modules
  36. from core import config
  37. from irc import frontend, watcher
  38. from wiki import task_manager
  39. f_conn = None
  40. w_conn = None
  41. def irc_watcher(f_conn=None):
  42. """Function to handle the IRC watcher as another thread (if frontend and/or
  43. scheduler is enabled), otherwise run as the main thread."""
  44. global w_conn
  45. while 1: # restart the watcher component if it breaks (and nothing else)
  46. w_conn = watcher.get_connection()
  47. w_conn.connect()
  48. print # blank line to signify that the bot has finished starting up
  49. try:
  50. watcher.main(w_conn, f_conn)
  51. except:
  52. traceback.print_exc()
  53. time.sleep(5) # sleep a bit before restarting watcher
  54. print "\nWatcher has stopped; restarting component..."
  55. def wiki_scheduler():
  56. """Function to handle the wiki scheduler as another thread, or as the
  57. primary thread if the IRC frontend is not enabled."""
  58. while 1:
  59. time_start = time.time()
  60. now = time.gmtime(time_start)
  61. task_manager.start_tasks(now)
  62. time_end = time.time()
  63. time_diff = time_start - time_end
  64. if time_diff < 60: # sleep until the next minute
  65. time.sleep(60 - time_diff)
  66. def irc_frontend():
  67. """If the IRC frontend is enabled, make it run on our primary thread, and
  68. enable the wiki scheduler and IRC watcher on new threads if they are
  69. enabled."""
  70. global f_conn
  71. print "Starting IRC frontend..."
  72. f_conn = frontend.get_connection()
  73. frontend.startup(f_conn)
  74. if "wiki_schedule" in config.components:
  75. print "\nStarting wiki scheduler..."
  76. task_manager.load_tasks()
  77. t_scheduler = threading.Thread(target=wiki_scheduler)
  78. t_scheduler.name = "wiki-scheduler"
  79. t_scheduler.daemon = True
  80. t_scheduler.start()
  81. if "irc_watcher" in config.components:
  82. print "\nStarting IRC watcher..."
  83. t_watcher = threading.Thread(target=irc_watcher, args=(f_conn,))
  84. t_watcher.name = "irc-watcher"
  85. t_watcher.daemon = True
  86. t_watcher.start()
  87. frontend.main()
  88. if "irc_watcher" in config.components:
  89. w_conn.close()
  90. f_conn.close()
  91. def run():
  92. try:
  93. key = raw_input() # wait for our password unlock key from the bot's
  94. except EOFError: # wrapper
  95. key = None
  96. config.parse_config(key) # load data from the config file and parse it
  97. # using the unlock key
  98. enabled = config.components
  99. if "irc_frontend" in enabled: # make the frontend run on our primary
  100. irc_frontend() # thread if enabled, and enable additional
  101. # components through that function
  102. elif "wiki_schedule" in enabled: # run the scheduler on the main
  103. print "Starting wiki scheduler..." # thread, but also run the IRC
  104. task_manager.load_tasks() # watcher on another thread iff it
  105. if "irc_watcher" in enabled: # is enabled
  106. print "\nStarting IRC watcher..."
  107. t_watcher = threading.Thread(target=irc_watcher, args=())
  108. t_watcher.name = "irc-watcher"
  109. t_watcher.daemon = True
  110. t_watcher.start()
  111. wiki_scheduler()
  112. elif "irc_watcher" in enabled: # the IRC watcher is our only enabled
  113. print "Starting IRC watcher..." # component, so run its function only
  114. irc_watcher() # and don't worry about anything else
  115. else: # nothing is enabled!
  116. print "No bot parts are enabled; stopping..."
  117. exit(1)
  118. if __name__ == "__main__":
  119. try:
  120. run()
  121. except KeyboardInterrupt:
  122. print "\nKeyboardInterrupt: stopping main bot loop."
  123. exit(1)