Assignment title: Information
import threading
from abc import ABCMeta
from time import sleep
import newsbot # you must import your bot file here, even if you don't use it
import eventbot
import config
from config import praw_config, bot_config
from bots import InvalidBotClassName, BotSignature, RedditBot
# If you declare your own RedditBot subclass in its own file,
# you must import it or else it will not be added to BOT_CLASSES.
BOT_CLASSES = {cls.__name__: cls for cls in RedditBot.get_subclasses()}
logger = config.getLogger()
# region DISPATCH
class Dispatch(threading.Thread, metaclass=ABCMeta):
"""
An object used to create, launch, and terminate bots.
"""
def __init__(self, bot_signatures, stop_event=None):
"""
Initializes a Dispatch object, and creates a pool of bots.
:param bot_signatures: A list of BotSignatures used to create the new bots
:param stop_event: A threading.Event used to keep the Dispatch alive and tell it when to close.
"""
super(Dispatch, self).__init__()
self.stop = stop_event or threading.Event()
self.bots = {}
for signature in bot_signatures:
if type(signature.classname) is str:
self.bots[signature.classname] = [BOT_CLASSES[name](user_name=signature.username)
for name in signature.classname.split(",")]
elif type(signature.classname) is list and all(type(name) is str for name in signature.classname):
self.bots[signature.classname] = [BOT_CLASSES[name](user_name=signature.username)
for name in signature.classname]
else:
raise InvalidBotClassName
def __enter__(self):
"""
Starts a Dispatch using a context manager,
e.g. with Dispatch():
# do something
:return: The dispatch object
"""
self.start()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""
Safely closes a Dispatch using a context manager,
e.g. with Dispatch():
# do something
"""
self.join()
def run(self):
"""
Override of Thread.run().
Starts the bots, and waits for a stop event.
:return:
"""
for bot_list in self.bots.values():
for bot in bot_list:
bot.start()
self.stop.wait()
def join(self, timeout=None):
"""
Override of Thread.join().
Stops all the bots, sets the stop event, and stops itself.
:param timeout: Time to wait before forcefully stopping itself (wait forever if None).
:return: Original return value of Thread.join()
"""
for bot_list in self.bots.values():
for bot in bot_list:
bot.join(timeout)
self.stop.set()
return super(Dispatch, self).join(timeout)
class GlobalDispatch(Dispatch):
"""
A Dispatch that creates Bots with every entry in praw.ini.
It assumes every entry is meant to be used for a Bot.
"""
def __init__(self, stop_event=None):
"""
Creates BotSignatures for every account in praw.ini, and initializes a Dispatch.
:param stop_event: A threading.Event used to stop the Dispatch.
"""
signatures = [BotSignature(classname=praw_config.get_bot_class_name(name),
username=name,
permissions=praw_config.get_reddit_oauth_scope(name))
for name in praw_config.get_all_site_names()]
super(GlobalDispatch, self).__init__(signatures, stop_event)
# endregion
def main(args=None):
logger.info("Starting bots")
with GlobalDispatch():
try:
while True:
sleep(1)
except KeyboardInterrupt:
logger.info("Terminating bots")
logger.info("Program closed")
if __name__ == '__main__':
main()
>