def setup(self): self.logger.trace("Entered setup method.") self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() try: self.config = self.storage.get_file(self, "config", YAML, "plugins/inter.yml") except Exception: self.logger.exception("Error loading configuration!") self.logger.error(_("Disabling..")) self._disable_self() return if not self.config.exists: self.logger.error("Unable to find config/plugins/inter.yml") self.logger.error(_("Disabling..")) self._disable_self() return self.config.add_callback(self.reload) self.commands.register_command("players", self.players_command, self, "inter.players", default=True) if not reactor.running: self.events.add_callback( "ReactorStarted", self, self.first_load, 0 ) else: self.first_load()
def setup(self): ### Grab important shit self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() ### Initial config load try: self._config = self.storage.get_file(self, "config", YAML, "plugins/triggers.yml") except Exception: self.logger.exception("Error loading configuration!") self.logger.error("Disabling...") self._disable_self() return if not self._config.exists: self.logger.error("Unable to find config/plugins/triggers.yml") self.logger.error("Disabling...") self._disable_self() return ### Register event handlers def _message_event_filter(event=MessageReceived): return isinstance(event.target, Channel) self.events.add_callback("MessageReceived", self, self.message_handler, 1, _message_event_filter) self.events.add_callback("ActionReceived", self, self.action_handler, 1, _message_event_filter)
def setup(self): self.events = EventManager() self.storage = StorageManager() try: self.config = self.storage.get_file(self, "config", YAML, "plugins/blowfish.yml") except Exception: self.logger.exception("Error loading configuration!") self._disable_self() return if not self.config.exists: self.logger.error("Unable to find config/plugins/blowfish.yml") self._disable_self() return self.events.add_callback("PreMessageReceived", self, self.pre_message, 10001) self.events.add_callback("MessageSent", self, self.message_sent, 10001) self.events.add_callback("ActionReceived", self, self.message_sent, 10001) self.events.add_callback("ActionSent", self, self.message_sent, 10001)
def __init__(self): self.commands = CommandManager() self.event_manager = EventManager() self.logger = getLogger("Manager") self.plugman = PluginManager(self) self.yapsy_logger = getLogger("yapsy") self.metrics = None
def setup(self): """ Called when the plugin is loaded. Performs initial setup. """ self.logger.trace(_("Entered setup method.")) self.storage = StorageManager() try: self.config = self.storage.get_file(self, "config", YAML, "plugins/urls.yml") except Exception: self.logger.exception(_("Error loading configuration!")) else: if not self.config.exists: self.logger.warn(_("Unable to find config/plugins/urls.yml")) else: self.content_types = self.config["content_types"] self.spoofing = self.config["spoofing"] self.logger.debug(_("Spoofing: %s") % self.spoofing) self.channels = self.storage.get_file(self, "data", YAML, "plugins/urls/channels.yml") self.shortened = self.storage.get_file( self, "data", DBAPI, "sqlite3:data/plugins/urls/shortened.sqlite", "data/plugins/urls/shortened.sqlite", check_same_thread=False) self.commands = CommandManager() self.events = EventManager() self.reload() def message_event_filter(event=MessageReceived): target = event.target type_ = event.type return type_ == "message" \ or isinstance(target, Channel) \ or isinstance(target, User) self.add_shortener("tinyurl", self.tinyurl) self.events.add_callback("MessageReceived", self, self.message_handler, 1, message_event_filter) self.commands.register_command("urls", self.urls_command, self, "urls.manage") self.commands.register_command("shorten", self.shorten_command, self, "urls.shorten", default=True)
def __init__(self, name, factory, config): NoChannelsProtocol.__init__(self, name, factory, config) self.name = name self.log = getLogger(self.name) self.event_manager = EventManager() self.command_manager = CommandManager() reactor.connectTCP(self.config["connection"]["host"], self.config["connection"]["port"], self.factory, 120)
class MemosPlugin(plugin.PluginObject): commands = None events = None storage = None # data = None # SQLite for a change def setup(self): self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() # self.data = self.storage.get_file(self, "data", SQLITE, # "plugins/memos/memos.sqlite") # with self.data as c: # # Multiline strings because of an IDE bug # c.execute("""CREATE TABLE IF NOT EXISTS memos # (to TEXT, from TEXT, memo TEXT)""") self.events.add_callback("PreMessageReceived", self, self.message_received, 0) self.commands.register_command("memo", self.memo_command, self, "memo.send", default=True) def save_memo(self, sender, recipient, memo): recipient = recipient.lower() # with self.data as c: # c.execute("""INSERT INTO memos VALUES (?, ?, ?)""", # (recipient, sender, memo)) def get_memos(self, recipient): recipient = recipient.lower() # with self.data as c: # c.execute("""SELECT * FROM memos WHERE from=?""", (recipient,)) # d = c.fetchall() # return d def message_received(self, event=PreMessageReceived): user = event.source target = event.target if isinstance(event.target, Channel) else user memos = self.get_memos(user.name) if memos: for memo in memos: sender = memo[1] text = memo[2] target.respond("Memo for %s (from %s): %s" % (user.name, sender, text)) def memo_command(self, protocol, caller, source, command, raw_args, parsed_args): args = raw_args.split() # Quick fix for new command handler signature raise NotImplementedError("This isn't done yet.")
class WebhooksPlugin(plugin.PluginObject): config = None data = None commands = None events = None plugins = None storage = None @property def web(self): """ :rtype: plugins.web.WebPlugin """ return self.plugins.get_plugin("Web") def setup(self): self.logger.trace("Entered setup method.") self.commands = CommandManager() self.events = EventManager() self.plugins = PluginManager() self.storage = StorageManager() try: self.config = self.storage.get_file(self, "config", YAML, "plugins/webhooks.yml") except Exception: self.logger.exception("Error loading configuration!") return self._disable_self() else: if not self.config.exists: self.logger.error("Unable to find config/plugins/webhooks.yml") return self._disable_self() self._load() self.config.add_callback(self._load) self.events.add_callback("Web/ServerStartedEvent", self, self.add_routes, 0) def _load(self): pass def add_routes(self, _=None): self.web.add_navbar_entry("Webhooks", "/webhooks", "url") self.web.add_handler("/webhooks", "plugins.webhooks.routes.webhooks.Route") self.logger.info("Registered route: /webhooks") pass # So the regions work in PyCharm
def setup(self): # ## Grab important shit self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() self.plugman = PluginManager() # ## Set up database self.database = self.storage.get_file( self, "data", DBAPI, "sqlite3:data/plugins/factoids.sqlite", "data/plugins/factoids.sqlite", check_same_thread=False ) self.database.add_callback(self.reload) self.reload() # ## Register commands # We have multiple possible permissions per command, so we have to do # permission handling ourselves self.commands.register_command("addfactoid", self.factoid_add_command, self, None) self.commands.register_command("setfactoid", self.factoid_set_command, self, None) self.commands.register_command("deletefactoid", self.factoid_delete_command, self, None, ["delfactoid"]) self.commands.register_command("getfactoid", self.factoid_get_command, self, None, default=True) # ## Register events self.events.add_callback("MessageReceived", self, self.message_handler, 1) self.events.add_callback("Web/ServerStartedEvent", self, self.web_routes, 1)
def setup(self): self.logger.trace("Entered setup method.") self.storage = StorageManager() try: self.config = self.storage.get_file(self, "config", YAML, "plugins/feeds.yml") except Exception: self.logger.exception("Error loading configuration!") self.logger.error("Disabling..") self._disable_self() return if not self.config.exists: self.logger.error("Unable to find config/plugins/feeds.yml") self.logger.error("Disabling..") self._disable_self() return self.config.add_callback(self.delayed_setup) self.events = EventManager() self.plugman = PluginManager() self.logger.info("Waiting 30 seconds to set up.") reactor.callLater(30, self.delayed_setup)
def setup(self): """The list of bridging rules""" self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() self.data = self.storage.get_file(self, "data", YAML, "plugins/dialectizer/settings.yml") self.events.add_callback("MessageSent", self, self.handle_msg_sent, 1) self.commands.register_command("dialectizer", self.dialectizer_command, self, "dialectizer.set", aliases=["dialectiser"])
def setup(self): self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() # self.data = self.storage.get_file(self, "data", SQLITE, # "plugins/memos/memos.sqlite") # with self.data as c: # # Multiline strings because of an IDE bug # c.execute("""CREATE TABLE IF NOT EXISTS memos # (to TEXT, from TEXT, memo TEXT)""") self.events.add_callback("PreMessageReceived", self, self.message_received, 0) self.commands.register_command("memo", self.memo_command, self, "memo.send", default=True)
def setup(self): self.events = EventManager() self.storage = StorageManager() try: self.config = self.storage.get_file(self, "config", YAML, "plugins/blowfish.yml") except Exception: self.logger.exception("Error loading configuration!") self._disable_self() return if not self.config.exists: self.logger.error("Unable to find config/plugins/blowfish.yml") self._disable_self() return self.events.add_callback( "PreMessageReceived", self, self.pre_message, 10001 ) self.events.add_callback( "MessageSent", self, self.message_sent, 10001 ) self.events.add_callback( "ActionReceived", self, self.message_sent, 10001 ) self.events.add_callback( "ActionSent", self, self.message_sent, 10001 )
def setup(self): self.logger.trace("Entered setup method.") self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() try: self.config = self.storage.get_file(self, "config", YAML, "plugins/inter.yml") except Exception: self.logger.exception("Error loading configuration!") self.logger.error(_("Disabling..")) self._disable_self() return if not self.config.exists: self.logger.error("Unable to find config/plugins/inter.yml") self.logger.error(_("Disabling..")) self._disable_self() return self.config.add_callback(self.reload) self.commands.register_command("players", self.players_command, self, "inter.players", default=True) self.events.add_callback("ReactorStarted", self, self.first_load, 0)
def setup(self): # Define this so we have instances of everything we need self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() self.plugman = PluginManager() # Now run the actual setup function self.wrapper(self.clj_plugin.setup)()
def setup(self): self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() self.data = self.storage.get_file( self, "data", DBAPI, "sqlite3:data/plugins/lastseen/users.sqlite", "data/plugins/lastseen/users.sqlite", check_same_thread=False ) self.data.runQuery("CREATE TABLE IF NOT EXISTS users (" "user TEXT, " "protocol TEXT, " "at INTEGER)") self.commands.register_command("seen", self.seen_command, self, "seen.seen", default=True) # General events self.events.add_callback("PreMessageReceived", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_source_caller]) self.events.add_callback("PreCommand", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_source_caller]) self.events.add_callback("NameChanged", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("UserDisconnected", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) # Mumble events self.events.add_callback("Mumble/UserRemove", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserJoined", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserMoved", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserSelfMuteToggle", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserSelfDeafToggle", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserRecordingToggle", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller])
def __init__(self, name, factory, config): self.name = name self.factory = factory self.config = config self.received = "" self.log = getLogger(self.name) self.log.info("Setting up..") self.command_manager = CommandManager() self.event_manager = EventManager() self.username = config["identity"]["username"] self.password = config["identity"]["password"] self.networking = config["network"] self.tokens = config["identity"]["tokens"] self.control_chars = config["control_chars"] audio_conf = config.get("audio", {}) self.should_mute_self = audio_conf.get("should_mute_self", True) self.should_deafen_self = audio_conf.get("should_deafen_self", True) event = general_events.PreConnectEvent(self, config) self.event_manager.run_callback("PreConnect", event) context = self._get_client_context() if context is None: # Could not create a context (problem loading cert file) self.factory.manager.remove_protocol(self.name) return reactor.connectSSL( self.networking["address"], self.networking["port"], self.factory, context, 120 ) event = general_events.PostConnectEvent(self, config) self.event_manager.run_callback("PostConnect", event)
def setup(self): ### Grab important shit self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() ### Initial config load try: self.config = self.storage.get_file(self, "config", YAML, "plugins/drunkoctopus.yml") except Exception: self.logger.exception("Error loading configuration!") self.logger.error("Disabling...") self._disable_self() return if not self.config.exists: self.logger.error("Unable to find config/plugins/drunkoctopus.yml") self.logger.error("Disabling...") self._disable_self() return ### Create vars and stuff self._sobering_call = None self._drunktalk = DrunkTalk() ### Load options from config self._load() self.config.add_callback(self._load) ### Register events and commands self.events.add_callback("MessageSent", self, self.outgoing_message_handler, 1) self.commands.register_command("drunkenness", self.drunkenness_command, self, "drunkoctopus.drunkenness", default=True) self.commands.register_command("drink", self.drink_command, self, "drunkoctopus.drink")
def setup(self): """ Called when the plugin is loaded. Performs initial setup. """ self.logger.trace(_("Entered setup method.")) self.storage = StorageManager() try: self.config = self.storage.get_file(self, "config", YAML, "plugins/urls.yml") except Exception: self.logger.exception(_("Error loading configuration!")) else: if not self.config.exists: self.logger.warn(_("Unable to find config/plugins/urls.yml")) else: self.content_types = self.config["content_types"] self.spoofing = self.config["spoofing"] self.logger.debug(_("Spoofing: %s") % self.spoofing) self.channels = self.storage.get_file(self, "data", YAML, "plugins/urls/channels.yml") self.shortened = self.storage.get_file( self, "data", DBAPI, "sqlite3:data/plugins/urls/shortened.sqlite", "data/plugins/urls/shortened.sqlite", check_same_thread=False ) self.commands = CommandManager() self.events = EventManager() self.reload() def message_event_filter(event=MessageReceived): target = event.target type_ = event.type return type_ == "message" \ or isinstance(target, Channel) \ or isinstance(target, User) self.add_shortener("tinyurl", self.tinyurl) self.events.add_callback("MessageReceived", self, self.message_handler, 1, message_event_filter) self.commands.register_command("urls", self.urls_command, self, "urls.manage") self.commands.register_command("shorten", self.shorten_command, self, "urls.shorten", default=True)
class AssPlugin(plugin.PluginObject): events = None regex = None def setup(self): self.events = EventManager() self.regex = re.compile(r"(\w+)-ass (\w+)") self.events.add_callback("MessageReceived", self, self.ass_swap, 1) def ass_swap(self, event=MessageReceived): source = event.source target = event.target message = event.message if re.search(self.regex, message) is None: return result = re.sub(self.regex, r"\1 ass-\2", message) target.respond("%s: %s" % (source.nickname, result))
def __init__(self, name, factory, config): NoChannelsProtocol.__init__(self, name, factory, config) self.log = getLogger(self.name) self.event_manager = EventManager() self.command_manager = CommandManager() reactor.connectTCP( self.config["connection"]["host"], self.config["connection"]["port"], self.factory, 120 )
def setup(self): self.logger.trace("Entered setup method.") self.commands = CommandManager() self.events = EventManager() self.plugins = PluginManager() self.storage = StorageManager() try: self.config = self.storage.get_file(self, "config", YAML, "plugins/twilio.yml") except Exception: self.logger.exception("Error loading configuration!") return self._disable_self() else: if not self.config.exists: self.logger.error("Unable to find config/plugins/twilio.yml") return self._disable_self() try: self.data = self.storage.get_file(self, "data", JSON, "plugins/twilio/contacts.json") except Exception: self.logger.exception("Error loading data!") self.logger.error("This data file is required. Shutting down...") return self._disable_self() self._load() self.config.add_callback(self._load) self.events.add_callback("Web/ServerStartedEvent", self, self.add_routes, 0) self.commands.register_command("sms", self.sms_command, self, "twilio.sms") self.commands.register_command("mms", self.mms_command, self, "twilio.mms") self.commands.register_command("tw", self.tw_command, self, "twilio.tw")
def setup(self): self.logger.trace("Entered setup method.") self.commands = CommandManager() self.events = EventManager() self.plugins = PluginManager() self.storage = StorageManager() try: self.config = self.storage.get_file(self, "config", YAML, "plugins/webhooks.yml") except Exception: self.logger.exception("Error loading configuration!") return self._disable_self() else: if not self.config.exists: self.logger.error("Unable to find config/plugins/webhooks.yml") return self._disable_self() self._load() self.config.add_callback(self._load) self.events.add_callback("Web/ServerStartedEvent", self, self.add_routes, 0)
class ReplPlugin(plugin.PluginObject): commands = None events = None storage = None config = None proto = None channel = None formatting = None def __init__(self): self.protocol_events = { "general": [ # This is basically just *args. ["MessageReceived", self, self.message_received, 0] ], "inter": [ ["Inter/PlayerConnected", self, self.inter_player_connected, 0], ["Inter/PlayerDisconnected", self, self.inter_player_disconnected, 0], ["Inter/ServerConnected", self, self.inter_server_connected, 0], ["Inter/ServerDisconnected", self, self.inter_server_disconnected, 0] ] } super(ReplPlugin, self).__init__() def setup(self): self.logger.trace("Entered setup method.") self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() try: self.config = self.storage.get_file(self, "config", YAML, "plugins/inter.yml") except Exception: self.logger.exception("Error loading configuration!") self.logger.error(_("Disabling..")) self._disable_self() return if not self.config.exists: self.logger.error("Unable to find config/plugins/inter.yml") self.logger.error(_("Disabling..")) self._disable_self() return self.config.add_callback(self.reload) self.commands.register_command("players", self.players_command, self, "inter.players", default=True) self.events.add_callback("ReactorStarted", self, self.first_load, 0) def first_load(self, event): if not self.reload(): self.logger.error(_("Disabling..")) self._disable_self() def reload(self): self.events.remove_callbacks_for_plugin(self.info.name) proto = self.factory_manager.get_protocol(self.config["protocol"]) if proto is None: self.logger.error(_("Unknown protocol: %s") % self.config["protocol"]) return False if proto.TYPE == "inter": self.logger.error(_("You cannot relay between two Inter " "protocols!")) return False self.proto = proto self.channel = self.config["channel"] self.formatting = self.config["formatting"] for event in self.protocol_events["general"]: self.events.add_callback(*event) for event in self.protocol_events["inter"]: self.events.add_callback(*event) if proto.TYPE in self.protocol_events: for event in self.protocol_events[proto.TYPE]: self.events.add_callback(*event) return True def get_inters(self): inters = {} for key in self.factory_manager.factories.keys(): if self.factory_manager.get_protocol(key).TYPE == "inter": inters[key] = self.factory_manager.get_protocol(key) return inters def players_command(self, protocol, caller, source, command, raw_args, args): if protocol.TYPE == "inter": caller.respond("This command cannot be used via Inter.") return inters = self.get_inters() if len(inters) < 1: caller.respond("No Inter protocols were found.") elif len(inters) == 1: servers = inters[inters.keys()[0]].inter_servers for key in servers.keys(): formatting = self.formatting["player"]["list"] done = formatting["message"] _done = [] for x in servers[key]: _done.append(str(x)) if len(_done): players = formatting["join"].join(_done) else: players = "No players online." done = done.replace("{SERVER}", key) done = done.replace("{PLAYERS}", players) source.respond(done) else: if len(args) < 1: caller.respond("Usage: {CHARS}%s <inter server>") caller.respond("Servers: %s" % ", ".join(inters.keys())) return srv = args[1] if srv not in inters: caller.respond("Unknown inter server: %s" % srv) caller.respond("Servers: %s" % ", ".join(inters.keys())) return servers = inters[srv].inter_servers for key in servers.keys(): formatting = self.formatting["player"]["list"] done = formatting["message"] _done = [] for x in servers[key]: _done.append(str(x)) if len(_done): players = formatting["join"].join(_done) else: players = "No players online." done = done.replace("{SERVER}", key) done = done.replace("{PLAYERS}", players) source.respond(done) def message_received(self, event=MessageReceived): caller = event.caller user = event.source message = event.message target = event.target if target is None: return if caller is None: return if isinstance(caller, Protocol): if caller.TYPE == "inter": f_str = self.formatting["player"]["message"] f_str = f_str.replace("{SERVER}", user.server) f_str = f_str.replace("{USER}", str(user)) f_str = f_str.replace("{MESSAGE}", message) self.proto.send_msg(self.channel, f_str) else: if caller.name == self.proto.name: if target.name.lower() == self.channel.lower(): inters = self.get_inters() for proto in inters.values(): proto.send_msg_other(user, message) def inter_server_connected(self, event=InterServerConnected): f_str = self.formatting["server"]["connected"] f_str = f_str.replace("{SERVER}", event.name) self.proto.send_msg(self.channel, f_str) def inter_server_disconnected(self, event=InterServerDisonnected): f_str = self.formatting["server"]["disconnected"] f_str = f_str.replace("{SERVER}", event.name) self.proto.send_msg(self.channel, f_str) def inter_player_connected(self, event=InterPlayerConnected): f_str = self.formatting["player"]["connected"] f_str = f_str.replace("{SERVER}", event.user.server) f_str = f_str.replace("{USER}", str(event.user)) self.proto.send_msg(self.channel, f_str) def inter_player_disconnected(self, event=InterPlayerDisonnected): f_str = self.formatting["player"]["disconnected"] f_str = f_str.replace("{SERVER}", event.user.server) f_str = f_str.replace("{USER}", str(event.user)) self.proto.send_msg(self.channel, f_str)
class Protocol(LineOnlyReceiver, NoChannelsProtocol): __version__ = "0.0.1" __inter_version__ = 3 TYPE = "inter" CHANNELS = False factory = None config = None log = None event_manager = None command_manager = None nickname = "" ourselves = None channel = None can_flood = False control_chars = "." handshake_done = False inter_servers = {} _users = [] def __init__(self, name, factory, config): NoChannelsProtocol.__init__(self, name, factory, config) self.name = name self.log = getLogger(self.name) self.event_manager = EventManager() self.command_manager = CommandManager() reactor.connectTCP(self.config["connection"]["host"], self.config["connection"]["port"], self.factory, 120) def connectionMade(self): self.handshake_done = False self.inter_servers = {} self.nickname = "" self.control_chars = self.config["control_char"] self.ourselves = User(self.config["nickname"], self, True) self.channel = Channel(protocol=self) def lineReceived(self, line): try: self.log.trace("<- %s" % repr(line)) message = json.loads(line) except Exception: self.log.exception("Failed to parse line") else: if "version" in message: v = message["version"] if v != self.__inter_version__: self.log.error("Protocol version mismatch!") self.log.error("Ours: %s | Theirs: %s" % (self.__inter_version__, v)) self.factory.manager.remove_protocol(self.name) return self.log.info("Connected to Inter, version %s" % v) message = {"api_key": self.config["connection"]["api_key"]} self.send(message) if "from" in message: origin = message["from"] for case, default in Switch(origin): if case("chat"): source = message["source"] msg = message["message"] user = self.get_user(message["user"], server=source, create=True) if not user.server: user.server = source if user == self.ourselves: break # Since, well, this is us. if source == self.nickname: break # Since this is also us. event = general_events.PreMessageReceived( self, user, user, msg, "message" # No channels ) self.event_manager.run_callback( "PreMessageReceived", event) if event.printable: for line in event.message.split("\n"): self.log.info("<%s> %s" % (user, line)) if not event.cancelled: result = self.command_manager.process_input( event.message, user, user, self, self.control_chars, self.nickname) for c, d in Switch(result[0]): if c(CommandState.RateLimited): self.log.debug("Command rate-limited") user.respond("That command has been " "rate-limited, please try " "again later.") return # It was a command if c(CommandState.NotACommand): self.log.debug("Not a command") break if c(CommandState.UnknownOverridden): self.log.debug("Unknown command " "overridden") return # It was a command if c(CommandState.Unknown): self.log.debug("Unknown command") break if c(CommandState.Success): self.log.debug("Command ran successfully") return # It was a command if c(CommandState.NoPermission): self.log.debug("No permission to run " "command") return # It was a command if c(CommandState.Error): user.respond("Error running command: " "%s" % result[1]) return # It was a command if d: self.log.debug("Unknown command state: " "%s" % result[0]) break second_event = general_events.MessageReceived( self, user, user, msg, "message") self.event_manager.run_callback( "MessageReceived", second_event) break if case("players"): _type = message["type"] target = message["target"] if _type == "list": if target == "all": # All servers, we can just overwrite the list. self.inter_servers = {} players = message["players"] for key in players: self.inter_servers[key] = [] for user in players[key]: obj = User(user, self, True) obj.server = key self.inter_servers[key].append(obj) if obj not in self._users: self._users.append(obj) self.log.info("Got player list.") for key in self.inter_servers.keys(): self.log.info( "%s: %s players" % (key, len(self.inter_servers[key]))) event = inter_events.InterServerListReceived( self, self.inter_servers) self.event_manager.run_callback( "Inter/ServerListReceived", event) else: # Unexpected! self.log.warn("Unknown list target: %s" % target) elif _type == "offline": player = self.get_user(message["player"], target, True) player.server = target if target not in self.inter_servers: self.inter_servers[target] = [] if player in self.inter_servers[target]: self.inter_servers[target].remove(player) if player in self._users: self._users.remove(player) self.log.info("%s disconnected from %s." % (player, target)) event = general_events.UserDisconnected( self, player) self.event_manager.run_callback( "UserDisconnected", event) second_event = inter_events.InterPlayerDisonnected( self, player) self.event_manager.run_callback( "Inter/PlayerDisconnected", second_event) elif _type == "online": player = self.get_user(message["player"], target, True) player.server = target if target not in self.inter_servers: self.inter_servers[target] = [] if player not in self.inter_servers[target]: self.inter_servers[target].append(player) if player not in self._users: self._users.append(player) self.log.info("%s connected to %s." % (player, target)) event = inter_events.InterPlayerConnected( self, player) self.event_manager.run_callback( "Inter/PlayerConnected", event) break if case("auth"): action = message["action"] if action == "authenticated": if not self.handshake_done and "status" in message: status = message["status"] if status == "success": self.nickname = message["name"] self.log.info("Authenticated as %s" % self.nickname) self.handshake_done = True # Get players self.send_get_players() self.send_connect(self.ourselves.nickname) event = inter_events.InterAuthenticated( self) self.event_manager.run_callback( "Inter/Authenticated", event) else: error = message["error"] self.log.error("Error authenticating: %s" % error) event = ( inter_events.InterAuthenticationError( self, error)) self.event_manager.run_callback( "Inter/AuthenticationError", event) self.transport.close() self.factory.manager.remove_protocol( self.name) else: name = message["name"] self.log.info("Server connected to Inter: %s" % name) event = inter_events.InterServerConnected( self, name) self.event_manager.run_callback( "Inter/ServerConnected", event) else: name = message["name"] self.log.info( "Server disconnected from Inter: %s" % name) event = inter_events.InterServerDisonnected( self, name) self.event_manager.run_callback( "Inter/ServerDisconnected", event) if name in self.inter_servers: del self.inter_servers[name] break if case("core"): event = inter_events.InterCoreMessage(self, message) self.event_manager.run_callback( "Inter/CoreMessage", event) break if case("ping"): timestamp = message["timestamp"] event = inter_events.InterPing(self, timestamp) self.event_manager.run_callback("Inter/Ping", event) self.send_pong(timestamp) break if default: self.log.warn("Unknown message origin: %s" % origin) event = inter_events.InterUnknownMessage(self, message) self.event_manager.run_callback( "Inter/UnknownMessage", event) break def send(self, _json): self.sendLine(json.dumps(_json)) def sendLine(self, line): self.log.trace("-> %s" % repr(line)) LineOnlyReceiver.sendLine(self, line) def shutdown(self): """ Called when a protocol needs to disconnect. Cleanup should be done here. """ self.transport.loseConnection() def get_user(self, username, server=None, create=False): """ Used to retrieve a user. Return None if we can't find it. :param user: string representing the user we need. """ if server is None: for key in self.inter_servers: for user in self.inter_servers[key]: if user.name.lower() == username.lower(): return user else: if server in self.inter_servers: for user in self.inter_servers[server]: if user.name.lower() == username.lower(): return user if create: return User(username, self) return None def send_msg(self, target, message, target_type=None, use_event=True): """ Send a message. :param target: Ignored. :param message: The message to send. :param target_type: Ignored. :param use_event: Whether to fire the MessageSent event or not. :return: Boolean describing whether the target was found and messaged. """ # Target and target type are ignored here. self.send({ "action": "chat", "message": message, "user": str(self.ourselves), "primaryGroup": self.name, "prefix": "", "suffix": "", "UUID": "N/A", "world": self.name }) return True def send_msg_other(self, user, message): self.send({ "action": "chat", "message": message, "user": str(user), "primaryGroup": self.name, "prefix": "", "suffix": "", "UUID": "N/A", "world": self.name }) return True def send_action(self, target, message, target_type=None, use_event=True): # Target and target type are ignored here. return self.send_msg(target, "*%s*" % message, target_type, use_event) def send_action_other(self, user, message): self.send({ "action": "chat", "message": "*%s*" % message, "user": str(user), "primaryGroup": self.name, "prefix": "", "suffix": "", "UUID": "N/A", "world": self.name }) def send_connect(self, username): message = {"action": "players", "type": "online", "player": username} self.send(message) def send_disconnect(self, username): message = {"action": "players", "type": "offline", "player": username} self.send(message) def send_get_players(self): message = {"action": "players", "type": "list"} self.send(message) def send_pong(self, timestamp): message = { "pong": timestamp, } self.send(message)
__author__ = 'Gareth Coles' import os import sys print os.getcwd() sys.path.append(os.getcwd()) # Because herp derp import cProfile from profiling.fakes import FakePlugin, FakePluginEvent from system.event_manager import EventManager events = EventManager() class EventPlugin(FakePlugin): def __init__(self, info): super(EventPlugin, self).__init__(info) events.add_callback("Test", self, self.event_callback, 0) def event_callback(self, event): pass def do_profile(): plugin = EventPlugin({"name": "FAAAAAAAAKE!"}) cProfile.run("run()")
class DrunkPlugin(plugin.PluginObject): commands = None config = None storage = None def setup(self): ### Grab important shit self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() ### Initial config load try: self.config = self.storage.get_file(self, "config", YAML, "plugins/drunkoctopus.yml") except Exception: self.logger.exception("Error loading configuration!") self.logger.error("Disabling...") self._disable_self() return if not self.config.exists: self.logger.error("Unable to find config/plugins/drunkoctopus.yml") self.logger.error("Disabling...") self._disable_self() return ### Create vars and stuff self._sobering_call = None self._drunktalk = DrunkTalk() ### Load options from config self._load() self.config.add_callback(self._load) ### Register events and commands self.events.add_callback("MessageSent", self, self.outgoing_message_handler, 1) self.commands.register_command("drunkenness", self.drunkenness_command, self, "drunkoctopus.drunkenness", default=True) self.commands.register_command("drink", self.drink_command, self, "drunkoctopus.drink") def reload(self): try: self.config.reload() except Exception: self.logger.exception("Error reloading configuration!") return False return True def _load(self): self._drunktalk.drunkenness = self.config["drunkenness"] self._cooldown_enabled = self.config["cooldown"]["enabled"] self._cooldown_time = self.config["cooldown"]["time"] self._cooldown_amount = self.config["cooldown"]["amount"] self._drinks = self.config["drinks"] # Sort out the sobering deferred as necessary if self._cooldown_enabled: if self._sobering_call is None: self.logger.trace("Starting sobering call due to config " "change") self._sobering_call = reactor.callLater(self._cooldown_time, self._sober_up) else: if self._sobering_call is not None: self.logger.trace("Cancelling sobering call due to config " "change") self._sobering_call.cancel() def _sober_up(self): self.logger.trace("Sobering up") drunk = self._drunktalk.drunkenness drunk -= self._cooldown_amount if drunk < 0: drunk = 0 self._drunktalk.drunkenness = drunk if self._cooldown_enabled: reactor.callLater(self._cooldown_time, self._sober_up) def drunkenness_command(self, protocol, caller, source, command, raw_args, parsed_args): args = raw_args.split() # Quick fix for new command handler signature if len(args) == 0: caller.respond("Drunkenness level: %s" % self._drunktalk.drunkenness) return elif len(args) == 1: try: new_drunk = int(args[0]) self._drunktalk.drunkenness = new_drunk caller.respond("New drunkenness level: %s" % self._drunktalk.drunkenness) return except: caller.respond("Invalid drunkenness level (use without " "arguments for usage)") else: caller.respond("Usage: {CHARS}drunkenness [percent level]") def drink_command(self, protocol, caller, source, command, raw_args, parsed_args): args = raw_args.split() # Quick fix for new command handler signature if len(args) == 0: caller.respond("Usage: {CHARS}drink <type of drink>") return drink = " ".join(args) drinkl = drink.lower() if drinkl in self._drinks: protocol.send_action(source, "drinks {}".format(drink)) self._drunktalk.drunkenness += self._drinks[drinkl] else: caller.respond("I don't have any of that.") def outgoing_message_handler(self, event): """ :type event: MessageSent """ self.logger.trace("RECEIVED %s EVENT: %s" % (event.type, event.message)) event.message = self._drunktalk.drunk_typing(event.message)
class DrunkPlugin(plugin.PluginObject): commands = None config = None storage = None def setup(self): ### Grab important shit self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() ### Initial config load try: self.config = self.storage.get_file(self, "config", YAML, "plugins/drunkoctopus.yml") except Exception: self.logger.exception("Error loading configuration!") self.logger.error("Disabling...") self._disable_self() return if not self.config.exists: self.logger.error("Unable to find config/plugins/drunkoctopus.yml") self.logger.error("Disabling...") self._disable_self() return ### Create vars and stuff self._sobering_call = None self._drunktalk = DrunkTalk() ### Load options from config self._load() self.config.add_callback(self._load) ### Register events and commands self.events.add_callback("MessageSent", self, self.outgoing_message_handler, 1) self.commands.register_command("drunkenness", self.drunkenness_command, self, "drunkoctopus.drunkenness", default=True) self.commands.register_command("drink", self.drink_command, self, "drunkoctopus.drink") def reload(self): try: self.config.reload() except Exception: self.logger.exception("Error reloading configuration!") return False return True def _load(self): self._drunktalk.drunkenness = self.config["drunkenness"] self._cooldown_enabled = self.config["cooldown"]["enabled"] self._cooldown_time = self.config["cooldown"]["time"] self._cooldown_amount = self.config["cooldown"]["amount"] self._drinks = self.config["drinks"] # Sort out the sobering deferred as necessary if self._cooldown_enabled: if self._sobering_call is None: self.logger.trace("Starting sobering call due to config " "change") self._sobering_call = reactor.callLater( self._cooldown_time, self._sober_up) else: if self._sobering_call is not None: self.logger.trace("Cancelling sobering call due to config " "change") self._sobering_call.cancel() def _sober_up(self): self.logger.trace("Sobering up") drunk = self._drunktalk.drunkenness drunk -= self._cooldown_amount if drunk < 0: drunk = 0 self._drunktalk.drunkenness = drunk if self._cooldown_enabled: reactor.callLater(self._cooldown_time, self._sober_up) def drunkenness_command(self, protocol, caller, source, command, raw_args, parsed_args): args = raw_args.split() # Quick fix for new command handler signature if len(args) == 0: caller.respond("Drunkenness level: %s" % self._drunktalk.drunkenness) return elif len(args) == 1: try: new_drunk = int(args[0]) self._drunktalk.drunkenness = new_drunk caller.respond("New drunkenness level: %s" % self._drunktalk.drunkenness) return except: caller.respond("Invalid drunkenness level (use without " "arguments for usage)") else: caller.respond("Usage: {CHARS}drunkenness [percent level]") def drink_command(self, protocol, caller, source, command, raw_args, parsed_args): args = raw_args.split() # Quick fix for new command handler signature if len(args) == 0: caller.respond("Usage: {CHARS}drink <type of drink>") return drink = " ".join(args) drinkl = drink.lower() if drinkl in self._drinks: protocol.send_action(source, "drinks {}".format(drink)) self._drunktalk.drunkenness += self._drinks[drinkl] else: caller.respond("I don't have any of that.") def outgoing_message_handler(self, event): """ :type event: MessageSent """ self.logger.trace("RECEIVED %s EVENT: %s" % (event.type, event.message)) event.message = self._drunktalk.drunk_typing(event.message)
def setup(self): self.events = EventManager() self.regex = re.compile(r"(\w+)-ass (\w+)") self.events.add_callback("MessageReceived", self, self.ass_swap, 1)
class TriggersPlugin(plugin.PluginObject): commands = None events = None storage = None _config = None def setup(self): ### Grab important shit self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() ### Initial config load try: self._config = self.storage.get_file(self, "config", YAML, "plugins/triggers.yml") except Exception: self.logger.exception("Error loading configuration!") self.logger.error("Disabling...") self._disable_self() return if not self._config.exists: self.logger.error("Unable to find config/plugins/triggers.yml") self.logger.error("Disabling...") self._disable_self() return ### Register event handlers def _message_event_filter(event=MessageReceived): return isinstance(event.target, Channel) self.events.add_callback("MessageReceived", self, self.message_handler, 1, _message_event_filter) self.events.add_callback("ActionReceived", self, self.action_handler, 1, _message_event_filter) def reload(self): try: self._config.reload() except Exception: self.logger.exception("Error reloading configuration!") return False return True @property def _triggers(self): return self._config.get("triggers", {}) def message_handler(self, event=MessageReceived): self.event_handler( event.caller, event.source, event.target, event.message, event.type ) def action_handler(self, event=ActionReceived): self.event_handler( event.caller, event.source, event.target, event.message, "action" ) def event_handler(self, protocol, source, target, message, e_type): """ Event handler for general messages """ allowed = self.commands.perm_handler.check("triggers.trigger", source, target, protocol) if not allowed: return # TODO: Rewrite this when Matcher is finished # TODO: We check the types of half of these - do the rest global_triggers = self._triggers.get("global", []) proto_trigger_block = self._triggers.get("protocols", {}) proto_triggers = proto_trigger_block.get(protocol.name, {}) if not isinstance(proto_triggers, dict): self.logger.error( "Invalid triggers for protocol '%s'" % protocol.name ) return proto_triggers_global = proto_triggers.get("global", []) channel_triggers_block = proto_triggers.get("channels", {}) channel_triggers = [] _channel = None for _channel, _triggers in channel_triggers_block.iteritems(): if protocol.get_channel(_channel) == target: channel_triggers = _triggers break if not isinstance(channel_triggers, list): self.logger.error( "Invalid triggers for channel '%s' in protocol '%s'" % ( _channel, protocol.name ) ) return for trigger in itertools.chain(channel_triggers, proto_triggers_global, global_triggers): try: trigger_regex = trigger["trigger"] responses = trigger["response"] chance = trigger.get("chance", 100) flags = trigger.get("flags", "") trigger_types = trigger.get("trigger_types", {"message": True}) response_type = trigger.get("response_type", "message") if not trigger_types.get(e_type, False): continue if random.random() * 100 >= chance: continue response = random.choice(responses) response_type = response_type.lower() flags_parsed = 0 for flag in flags.lower(): if flag == "i": flags_parsed += re.I elif flag == "u": flags_parsed += re.U elif flag == "l": flags_parsed += re.L elif flag == "m": flags_parsed += re.M elif flag == "x": flags_parsed += re.X elif flag == "s": flags_parsed += re.S elif flag == "d": flags_parsed += re.DEBUG else: self.log.warning("Unknown regex flag '%s'" % flag) # TODO: Rate limiting # re caches compiled patterns internally, so we don't have to match = re.search(trigger_regex, message, flags_parsed) if match: # Hack to get around the fact that regex groups start at # one, but formatting args start at 0 format_args = [""] format_args.extend(match.groups()) format_kwargs = {} for k, v in match.groupdict().iteritems(): format_kwargs[k] = v format_kwargs["channel"] = _channel format_kwargs["source"] = source format_kwargs["target"] = target format_kwargs["message"] = message format_kwargs["protocol"] = protocol.name response_formatted = response.format( *format_args, **format_kwargs ) if response_type == "message": protocol.send_msg(target, response_formatted) elif response_type == "action": protocol.send_action(target, response_formatted) elif response_type == "notice": if hasattr(protocol, "send_notice"): protocol.send_notice(target, response_formatted) else: self.logger.error( "Cannot respond with notice on protocol: '%s'" % protocol.name ) else: self.logger.error( "Invalid response_type '%s'" % response_type ) except Exception: self.logger.exception( "Invalid trigger for channel '%s' in protocol '%s'" % ( _channel, protocol.name ) )
def setup(self): """ Called when the plugin is loaded. Performs initial setup. """ reload(auth_handler) reload(permissions_handler) self.logger.trace(_("Entered setup method.")) self.storage = StorageManager() try: self.config = self.storage.get_file(self, "config", YAML, "plugins/auth.yml") except Exception: self.logger.exception(_("Error loading configuration!")) self.logger.error(_("Disabling..")) self._disable_self() return if not self.config.exists: self.logger.error(_("Unable to find config/plugins/auth.yml")) self.logger.error(_("Disabling..")) self._disable_self() return self.commands = CommandManager() self.events = EventManager() if self.config["use-permissions"]: try: self.permissions = self.storage.get_file( self, "data", YAML, "plugins/auth/" # PEP "permissions.yml") except Exception: self.logger.exception( _("Unable to load permissions. They " "will be unavailable!")) else: self.perms_h = permissions_handler.permissionsHandler( self, self.permissions) result = self.commands.set_permissions_handler(self.perms_h) if not result: self.logger.warn(_("Unable to set permissions handler!")) if self.config["use-auth"]: try: self.passwords = self.storage.get_file( self, "data", YAML, "plugins/auth/" # PEP! "passwords.yml") self.blacklist = self.storage.get_file( self, "data", YAML, "plugins/auth/" # PEP! "blacklist.yml") except Exception: self.logger.exception( _("Unable to load user accounts. They " "will be unavailable!")) else: self.auth_h = auth_handler.authHandler(self, self.passwords, self.blacklist) result = self.commands.set_auth_handler(self.auth_h) if not result: self.logger.warn(_("Unable to set auth handler!")) self.logger.debug(_("Registering commands.")) self.commands.register_command("login", self.login_command, self, "auth.login", default=True) self.commands.register_command("logout", self.logout_command, self, "auth.login", default=True) self.commands.register_command("register", self.register_command, self, "auth.register", default=True) self.commands.register_command("passwd", self.passwd_command, self, "auth.passwd", default=True) self.events.add_callback("PreCommand", self, self.pre_command, 10000)
class WebPlugin(PluginObject): """ Web plugin object """ api_log = None api_keys = None api_key_data = {} config = {} data = {} namespace = {} # Global, not used right now handlers = {} # Cyclone handlers interface = "" # Listening interface listen_port = 8080 running = False application = None # Cyclone application port = None # Twisted's server port storage = None template_loader = None navbar_items = {} ## Stuff plugins might find useful commands = None events = None packages = None plugins = None sessions = None stats = None ## Internal(ish) functions def setup(self): self.storage = StorageManager() try: self.config = self.storage.get_file(self, "config", YAML, "plugins/web.yml") self.logger.debug("Config loaded") except Exception: self.logger.exception(_("Error loading configuration")) self._disable_self() return if not self.config.exists: self.logger.error(_("Unable to find config/plugins/web.yml")) self._disable_self() return try: self.data = self.storage.get_file(self, "data", JSON, "plugins/web/data.json") self.logger.debug("Data loaded") except Exception: self.logger.exception("Error loading data file!") return self._disable_self() try: _sessions = self.storage.get_file(self, "data", JSON, "plugins/web/sessions.json") self.logger.debug("Sessions loaded") except Exception: self.logger.exception("Error loading sessions file!") return self._disable_self() try: self.api_log = open("logs/api.log", "w") except Exception: self.logger.exception("Unable to open api log file!") return self._disable_self() try: self.api_key_data = self.storage.get_file( self, "data", JSON, "plugins/web/apikeys.json" ) self.logger.debug("Sessions loaded") except Exception: self.logger.exception("Error loading API keys!") return self._disable_self() try: self.api_log = open("logs/api.log", "w") except Exception: self.logger.exception("Unable to open api log file!") return self._disable_self() self.config.add_callback(self.restart) self.data.add_callback(self.restart) # Index page self.add_handler(r"/", "plugins.web.routes.index.Route") # Login-related self.add_handler(r"/login", "plugins.web.routes.login.Route") self.add_handler(r"/logout", "plugins.web.routes.logout.Route") self.add_handler( r"/login/reset", "plugins.web.routes.login-reset.Route" ) # Accounts-related self.add_handler(r"/account", "plugins.web.routes.account.index.Route") self.add_handler( r"/account/password/change", "plugins.web.routes.account.password.change.Route" ) self.add_handler( r"/account/apikeys/create", "plugins.web.routes.account.apikeys.create.Route" ) self.add_handler( r"/account/apikeys/delete", "plugins.web.routes.account.apikeys.delete.Route" ) self.add_handler( r"/account/users/logout", "plugins.web.routes.account.users.logout.Route" ) # Admin-related self.add_handler( r"/admin", "plugins.web.routes.admin.index.Route" ) self.add_handler( r"/admin/files", "plugins.web.routes.admin.files.Route" ) self.add_handler( r"/admin/files/(config|data)/(.*)", "plugins.web.routes.admin.file.Route" ) self.add_handler( r"/api/admin/get_stats", "plugins.web.routes.api.admin.get_stats.Route" ) self.add_navbar_entry("admin", "/admin", "settings") # API routes self.add_api_handler( r"/plugins/web/get_username", "plugins.web.routes.api.plugins.web.get_username.Route" ) # Stuff routes might find useful self.api_keys = APIKeys(self, self.api_key_data) self.commands = CommandManager() self.events = EventManager() self.packages = Packages(False) self.plugins = PluginManager() self.sessions = Sessions(self, _sessions) self.stats = Stats() # Load 'er up! r = self.load() if not r: self._disable_self() return if not self.factory_manager.running: self.events.add_callback( "ReactorStarted", self, self.start, 0 ) else: self.start() def load(self): if "secret" not in self.data: self.logger.warn("Generating secret. DO NOT SHARE IT WITH ANYONE!") self.logger.warn("It's stored in data/plugins/web/data.json - " "keep this file secure!") with self.data: self.data["secret"] = mkpasswd(60, 20, 20, 20) self.template_loader = TemplateLoader(self) if self.config.get("output_requests", True): log_function = self.log_request else: log_function = self.null_log self.application = Application( list(self.handlers.items()), # Handler list ## General settings xheaders=True, log_function=log_function, gzip=True, # Are there browsers that don't support this now? # error_handler=ErrorHandler, ## Security settings cookie_secret=self.data["secret"], login_url="/login", ## Template settings template_loader=self.template_loader, ## Static file settings static_path="web/static" ) if self.config.get("hosted", False): hosted = self.config["hosted"] if isinstance(hosted, dict): self.interface = os.environ.get(hosted["hostname"], False) self.port = os.environ.get(hosted["port"], False) if not self.interface: self.logger.error( "Unknown env var: %s" % hosted["hostname"] ) return False if not self.port: self.logger.error( "Unknown env var: %s" % hosted["port"] ) return False else: if hosted in ["openshift"]: self.interface = os.environ.get("OPENSHIFT__IP", False) self.port = os.environ.get("OPENSHIFT__PORT", False) if not self.interface: self.logger.error( "Unknown env var: OPENSHIFT__IP - Are you on " "OpenShift?" ) return False if not self.port: self.logger.error( "Unknown env var: OPENSHIFT__PORT - Are you on " "OpenShift?" ) return False else: self.logger.error("Unknown hosted service: %s" % hosted) return False else: if self.config.get("hostname", "0.0.0.0").strip() == "0.0.0.0": self.interface = "" else: self.interface = self.config.get("hostname") self.listen_port = self.config.get("port", 8080) return True def start(self, _=None): self.stats.start() self.port = reactor.listenTCP( self.listen_port, self.application, interface=self.interface ) self.logger.info("Server started") self.running = True self.events.run_callback( "Web/ServerStartedEvent", ServerStartedEvent(self, self.application) ) def stop(self): self.application.doStop() d = self.port.stopListening() d.addCallback(lambda _: self.logger.info("Server stopped")) d.addCallback(lambda _: setattr(self, "running", False)) d.addCallback(lambda _: self.events.run_callback( "Web/ServerStopped", ServerStoppedEvent(self) )) d.addErrback(lambda f: self.logger.error("Failed to stop: %s" % f)) self.stats.stop() return d def restart(self): d = self.stop() d.addCallback(lambda _: [self.load(), self.start()]) def deactivate(self): d = self.stop() self.handlers.clear() self.navbar_items.clear() return d def log_request(self, request): log = self.logger.info status_code = request.get_status() if status_code >= 500: log = self.logger.error elif status_code >= 400: log = self.logger.warn path = request.request.path # Check if this is an API method and hide the key if so matched = re.match(r"/api/v[0-9]/([a-zA-Z0-9]+)/.*", path) if matched: key = matched.groups()[0] user = self.api_keys.get_username(key) if user: path = path.replace(key, "<API: %s>" % user) else: path = path.replace(key, "<API: Invalid key>") log( "[%s] %s %s -> HTTP %s" % ( request.request.remote_ip, request.request.method, path, request.get_status() ) ) def null_log(self, *args, **kwargs): pass ## Public API functions def add_api_handler(self, pattern, handler, version=1): if not pattern.startswith("/"): pattern = "/%s" % pattern pattern = "/api/v%s/([A-Za-z0-9]+)%s" % (version, pattern) return self.add_handler(pattern, handler) def add_handler(self, pattern, handler): self.logger.debug("Adding route: %s -> %s" % (pattern, handler)) if pattern in self.handlers: self.logger.debug("Route already exists.") return False self.handlers[pattern] = handler if self.application is not None: self.application.add_handlers(r".*$", [(pattern, handler)]) self.logger.debug("Handlers list: %s" % list(self.handlers.values())) def add_navbar_entry(self, title, url, icon="question"): if title in self.navbar_items: return False self.logger.debug("Adding navbar entry: %s -> %s" % (title, url)) self.navbar_items[title] = {"url": url, "active": False, "icon": icon} return True def check_permission(self, perm, session=None): if session is None: username = None elif isinstance(session, str) or isinstance(session, unicode): username = session else: username = session["username"] return self.commands.perm_handler.check( perm, username, "web", "plugin-web" ) def remove_api_handlers(self, *names, **kwargs): """ :param names: :param version: :return: """ version = kwargs.get("version", 1) patterns = [] for pattern in names: if not pattern.startswith("/"): pattern = "/%s" % pattern pattern = "/api/v%s/([A-Za-z0-9]+)%s" % (version, pattern) patterns.append(pattern) return self.remove_handlers(*patterns) def remove_handlers(self, *names): found = False for name in names: if name in self.handlers: found = True del self.handlers[name] if found: self.restart() def write_api_log(self, address, key, username, message): self.api_log.write( "%s | %s (%s) | %s\n" % (address, key, username, message) )
class TriggersPlugin(plugin.PluginObject): commands = None events = None storage = None _config = None def setup(self): ### Grab important shit self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() ### Initial config load try: self._config = self.storage.get_file(self, "config", YAML, "plugins/triggers.yml") except Exception: self.logger.exception("Error loading configuration!") self.logger.error("Disabling...") self._disable_self() return if not self._config.exists: self.logger.error("Unable to find config/plugins/triggers.yml") self.logger.error("Disabling...") self._disable_self() return ### Register event handlers def _message_event_filter(event=MessageReceived): return isinstance(event.target, Channel) self.events.add_callback("MessageReceived", self, self.message_handler, 1, _message_event_filter) self.events.add_callback("ActionReceived", self, self.action_handler, 1, _message_event_filter) def reload(self): try: self._config.reload() except Exception: self.logger.exception("Error reloading configuration!") return False return True @property def _triggers(self): return self._config.get("triggers", {}) def message_handler(self, event=MessageReceived): self.event_handler(event.caller, event.source, event.target, event.message, event.type) def action_handler(self, event=ActionReceived): self.event_handler(event.caller, event.source, event.target, event.message, "action") def event_handler(self, protocol, source, target, message, e_type): """ Event handler for general messages """ allowed = self.commands.perm_handler.check("triggers.trigger", source, target, protocol) if not allowed: return # TODO: Rewrite this when Matcher is finished # TODO: We check the types of half of these - do the rest global_triggers = self._triggers.get("global", []) proto_trigger_block = self._triggers.get("protocols", {}) proto_triggers = proto_trigger_block.get(protocol.name, {}) if not isinstance(proto_triggers, dict): self.logger.error("Invalid triggers for protocol '%s'" % protocol.name) return proto_triggers_global = proto_triggers.get("global", []) channel_triggers_block = proto_triggers.get("channels", {}) channel_triggers = [] _channel = None for _channel, _triggers in channel_triggers_block.iteritems(): if protocol.get_channel(_channel) == target: channel_triggers = _triggers break if not isinstance(channel_triggers, list): self.logger.error( "Invalid triggers for channel '%s' in protocol '%s'" % (_channel, protocol.name)) return for trigger in itertools.chain(channel_triggers, proto_triggers_global, global_triggers): try: trigger_regex = trigger["trigger"] responses = trigger["response"] chance = trigger.get("chance", 100) flags = trigger.get("flags", "") trigger_types = trigger.get("trigger_types", {"message": True}) response_type = trigger.get("response_type", "message") if not trigger_types.get(e_type, False): continue if random.random() * 100 >= chance: continue response = random.choice(responses) if isinstance(response, dict): response_type = response.get("type", response_type) response = response["response"] response_type = response_type.lower() flags_parsed = 0 for flag in flags.lower(): if flag == "i": flags_parsed += re.I elif flag == "u": flags_parsed += re.U elif flag == "l": flags_parsed += re.L elif flag == "m": flags_parsed += re.M elif flag == "x": flags_parsed += re.X elif flag == "s": flags_parsed += re.S elif flag == "d": flags_parsed += re.DEBUG else: self.log.warning("Unknown regex flag '%s'" % flag) # TODO: Rate limiting # re caches compiled patterns internally, so we don't have to match = re.search(trigger_regex, message, flags_parsed) if match: # Hack to get around the fact that regex groups start at # one, but formatting args start at 0 format_args = [""] format_args.extend(match.groups()) format_kwargs = {} for k, v in match.groupdict().iteritems(): format_kwargs[k] = v format_kwargs["channel"] = _channel format_kwargs["source"] = source format_kwargs["target"] = target format_kwargs["message"] = message format_kwargs["protocol"] = protocol.name response_formatted = response.format( *format_args, **format_kwargs) if response_type == "message": protocol.send_msg(target, response_formatted) elif response_type == "action": protocol.send_action(target, response_formatted) elif response_type == "notice": if hasattr(protocol, "send_notice"): protocol.send_notice(target, response_formatted) else: self.logger.error( "Cannot respond with notice on protocol: '%s'" % protocol.name) elif response_type == "channel_kick": attempted = protocol.channel_kick( source, channel=_channel, reason=response_formatted) if not attempted: self.logger.warning("Couldn't kick %s from %s" % (source, _channel)) elif response_type == "channel_ban": attempted = protocol.channel_ban( source, channel=_channel, reason=response_formatted) if not attempted: self.logger.warning("Couldn't ban %s from %s" % (source, _channel)) elif response_type == "global_kick": attempted = protocol.global_kick( source, reason=response_formatted) if not attempted: self.logger.warning("Couldn't global kick %s" % source) elif response_type == "global_ban": attempted = protocol.global_ban( source, reason=response_formatted) if not attempted: self.logger.warning("Couldn't global ban %s" % source) else: self.logger.error("Invalid response_type '%s'" % response_type) except Exception: self.logger.exception( "Invalid trigger for channel '%s' in protocol '%s'" % (_channel, protocol.name))
def setup(self): self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() self.data = self.storage.get_file( self, "data", DBAPI, "sqlite3:data/plugins/lastseen/users.sqlite", "data/plugins/lastseen/users.sqlite", check_same_thread=False) self.data.runQuery("CREATE TABLE IF NOT EXISTS users (" "user TEXT, " "protocol TEXT, " "at INTEGER)") self.commands.register_command("seen", self.seen_command, self, "seen.seen", default=True) # General events self.events.add_callback("PreMessageReceived", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_source_caller]) self.events.add_callback("PreCommand", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_source_caller]) self.events.add_callback("NameChanged", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("UserDisconnected", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) # Mumble events self.events.add_callback("Mumble/UserRemove", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserJoined", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserMoved", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserSelfMuteToggle", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserSelfDeafToggle", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserRecordingToggle", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller])
class LastseenPlugin(plugin.PluginObject): commands = None events = None storage = None data = None # SQLite for a change def setup(self): self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() self.data = self.storage.get_file( self, "data", DBAPI, "sqlite3:data/plugins/lastseen/users.sqlite", "data/plugins/lastseen/users.sqlite", check_same_thread=False) self.data.runQuery("CREATE TABLE IF NOT EXISTS users (" "user TEXT, " "protocol TEXT, " "at INTEGER)") self.commands.register_command("seen", self.seen_command, self, "seen.seen", default=True) # General events self.events.add_callback("PreMessageReceived", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_source_caller]) self.events.add_callback("PreCommand", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_source_caller]) self.events.add_callback("NameChanged", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("UserDisconnected", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) # Mumble events self.events.add_callback("Mumble/UserRemove", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserJoined", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserMoved", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserSelfMuteToggle", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserSelfDeafToggle", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserRecordingToggle", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) def _get_user_txn(self, txn, user, protocol): user = user.lower() user = to_unicode(user) txn.execute(u"SELECT * FROM users WHERE user=? AND protocol=?", (user, protocol)) r = txn.fetchone() return r def _get_user_callback(self, result, user, protocol, source): if result is None: source.respond("User '%s' not found." % user) else: then = math.floor(result[2]) now = math.floor(time.time()) seconds = now - then m, s = divmod(seconds, 60) h, m = divmod(m, 60) d, h = divmod(h, 24) s = int(s) m = int(m) h = int(h) d = int(d) if (s + m + h + d) == 0: source.respond("'%s' was seen just now!" % user) else: constructed = "'%s' was seen" % user to_append = [] if d > 0: to_append.append("%s days" % d) if h > 0: to_append.append("%s hours" % h) if m > 0: to_append.append("%s minutes" % m) if s > 0: to_append.append("%s seconds" % s) length = len(to_append) i = 1 for x in to_append: if length - i == 0: if i != 1: constructed += " and %s" % x i += 1 continue if i != 1: constructed += ", %s" % x else: constructed += " %s" % x i += 1 constructed += " ago." source.respond(constructed) def _get_user_callback_fail(self, failure, user, protocol, source): source.respond("Error while finding user %s: %s" % (user, failure)) def get_user(self, user, protocol): return self.data.runInteraction(self._get_user_txn, user, protocol) def _insert_or_update_user(self, txn, user, protocol): user = user.lower() user = to_unicode(user) txn.execute("SELECT * FROM users WHERE user=? AND protocol=?", (user, protocol)) r = txn.fetchone() now = time.time() if r is None: txn.execute("INSERT INTO users VALUES (?, ?, ?)", (user, protocol, now)) return False else: txn.execute("UPDATE users SET at=? WHERE user=? AND protocol=?", (now, user, protocol)) return True def insert_or_update_user(self, user, protocol): self.data.runInteraction(self._insert_or_update_user, user, protocol) def seen_command(self, protocol, caller, source, command, raw_args, parsed_args): args = raw_args.split() # Quick fix for new command handler signature if not args: caller.respond("Usage: {CHARS}seen <username>") else: user = "******".join(args) if user.lower() == protocol.ourselves.nickname.lower(): source.respond("I'm right here, smartass.") return if user.lower() == caller.nickname.lower(): source.respond("Having a bit of an out-of-body experience, " "%s?" % caller.nickname) return d = self.get_user(user, protocol.name) d.addCallbacks(self._get_user_callback, self._get_user_callback_fail, callbackArgs=(user.lower(), protocol.name, source), errbackArgs=(user.lower(), protocol.name, source)) def event_handler(self, event, handler): """ This is a generic function so that other plugins can catch events and cause a user's last seen value to update. The handler should return (username, protocol name) as a tuple, or a list of tuples if it needs to do more than one update. """ data = handler(event) if not isinstance(data, list): data = [data] for element in data: user, proto = element self.insert_or_update_user(user, proto) def event_source_caller(self, event): user = event.source.nickname proto = event.caller.name return user, proto def event_user_caller(self, event): user = event.user.nickname proto = event.caller.name return user, proto
def setup(self): """ Called when the plugin is loaded. Performs initial setup. """ reload(auth_handler) reload(permissions_handler) self.logger.trace(_("Entered setup method.")) self.storage = StorageManager() try: self.config = self.storage.get_file(self, "config", YAML, "plugins/auth.yml") except Exception: self.logger.exception(_("Error loading configuration!")) self.logger.error(_("Disabling..")) self._disable_self() return if not self.config.exists: self.logger.error(_("Unable to find config/plugins/auth.yml")) self.logger.error(_("Disabling..")) self._disable_self() return self.commands = CommandManager() self.events = EventManager() if self.config["use-permissions"]: try: self.permissions = self.storage.get_file(self, "data", YAML, "plugins/auth/" # PEP "permissions.yml") except Exception: self.logger.exception(_("Unable to load permissions. They " "will be unavailable!")) else: self.perms_h = permissions_handler.permissionsHandler( self, self.permissions) result = self.commands.set_permissions_handler(self.perms_h) if not result: self.logger.warn(_("Unable to set permissions handler!")) if self.config["use-auth"]: try: self.passwords = self.storage.get_file(self, "data", YAML, "plugins/auth/" # PEP! "passwords.yml") self.blacklist = self.storage.get_file(self, "data", YAML, "plugins/auth/" # PEP! "blacklist.yml") except Exception: self.logger.exception(_("Unable to load user accounts. They " "will be unavailable!")) else: self.auth_h = auth_handler.authHandler(self, self.passwords, self.blacklist) result = self.commands.set_auth_handler(self.auth_h) if not result: self.logger.warn(_("Unable to set auth handler!")) self.logger.debug(_("Registering commands.")) self.commands.register_command("login", self.login_command, self, "auth.login", default=True) self.commands.register_command("logout", self.logout_command, self, "auth.login", default=True) self.commands.register_command("register", self.register_command, self, "auth.register", default=True) self.commands.register_command("passwd", self.passwd_command, self, "auth.passwd", default=True) self.events.add_callback("PreCommand", self, self.pre_command, 10000)
class AuthPlugin(plugin.PluginObject): """ Auth plugin. In charge of logins and permissions. """ config = None passwords = None permissions = None blacklist = None commands = None events = None storage = None auth_h = None perms_h = None def setup(self): """ Called when the plugin is loaded. Performs initial setup. """ reload(auth_handler) reload(permissions_handler) self.logger.trace(_("Entered setup method.")) self.storage = StorageManager() try: self.config = self.storage.get_file(self, "config", YAML, "plugins/auth.yml") except Exception: self.logger.exception(_("Error loading configuration!")) self.logger.error(_("Disabling..")) self._disable_self() return if not self.config.exists: self.logger.error(_("Unable to find config/plugins/auth.yml")) self.logger.error(_("Disabling..")) self._disable_self() return self.commands = CommandManager() self.events = EventManager() if self.config["use-permissions"]: try: self.permissions = self.storage.get_file(self, "data", YAML, "plugins/auth/" # PEP "permissions.yml") except Exception: self.logger.exception(_("Unable to load permissions. They " "will be unavailable!")) else: self.perms_h = permissions_handler.permissionsHandler( self, self.permissions) result = self.commands.set_permissions_handler(self.perms_h) if not result: self.logger.warn(_("Unable to set permissions handler!")) if self.config["use-auth"]: try: self.passwords = self.storage.get_file(self, "data", YAML, "plugins/auth/" # PEP! "passwords.yml") self.blacklist = self.storage.get_file(self, "data", YAML, "plugins/auth/" # PEP! "blacklist.yml") except Exception: self.logger.exception(_("Unable to load user accounts. They " "will be unavailable!")) else: self.auth_h = auth_handler.authHandler(self, self.passwords, self.blacklist) result = self.commands.set_auth_handler(self.auth_h) if not result: self.logger.warn(_("Unable to set auth handler!")) self.logger.debug(_("Registering commands.")) self.commands.register_command("login", self.login_command, self, "auth.login", default=True) self.commands.register_command("logout", self.logout_command, self, "auth.login", default=True) self.commands.register_command("register", self.register_command, self, "auth.register", default=True) self.commands.register_command("passwd", self.passwd_command, self, "auth.passwd", default=True) self.events.add_callback("PreCommand", self, self.pre_command, 10000) def pre_command(self, event=PreCommand): """ Pre-command hook to remove passwords from the log output. """ self.logger.trace(_("Command: %s") % event.command) if event.command.lower() in ["login", "register"]: if len(event.args) >= 2: split_ = event.printable.split("%s " % event.command) second_split = split_[1].split() second_split[1] = _("[REDACTED]") split_[1] = " ".join(second_split) donestr = "%s " % event.command done = donestr.join(split_) event.printable = done elif event.command.lower() == "passwd": split_ = event.printable.split("%s " % event.command) second_split = split_[1].split() dsplit = [] for x in second_split: dsplit.append(_("[REDACTED]")) split_[1] = " ".join(dsplit) donestr = "%s " % event.command done = donestr.join(split_) event.printable = done def login_command(self, protocol, caller, source, command, raw_args, parsed_args): """ Command handler for the login command - for logging users in. """ args = raw_args.split() # Quick fix for new command handler signature if len(args) < 2: caller.respond(__("Usage: {CHARS}login <username> <password>")) else: if self.auth_h.authorized(caller, source, protocol): caller.respond(__("You're already logged in. " "Try logging out first!")) return username = args[0] password = args[1] result = self.auth_h.login(caller, protocol, username, password) if not result: self.logger.warn(_("%s failed to login as %s") % (caller.nickname, username)) caller.respond(__("Invalid username or password!")) else: self.logger.info(_("%s logged in as %s") % (caller.nickname, username)) caller.respond(__("You are now logged in as %s.") % username) def logout_command(self, protocol, caller, source, command, raw_args, parsed_args): """ Command handler for the logout command - for logging users out. """ if self.auth_h.authorized(caller, source, protocol): self.auth_h.logout(caller, protocol) caller.respond(__("You have been logged out successfully.")) else: caller.respond(__("You're not logged in.")) def register_command(self, protocol, caller, source, command, raw_args, parsed_args): """ Command handler for the register command - for creating new user accounts. """ args = raw_args.split() # Quick fix for new command handler signature if len(args) < 2: caller.respond(__("Usage: {CHARS}register <username> <password>")) return username = args[0] password = args[1] if isinstance(source, Channel): source.respond(__("You can't create an account in a channel.")) caller.respond(__("Don't use this command in a channel!")) caller.respond(__("You should only use it in a private message.")) caller.respond(__("For your security, the password you used has " "been blacklisted.")) self.auth_h.blacklist_password(password, username) return if self.auth_h.user_exists(username): caller.respond(__("That username already exists!")) return if self.auth_h.password_backlisted(password, username): caller.respond(__("That password has been blacklisted. " "Try another!")) return if self.auth_h.create_user(username, password): caller.respond(__("Your account has been created and you will now " "be logged in. Thanks for registering!")) self.perms_h.create_user(username) self.login_command(caller, source, [username, password], protocol, raw_args, parsed_args) else: caller.respond(__("Something went wrong when creating your " "account! You should ask the bot operators " "about this.")) def passwd_command(self, protocol, caller, source, command, raw_args, parsed_args): """ Command handler for the passwd command - for changing passwords. """ args = raw_args.split() # Quick fix for new command handler signature if len(args) < 2: caller.respond(__("Usage: {CHARS}passwd <old password> " "<new password>")) return if not self.auth_h.authorized(caller, source, protocol): caller.respond(__("You must be logged in to change your " "password.")) return username = caller.auth_name old = args[0] new = args[1] if self.auth_h.password_backlisted(new, username): caller.respond(__("That password has been blacklisted. Try " "another!")) return if self.auth_h.change_password(username, old, new): caller.respond(__("Your password has been changed successfully.")) else: caller.respond(__("Old password incorrect - please try again!")) self.logger.warn(_("User %s failed to change the password for %s") % (caller, username)) def get_auth_handler(self): """ API function for getting the auth handler. This will return None if no handler is registered. """ if self.config["use-auth"]: return self.auth_h return None def get_permissions_handler(self): """ API function for getting the permissions handler. This will return None if no handler is registered. """ if self.config["use-permissions"]: return self.perms_h return None def deactivate(self): """ Called when the plugin is deactivated. Does nothing right now. """ if self.config["use-auth"]: if isinstance( self.commands.auth_handler, auth_handler.authHandler ): self.commands.auth_handler = None if self.config["use-permissions"]: if isinstance( self.commands.perm_handler, permissions_handler.permissionsHandler ): self.commands.perm_handler = None
class Manager(object): """ Manager for keeping track of multiple factories - one per protocol. This is so that the bot can connect to multiple services at once, and have them communicate with each other. """ __metaclass__ = Singleton #: Instance of the storage manager storage = None #: Storage for all of our factories. factories = {} #: Storage for all of the protocol configs. configs = {} #: The main configuration is stored here. main_config = None #: Whether the manager is already running or not running = False def __init__(self): self.commands = CommandManager() self.event_manager = EventManager() self.logger = getLogger("Manager") self.plugman = PluginManager(self) self.yapsy_logger = getLogger("yapsy") self.metrics = None @property def all_plugins(self): return self.plugman.info_objects @property def loaded_plugins(self): return self.plugman.plugin_objects def setup(self): signal.signal(signal.SIGINT, self.signal_callback) self.yapsy_logger.debug_ = self.yapsy_logger.debug self.yapsy_logger.debug = self.yapsy_logger.trace self.storage = StorageManager() self.main_config = self.storage.get_file(self, "config", YAML, "settings.yml") self.commands.set_factory_manager(self) self.load_config() # Load the configuration try: self.metrics = Metrics(self.main_config, self) except Exception: self.logger.exception(_("Error setting up metrics.")) self.plugman.scan() self.load_plugins() # Load the configured plugins self.load_protocols() # Load and set up the protocols if not len(self.factories): self.logger.info(_("It seems like no protocols are loaded. " "Shutting down..")) return def run(self): if not self.running: event = ReactorStartedEvent(self) reactor.callLater(0, self.event_manager.run_callback, "ReactorStarted", event) self.running = True reactor.run() else: raise RuntimeError(_("Manager is already running!")) def signal_callback(self, signum, frame): try: try: self.unload() except Exception: self.logger.exception(_("Error while unloading!")) try: reactor.stop() except Exception: try: reactor.crash() except Exception: pass except Exception: exit(0) # Load stuff def load_config(self): """ Load the main configuration file. :return: Whether the config was loaded or not :rtype: bool """ try: self.logger.info(_("Loading global configuration..")) if not self.main_config.exists: self.logger.error(_( "Main configuration not found! Please correct this and try" " again.")) return False except IOError: self.logger.error(_( "Unable to load main configuration at config/settings.yml")) self.logger.error(_("Please check that this file exists.")) return False except Exception: self.logger.exception(_( "Unable to load main configuration at config/settings.yml")) return False return True def load_plugins(self): """ Attempt to load all of the plugins. """ self.logger.info(_("Loading plugins..")) self.logger.trace(_("Configured plugins: %s") % ", ".join(self.main_config["plugins"])) self.plugman.load_plugins(self.main_config.get("plugins", [])) event = PluginsLoadedEvent(self, self.plugman.plugin_objects) self.event_manager.run_callback("PluginsLoaded", event) @deprecated("Use the plugin manager directly") def load_plugin(self, name, unload=False): """ Load a single plugin by name. This will return one of the system.enums.PluginState values. :param name: The plugin to load. :type name: str :param unload: Whether to unload the plugin, if it's already loaded. :type unload: bool """ result = self.plugman.load_plugin(name) if result is PluginState.AlreadyLoaded: if unload: result_two = self.plugman.unload_plugin(name) if result_two is not PluginState.Unloaded: return result_two result = self.plugman.load_plugin(name) return result @deprecated("Use the plugin manager directly") def collect_plugins(self): """ Collect all possible plugin candidates. """ self.plugman.scan() def load_protocols(self): """ Load and set up all of the configured protocols. """ self.logger.info(_("Setting up protocols..")) for protocol in self.main_config["protocols"]: if protocol.lower().startswith("plugin-"): self.logger.error("Invalid protocol name: %s" % protocol) self.logger.error( "Protocol names beginning with \"plugin-\" are reserved " "for plugin use." ) continue self.logger.info(_("Setting up protocol: %s") % protocol) conf_location = "protocols/%s.yml" % protocol result = self.load_protocol(protocol, conf_location) if result is not PROTOCOL_LOADED: if result is PROTOCOL_ALREADY_LOADED: self.logger.warn(_("Protocol is already loaded.")) elif result is PROTOCOL_CONFIG_NOT_EXISTS: self.logger.warn(_("Unable to find protocol " "configuration.")) elif result is PROTOCOL_LOAD_ERROR: self.logger.warn(_("Error detected while loading " "protocol.")) elif result is PROTOCOL_SETUP_ERROR: self.logger.warn(_("Error detected while setting up " "protocol.")) def load_protocol(self, name, conf_location): """ Attempt to load a protocol by name. This can return one of the following, from system.constants: * PROTOCOL_ALREADY_LOADED * PROTOCOL_CONFIG_NOT_EXISTS * PROTOCOL_LOAD_ERROR * PROTOCOL_LOADED * PROTOCOL_SETUP_ERROR :param name: The name of the protocol :type name: str :param conf_location: The location of the config file, relative to the config/ directory, or a Config object :type conf_location: str, Config """ if name in self.factories: return PROTOCOL_ALREADY_LOADED config = conf_location if not isinstance(conf_location, Config): # TODO: Prevent upward directory traversal properly conf_location = conf_location.replace("..", "") try: config = self.storage.get_file(self, "config", YAML, conf_location) if not config.exists: return PROTOCOL_CONFIG_NOT_EXISTS except Exception: self.logger.exception( _("Unable to load configuration for the '%s' protocol.") % name) return PROTOCOL_LOAD_ERROR try: self.factories[name] = Factory(name, config, self) self.factories[name].setup() return PROTOCOL_LOADED except Exception: if name in self.factories: del self.factories[name] self.logger.exception( _("Unable to create factory for the '%s' protocol!") % name) return PROTOCOL_SETUP_ERROR # Reload stuff @deprecated("Use the plugin manager directly") def reload_plugin(self, name): """ Attempt to reload a plugin by name. This will return one of the system.enums.PluginState values. :param name: The name of the plugin :type name: str """ return self.plugman.reload_plugin(name) def reload_protocol(self, name): factory = self.get_factory(name) if name is not None: factory.shutdown() factory.setup() return True # Unload stuff @deprecated("Use the plugin manager directly") def unload_plugin(self, name): """ Attempt to unload a plugin by name. This will return one of the system.enums.PluginState values. :param name: The name of the plugin :type name: str """ return self.plugman.unload_plugin(name) def unload_protocol(self, name): # Removes with a shutdown """ Attempt to unload a protocol by name. This will also shut it down. :param name: The name of the protocol :type name: str :return: Whether the protocol was unloaded :rtype: bool """ if name in self.factories: proto = self.factories[name] try: proto.shutdown() except Exception: self.logger.exception(_("Error shutting down protocol %s") % name) finally: try: self.storage.release_file(self, "config", "protocols/%s.yml" % name) self.storage.release_files(proto) self.storage.release_files(proto.protocol) except Exception: self.logger.exception("Error releasing files for protocol " "%s" % name) del self.factories[name] return True return False def unload(self): """ Shut down and unload everything. """ # Shut down! for name in self.factories.keys(): self.logger.info(_("Unloading protocol: %s") % name) self.unload_protocol(name) self.plugman.unload_plugins() if reactor.running: try: reactor.stop() except Exception: self.logger.exception("Error stopping reactor") # Grab stuff def get_protocol(self, name): """ Get the instance of a protocol, by name. :param name: The name of the protocol :type name: str :return: The protocol, or None if it doesn't exist. """ if name in self.factories: return self.factories[name].protocol return None def get_factory(self, name): """ Get the instance of a protocol's factory, by name. :param name: The name of the protocol :type name: str :return: The factory, or None if it doesn't exist. """ if name in self.factories: return self.factories[name] return None @deprecated("Use the plugin manager directly") def get_plugin(self, name): """ Get the insatnce of a plugin, by name. :param name: The name of the plugin :type name: str :return: The plugin, or None if it isn't loaded. """ return self.plugman.get_plugin(name) def remove_protocol(self, protocol): # Removes without shutdown """ Remove a protocol without shutting it down. You shouldn't use this. :param protocol: The name of the protocol :type protocol: str :return: Whether the protocol was removed. :rtype: bool """ if protocol in self.factories: del self.factories[protocol] return True return False
class LastseenPlugin(plugin.PluginObject): commands = None events = None storage = None data = None # SQLite for a change def setup(self): self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() self.data = self.storage.get_file( self, "data", DBAPI, "sqlite3:data/plugins/lastseen/users.sqlite", "data/plugins/lastseen/users.sqlite", check_same_thread=False ) self.data.runQuery("CREATE TABLE IF NOT EXISTS users (" "user TEXT, " "protocol TEXT, " "at INTEGER)") self.commands.register_command("seen", self.seen_command, self, "seen.seen", default=True) # General events self.events.add_callback("PreMessageReceived", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_source_caller]) self.events.add_callback("PreCommand", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_source_caller]) self.events.add_callback("NameChanged", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("UserDisconnected", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) # Mumble events self.events.add_callback("Mumble/UserRemove", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserJoined", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserMoved", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserSelfMuteToggle", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserSelfDeafToggle", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) self.events.add_callback("Mumble/UserRecordingToggle", self, self.event_handler, 0, cancelled=True, extra_args=[self.event_user_caller]) def _get_user_txn(self, txn, user, protocol): user = user.lower() txn.execute("SELECT * FROM users WHERE user=? AND protocol=?", (user, protocol)) r = txn.fetchone() return r def _get_user_callback(self, result, user, protocol, source): if result is None: source.respond("User '%s' not found." % user) else: then = math.floor(result[2]) now = math.floor(time.time()) seconds = now - then m, s = divmod(seconds, 60) h, m = divmod(m, 60) d, h = divmod(h, 24) s = int(s) m = int(m) h = int(h) d = int(d) if (s + m + h + d) == 0: source.respond("'%s' was seen just now!" % user) else: constructed = "'%s' was seen" % user to_append = [] if d > 0: to_append.append("%s days" % d) if h > 0: to_append.append("%s hours" % h) if m > 0: to_append.append("%s minutes" % m) if s > 0: to_append.append("%s seconds" % s) length = len(to_append) i = 1 for x in to_append: if length - i == 0: if i != 1: constructed += " and %s" % x i += 1 continue if i != 1: constructed += ", %s" % x else: constructed += " %s" % x i += 1 constructed += " ago." source.respond(constructed) def _get_user_callback_fail(self, failure, user, protocol, source): source.respond("Error while finding user %s: %s" % (user, failure)) def get_user(self, user, protocol): return self.data.runInteraction(self._get_user_txn, user, protocol) def _insert_or_update_user(self, txn, user, protocol): user = user.lower() txn.execute("SELECT * FROM users WHERE user=? AND protocol=?", (user, protocol)) r = txn.fetchone() now = time.time() if r is None: txn.execute( "INSERT INTO users VALUES (?, ?, ?)", (user, protocol, now) ) return False else: txn.execute( "UPDATE users SET at=? WHERE user=? AND protocol=?", (now, user, protocol) ) return True def insert_or_update_user(self, user, protocol): self.data.runInteraction(self._insert_or_update_user, user, protocol) def seen_command(self, protocol, caller, source, command, raw_args, parsed_args): args = raw_args.split() # Quick fix for new command handler signature if not args: caller.respond("Usage: {CHARS}seen <username>") else: user = "******".join(args) if user.lower() == protocol.ourselves.nickname.lower(): source.respond("I'm right here, smartass.") return if user.lower() == caller.nickname.lower(): source.respond("Having a bit of an out-of-body experience, " "%s?" % caller.nickname) return d = self.get_user(user, protocol.name) d.addCallbacks(self._get_user_callback, self._get_user_callback_fail, callbackArgs=(user.lower(), protocol.name, source), errbackArgs=(user.lower(), protocol.name, source)) def event_handler(self, event, handler): """ This is a generic function so that other plugins can catch events and cause a user's last seen value to update. The handler should return (username, protocol name) as a tuple, or a list of tuples if it needs to do more than one update. """ data = handler(event) if not isinstance(data, list): data = [data] for element in data: user, proto = element self.insert_or_update_user(user, proto) def event_source_caller(self, event): user = event.source.nickname proto = event.caller.name return user, proto def event_user_caller(self, event): user = event.user.nickname proto = event.caller.name return user, proto
class FactoidsPlugin(plugin.PluginObject): CHANNEL = "channel" PROTOCOL = "protocol" GLOBAL = "global" PERM_ADD = "factoids.add.%s" PERM_SET = "factoids.set.%s" PERM_DEL = "factoids.delete.%s" PERM_GET = "factoids.get.%s" (RES_INVALID_LOCATION, RES_INVALID_METHOD, # _FOR_LOCATION - i.e. CHANNEL in PM RES_NO_PERMS, RES_MISSING_FACTOID) = xrange(4) def setup(self): # ## Grab important shit self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() self.plugman = PluginManager() # ## Set up database self.database = self.storage.get_file( self, "data", DBAPI, "sqlite3:data/plugins/factoids.sqlite", "data/plugins/factoids.sqlite", check_same_thread=False ) self.database.add_callback(self.reload) self.reload() # ## Register commands # We have multiple possible permissions per command, so we have to do # permission handling ourselves self.commands.register_command("addfactoid", self.factoid_add_command, self, None) self.commands.register_command("setfactoid", self.factoid_set_command, self, None) self.commands.register_command("deletefactoid", self.factoid_delete_command, self, None, ["delfactoid"]) self.commands.register_command("getfactoid", self.factoid_get_command, self, None, default=True) # ## Register events self.events.add_callback("MessageReceived", self, self.message_handler, 1) self.events.add_callback("Web/ServerStartedEvent", self, self.web_routes, 1) def reload(self): with self.database as db: db.runQuery("CREATE TABLE IF NOT EXISTS factoids (" "factoid_key TEXT, " "location TEXT, " "protocol TEXT, " "channel TEXT, " "factoid_name TEXT, " "info TEXT, " "UNIQUE(factoid_key, location, protocol, channel) " "ON CONFLICT REPLACE)") # region Util functions def __check_perm(self, perm, caller, source, protocol): self.logger.trace(_("Checking for permission: '%s'"), perm) allowed = self.commands.perm_handler.check(perm, caller, source, protocol) return allowed def _parse_args(self, raw_args): """ Grabs the location, factoid name, and info from a raw_args string """ pos = raw_args.find(" ") if pos < 0: raise ValueError(_("Invalid args")) location = raw_args[:pos] pos2 = raw_args.find(" ", pos + 1) if pos2 < 0: raise ValueError(_("Invalid args")) factoid = raw_args[pos + 1:pos2] # pos3 = raw_args.find(" ", pos2 + 1) info = raw_args[pos2 + 1:] if info == "": raise ValueError(_("Invalid args")) return location, factoid, info def valid_location(self, location, source=None): """ Checks if a given location is one of channel, protocol or global, and if it's a channel request, that it's in a channel. """ location = location.lower() result = location in (self.CHANNEL, self.PROTOCOL, self.GLOBAL) if not result: raise InvalidLocationError(_("'%s' is not a valid location") % location) if source is not None: if location == self.CHANNEL and not isinstance(source, Channel): raise InvalidMethodError(_("'channel' location can only be " "used inside a channel")) return True # endregion # region API functions to access factoids def _add_factoid_interaction(self, txn, factoid_key, location, protocol, channel, factoid, info): """ Appends a factoid to an existing one if there, otherwise creates it. :return: True if already exists, otherwise False """ txn.execute("SELECT * FROM factoids WHERE " "factoid_key = ? AND location = ? AND " "protocol = ? AND channel = ?", ( to_unicode(factoid_key), to_unicode(location), to_unicode(protocol), to_unicode(channel) )) results = txn.fetchall() if len(results) == 0: # Factoid doesn't exist yet, create it txn.execute("INSERT INTO factoids VALUES(?, ?, ?, ?, ?, ?)", ( to_unicode(factoid_key), to_unicode(location), to_unicode(protocol), to_unicode(channel), to_unicode(factoid), to_unicode(info) )) e = FactoidAddedEvent(self, factoid_key, factoid) self.events.run_callback("Factoids/Added", e, from_thread=True) return False else: # Factoid already exists, append txn.execute("INSERT INTO factoids VALUES(?, ?, ?, ?, ?, ?)", ( to_unicode(results[0][0]), to_unicode(results[0][1]), to_unicode(results[0][2]), to_unicode(results[0][3]), to_unicode(results[0][4]), results[0][5] + u"\n" + to_unicode(info) )) e = FactoidUpdatedEvent(self, factoid_key, factoid) self.events.run_callback("Factoids/Updated", e, from_thread=True) return True def _delete_factoid_interaction(self, txn, factoid_key, location, protocol, channel): """ Deletes a factoid if it exists, otherwise raises MissingFactoidError """ self.logger.trace("DELETE | Key: %s | Loc: %s | Pro: %s | Cha: %s" % (factoid_key, location, protocol, channel)) if location == self.CHANNEL: txn.execute("DELETE FROM factoids WHERE factoid_key = ? AND " "location = ? AND protocol = ? AND channel = ?", ( to_unicode(factoid_key), to_unicode(location), to_unicode(protocol), to_unicode(channel) )) else: txn.execute("DELETE FROM factoids WHERE factoid_key = ? AND " "location = ? AND protocol = ?", ( to_unicode(factoid_key), to_unicode(location), to_unicode(protocol) )) if txn.rowcount == 0: raise MissingFactoidError(_("Factoid '%s' does not exist") % factoid_key) e = FactoidDeletedEvent(self, factoid_key) self.events.run_callback("Factoids/Deleted", e, from_thread=True) def _get_factoid_interaction(self, txn, factoid_key, location, protocol, channel): """ Gets a factoid if it exists, otherwise raises MissingFactoidError :return: (factoid_name, [entry, entry, ...]) """ self.logger.trace(_("Getting factoid params: factoid_key = '%s', " "location = '%s', protocol = '%s', " "channel = '%s'"), factoid_key, location, protocol, channel) if location is None: self.logger.trace(_("Location is None - getting all factoids with " "key '%s'"), factoid_key) txn.execute("SELECT location, protocol, channel, factoid_name, " "info FROM factoids WHERE factoid_key = ?", ( to_unicode(factoid_key), )) results = txn.fetchall() if len(results) > 0: # Check for channel match for row in results: if ((row[0] == self.CHANNEL and row[1] == protocol and row[2] == channel)): self.logger.trace(_("Match found (channel)!")) return (row[3], row[4].split("\n")) # Check for protocol match for row in results: if row[0] == self.PROTOCOL and row[1] == protocol: self.logger.trace(_("Match found (protocol)!")) return (row[3], row[4].split("\n")) # Check for global match for row in results: if row[0] == self.GLOBAL: self.logger.trace(_("Match found (global)!")) return (row[3], row[4].split("\n")) else: txn.execute("SELECT location, protocol, channel, factoid_name, " "info FROM factoids WHERE factoid_key = ? AND " "location = ? AND protocol = ? AND channel = ?", ( to_unicode(factoid_key), to_unicode(location), to_unicode(protocol), to_unicode(channel) )) results = txn.fetchall() if len(results) > 0: return (results[0][3], results[0][4].split("\n")) raise MissingFactoidError(_("Factoid '%s' does not exist") % factoid_key) def _get_all_factoids_interaction(self, txn): """ Gets all factoids :return: (factoid_name, [entry, entry, ...]) """ self.logger.trace("Getting all factoids.") txn.execute("SELECT location, protocol, channel, factoid_name, " "info FROM factoids") results = txn.fetchall() return results def get_all_factoids(self): with self.database as db: return db.runInteraction(self._get_all_factoids_interaction) def add_factoid(self, caller, source, protocol, location, factoid, info): location = location.lower() factoid_key = factoid.lower() protocol_key = protocol.name.lower() channel_key = source.name.lower() try: location is None or self.valid_location(location, source) except Exception as ex: return defer.fail(ex) if not self.__check_perm(self.PERM_ADD % location, caller, source, protocol): return defer.fail( NoPermissionError(_("User does not have required permission")) ) with self.database as db: return db.runInteraction(self._add_factoid_interaction, factoid_key, location, protocol_key, channel_key, factoid, info) def set_factoid(self, caller, source, protocol, location, factoid, info): location = location.lower() factoid_key = factoid.lower() protocol_key = protocol.name.lower() channel_key = source.name.lower() try: location is None or self.valid_location(location, source) except Exception as ex: return defer.fail(ex) if not self.__check_perm(self.PERM_SET % location, caller, source, protocol): return defer.fail( NoPermissionError(_("User does not have required permission")) ) with self.database as db: return db.runQuery( "INSERT INTO factoids VALUES(?, ?, ?, ?, ?, ?)", ( to_unicode(factoid_key), to_unicode(location), to_unicode(protocol_key), to_unicode(channel_key), to_unicode(factoid), to_unicode(info) )) def delete_factoid(self, caller, source, protocol, location, factoid): location = location.lower() factoid_key = factoid.lower() protocol_key = protocol.name.lower() channel_key = source.name.lower() try: location is None or self.valid_location(location, source) except Exception as ex: return defer.fail(ex) if not self.__check_perm(self.PERM_DEL % location, caller, source, protocol): return defer.fail( NoPermissionError(_("User does not have required permission")) ) with self.database as db: return db.runInteraction(self._delete_factoid_interaction, factoid_key, location, protocol_key, channel_key) def get_factoid(self, caller, source, protocol, location, factoid): if location is not None: location = location.lower() factoid_key = factoid.lower() protocol_key = protocol.name.lower() channel_key = source.name.lower() try: location is None or self.valid_location(location, source) except Exception as ex: return defer.fail(ex) if not self.__check_perm(self.PERM_GET % location, caller, source, protocol): return defer.fail( NoPermissionError(_("User does not have required permission")) ) with self.database as db: return db.runInteraction(self._get_factoid_interaction, factoid_key, location, protocol_key, channel_key) # endregion # region Command handlers for interacting with factoids def _factoid_command_fail(self, caller, failure): """ :type failure: twisted.python.failure.Failure """ if failure.check(InvalidLocationError): caller.respond(__("Invalid location given - possible locations " "are: channel, protocol, global")) elif failure.check(InvalidMethodError): caller.respond(__("You must do that in a channel")) elif failure.check(NoPermissionError): caller.respond(__("You don't have permission to do that")) elif failure.check(MissingFactoidError): caller.respond(__("That factoid doesn't exist")) else: # TODO: We should probably handle this failure.raiseException() def _factoid_get_command_success(self, source, result, args=None): if not args: args = [] for line in result[1]: # _tokens = tokens.find_tokens(line) _numerical = tokens.find_numerical_tokens(line) for i, arg in enumerate(args): line = line.replace("{%d}" % i, arg) for token in _numerical: line = line.replace(token, "") # TODO: Token handlers source.respond("(%s) %s" % (result[0], line)) def factoid_add_command(self, protocol, caller, source, command, raw_args, parsed_args): try: location, factoid, info = self._parse_args(raw_args) except Exception: caller.respond(__("Usage: %s <location> <factoid> <info>") % command) return d = self.add_factoid(caller, source, protocol, location, factoid, info) d.addCallbacks( lambda r: caller.respond(__("Factoid added")), lambda f: self._factoid_command_fail(caller, f) ) def factoid_set_command(self, protocol, caller, source, command, raw_args, parsed_args): try: location, factoid, info = self._parse_args(raw_args) except Exception: caller.respond(__("Usage: %s <location> <factoid> <info>") % command) return d = self.set_factoid(caller, source, protocol, location, factoid, info) d.addCallbacks( lambda r: caller.respond(__("Factoid set")), lambda f: self._factoid_command_fail(caller, f) ) def factoid_delete_command(self, protocol, caller, source, command, raw_args, parsed_args): args = raw_args.split() # Quick fix for new command handler signature if len(args) != 2: caller.respond(__("Usage: %s <location> <factoid>") % command) return location = args[0] factoid = args[1] d = self.delete_factoid(caller, source, protocol, location, factoid) d.addCallbacks( lambda r: caller.respond(__("Factoid deleted")), lambda f: self._factoid_command_fail(caller, f) ) def factoid_get_command(self, protocol, caller, source, command, raw_args, parsed_args): args = raw_args.split() # Quick fix for new command handler signature if len(args) == 1: factoid = args[0] location = None elif len(args) == 2: location = args[0] factoid = args[1] else: caller.respond(__("Usage: %s [location] <factoid>") % command) return d = self.get_factoid(caller, source, protocol, location, factoid) d.addCallbacks( lambda r: self._factoid_get_command_success(source, r), lambda f: self._factoid_command_fail(caller, f) ) # endregion def _print_query(self, result): from pprint import pprint pprint(result) def web_routes(self, event=None): self.logger.info(_("Registering web route..")) #: :type: WebPlugin web = self.plugman.get_plugin("Web") if web is None: self.logger.debug("Web plugin not found.") return web.add_handler(r"/factoids", "plugins.factoids.route.Route") web.add_handler(r"/factoids/", "plugins.factoids.route.Route") web.add_navbar_entry("factoids", "/factoids", "text file outline") def message_handler(self, event): """ Handle ??-style factoid "commands" :type event: MessageReceived """ handlers = { "??": self._message_handler_get, "?<": self._message_handler_get_self, "??<": self._message_handler_get_self, "?>": self._message_handler_get_other, "??>": self._message_handler_get_other, "??+": self._message_handler_add, "??~": self._message_handler_set, "??-": self._message_handler_delete, "!?+": self._message_handler_add_global, "!?~": self._message_handler_set_global, "!?-": self._message_handler_delete_global, "@?+": self._message_handler_add_protocol, "@?~": self._message_handler_set_protocol, "@?-": self._message_handler_delete_protocol } msg = event.message command = None factoid = "" args = "" pos = msg.find(" ") split = msg.split(" ") if pos < 0: command = msg else: command = msg[:pos] pos2 = msg.find(" ", pos + 1) if pos2 < 0: factoid = msg[pos + 1:].strip() else: factoid = msg[pos + 1:pos2].strip() args = msg[pos2 + 1:].strip() if command in handlers: handlers[command](command, factoid, args, event, split) # ## Getting "commands" def _message_handler_get(self, command, factoid, args, event, split): """ Handle ?? factoid "command" :type event: MessageReceived """ if not factoid: event.source.respond(__("Usage: ?? <factoid>")) return d = self.get_factoid(event.source, event.target, event.caller, None, factoid) d.addCallbacks( lambda r: self._factoid_get_command_success(event.target, r, split[2:]), lambda f: self._factoid_command_fail(event.source, f) ) def _message_handler_get_self(self, command, factoid, args, event, split): """ Handle ?< factoid "command" :type event: MessageReceived """ if not factoid: event.source.respond(__("Usage: ?< <factoid>")) return d = self.get_factoid(event.source, event.target, event.caller, None, factoid) d.addCallbacks( lambda r: self._factoid_get_command_success(event.source, r, split[2:]), lambda f: self._factoid_command_fail(event.source, f) ) def _message_handler_get_other(self, command, factoid, args, event, split): """ Handle ?> factoid "command" :type event: MessageReceived """ if not len(split) > 2: event.source.respond(__("Usage: ?> <user> <factoid>")) return wanted = split[1] factoid = split[2] user = event.caller.get_user(wanted) if user is None: event.source.respond(__("Unable to find that user.")) return d = self.get_factoid(event.source, event.target, event.caller, None, factoid) d.addCallbacks( lambda r: self._factoid_get_command_success(user, r, split[3:]), lambda f: self._factoid_command_fail(event.source, f) ) # ## Channel "commands" def _message_handler_add(self, command, factoid, args, event, split): """ Handle ??+ factoid "command" :type event: MessageReceived """ if not factoid or not args: event.source.respond(__("Usage: ??+ <factoid> <info>")) return d = self.add_factoid(event.source, event.target, event.caller, self.CHANNEL, factoid, args) d.addCallbacks( lambda r: event.source.respond(__("Factoid added")), lambda f: self._factoid_command_fail(event.source, f) ) def _message_handler_set(self, command, factoid, args, event, split): """ Handle ??~ factoid "command" :type event: MessageReceived """ if not factoid or not args: event.source.respond(__("Usage: ??~ <factoid> <info>")) return d = self.set_factoid(event.source, event.target, event.caller, self.CHANNEL, factoid, args) d.addCallbacks( lambda r: event.source.respond(__("Factoid set")), lambda f: self._factoid_command_fail(event.source, f) ) def _message_handler_delete(self, command, factoid, args, event, split): """ Handle ??- factoid "command" :type event: MessageReceived """ if factoid is None: event.source.respond(__("Usage: ??- <factoid>")) return d = self.delete_factoid(event.source, event.target, event.caller, self.CHANNEL, factoid) d.addCallbacks( lambda r: event.source.respond(__("Factoid deleted")), lambda f: self._factoid_command_fail(event.source, f) ) # ## Global "commands" def _message_handler_add_global(self, command, factoid, args, event, split): """ Handle !?+ factoid "command" :type event: MessageReceived """ if not factoid or not args: event.source.respond(__("Usage: !?+ <factoid> <info>")) return d = self.add_factoid(event.source, event.target, event.caller, self.GLOBAL, factoid, args) d.addCallbacks( lambda r: event.source.respond(__("Factoid added")), lambda f: self._factoid_command_fail(event.source, f) ) def _message_handler_set_global(self, command, factoid, args, event, split): """ Handle !?~ factoid "command" :type event: MessageReceived """ if not factoid or not args: event.source.respond(__("Usage: !?~ <factoid> <info>")) return d = self.set_factoid(event.source, event.target, event.caller, self.GLOBAL, factoid, args) d.addCallbacks( lambda r: event.source.respond(__("Factoid set")), lambda f: self._factoid_command_fail(event.source, f) ) def _message_handler_delete_global(self, command, factoid, args, event, split): """ Handle !?- factoid "command" :type event: MessageReceived """ if factoid is None: event.source.respond(__("Usage: !?- <factoid>")) return d = self.delete_factoid(event.source, event.target, event.caller, self.GLOBAL, factoid) d.addCallbacks( lambda r: event.source.respond(__("Factoid deleted")), lambda f: self._factoid_command_fail(event.source, f) ) # ## Protocol-specific "commands" def _message_handler_add_protocol(self, command, factoid, args, event, split): """ Handle @?+ factoid "command" :type event: MessageReceived """ if not factoid or not args: event.source.respond(__("Usage: @?+ <factoid> <info>")) return d = self.add_factoid(event.source, event.target, event.caller, self.PROTOCOL, factoid, args) d.addCallbacks( lambda r: event.source.respond(__("Factoid added")), lambda f: self._factoid_command_fail(event.source, f) ) def _message_handler_set_protocol(self, command, factoid, args, event, split): """ Handle @?~ factoid "command" :type event: MessageReceived """ if not factoid or not args: event.source.respond(__("Usage: @?~ <factoid> <info>")) return d = self.set_factoid(event.source, event.target, event.caller, self.PROTOCOL, factoid, args) d.addCallbacks( lambda r: event.source.respond(__("Factoid set")), lambda f: self._factoid_command_fail(event.source, f) ) def _message_handler_delete_protocol(self, command, factoid, args, event, split): """ Handle @?- factoid "command" :type event: MessageReceived """ if factoid is None: event.source.respond(__("Usage: @?- <factoid>")) return d = self.delete_factoid(event.source, event.target, event.caller, self.PROTOCOL, factoid) d.addCallbacks( lambda r: event.source.respond(__("Factoid deleted")), lambda f: self._factoid_command_fail(event.source, f) )
def setup(self): self.storage = StorageManager() try: self.config = self.storage.get_file(self, "config", YAML, "plugins/web.yml") self.logger.debug("Config loaded") except Exception: self.logger.exception(_("Error loading configuration")) self._disable_self() return if not self.config.exists: self.logger.error(_("Unable to find config/plugins/web.yml")) self._disable_self() return try: self.data = self.storage.get_file(self, "data", JSON, "plugins/web/data.json") self.logger.debug("Data loaded") except Exception: self.logger.exception("Error loading data file!") return self._disable_self() try: _sessions = self.storage.get_file(self, "data", JSON, "plugins/web/sessions.json") self.logger.debug("Sessions loaded") except Exception: self.logger.exception("Error loading sessions file!") return self._disable_self() try: self.api_log = open("logs/api.log", "w") except Exception: self.logger.exception("Unable to open api log file!") return self._disable_self() try: self.api_key_data = self.storage.get_file( self, "data", JSON, "plugins/web/apikeys.json" ) self.logger.debug("Sessions loaded") except Exception: self.logger.exception("Error loading API keys!") return self._disable_self() try: self.api_log = open("logs/api.log", "w") except Exception: self.logger.exception("Unable to open api log file!") return self._disable_self() self.config.add_callback(self.restart) self.data.add_callback(self.restart) # Index page self.add_handler(r"/", "plugins.web.routes.index.Route") # Login-related self.add_handler(r"/login", "plugins.web.routes.login.Route") self.add_handler(r"/logout", "plugins.web.routes.logout.Route") self.add_handler( r"/login/reset", "plugins.web.routes.login-reset.Route" ) # Accounts-related self.add_handler(r"/account", "plugins.web.routes.account.index.Route") self.add_handler( r"/account/password/change", "plugins.web.routes.account.password.change.Route" ) self.add_handler( r"/account/apikeys/create", "plugins.web.routes.account.apikeys.create.Route" ) self.add_handler( r"/account/apikeys/delete", "plugins.web.routes.account.apikeys.delete.Route" ) self.add_handler( r"/account/users/logout", "plugins.web.routes.account.users.logout.Route" ) # Admin-related self.add_handler( r"/admin", "plugins.web.routes.admin.index.Route" ) self.add_handler( r"/admin/files", "plugins.web.routes.admin.files.Route" ) self.add_handler( r"/admin/files/(config|data)/(.*)", "plugins.web.routes.admin.file.Route" ) self.add_handler( r"/api/admin/get_stats", "plugins.web.routes.api.admin.get_stats.Route" ) self.add_navbar_entry("admin", "/admin", "settings") # API routes self.add_api_handler( r"/plugins/web/get_username", "plugins.web.routes.api.plugins.web.get_username.Route" ) # Stuff routes might find useful self.api_keys = APIKeys(self, self.api_key_data) self.commands = CommandManager() self.events = EventManager() self.packages = Packages(False) self.plugins = PluginManager() self.sessions = Sessions(self, _sessions) self.stats = Stats() # Load 'er up! r = self.load() if not r: self._disable_self() return if not self.factory_manager.running: self.events.add_callback( "ReactorStarted", self, self.start, 0 ) else: self.start()
class DialectizerPlugin(plugin.PluginObject): """Dialectizer plugin object""" commands = None data = None events = None storage = None dialectizers = {"chef": Chef(), "fudd": Fudd(), "lower": Lower(), "off": Dialectizer(), "olde": Olde(), "reverse": Reverse(), "upper": Upper()} def setup(self): """The list of bridging rules""" self.commands = CommandManager() self.events = EventManager() self.storage = StorageManager() self.data = self.storage.get_file(self, "data", YAML, "plugins/dialectizer/settings.yml") self.events.add_callback("MessageSent", self, self.handle_msg_sent, 1) self.commands.register_command("dialectizer", self.dialectizer_command, self, "dialectizer.set", aliases=["dialectiser"]) def handle_msg_sent(self, event=MessageSent): """Handler for general message sent event""" if isinstance(event.target, User): return name = event.caller.name target = event.target.name with self.data: if name not in self.data: self.data[name] = {} if target not in self.data[name]: self.data[name][target] = "off" subber = self.dialectizers[self.data[name][target]] message = event.message message = subber.sub(message) event.message = message def dialectizer_command(self, protocol, caller, source, command, raw_args, parsed_args): """Handler for the dialectizer command""" args = raw_args.split() # Quick fix for new command handler signature if isinstance(source, User): caller.respond(__("This command only applies to channels.")) return if not len(args) > 0: caller.respond(__("Usage: {CHARS}dialectizer <dialectizer>")) caller.respond(__("Available dialectizers: %s") % ", ".join(self.dialectizers.keys())) return with self.data: if protocol.name not in self.data: self.data[protocol.name] = {} if source.name not in self.data[protocol.name]: self.data[protocol.name][source.name] = "off" setting = args[0].lower() if setting not in self.dialectizers: caller.respond(__("Unknown dialectizer: %s") % setting) return self.data[protocol.name][source.name] = setting caller.respond(__("Dialectizer set to '%s'") % setting)
class AuthPlugin(plugin.PluginObject): """ Auth plugin. In charge of logins and permissions. """ config = None passwords = None permissions = None blacklist = None commands = None events = None storage = None auth_h = None perms_h = None def setup(self): """ Called when the plugin is loaded. Performs initial setup. """ reload(auth_handler) reload(permissions_handler) self.logger.trace(_("Entered setup method.")) self.storage = StorageManager() try: self.config = self.storage.get_file(self, "config", YAML, "plugins/auth.yml") except Exception: self.logger.exception(_("Error loading configuration!")) self.logger.error(_("Disabling..")) self._disable_self() return if not self.config.exists: self.logger.error(_("Unable to find config/plugins/auth.yml")) self.logger.error(_("Disabling..")) self._disable_self() return self.commands = CommandManager() self.events = EventManager() if self.config["use-permissions"]: try: self.permissions = self.storage.get_file( self, "data", YAML, "plugins/auth/" # PEP "permissions.yml") except Exception: self.logger.exception( _("Unable to load permissions. They " "will be unavailable!")) else: self.perms_h = permissions_handler.permissionsHandler( self, self.permissions) result = self.commands.set_permissions_handler(self.perms_h) if not result: self.logger.warn(_("Unable to set permissions handler!")) if self.config["use-auth"]: try: self.passwords = self.storage.get_file( self, "data", YAML, "plugins/auth/" # PEP! "passwords.yml") self.blacklist = self.storage.get_file( self, "data", YAML, "plugins/auth/" # PEP! "blacklist.yml") except Exception: self.logger.exception( _("Unable to load user accounts. They " "will be unavailable!")) else: self.auth_h = auth_handler.authHandler(self, self.passwords, self.blacklist) result = self.commands.set_auth_handler(self.auth_h) if not result: self.logger.warn(_("Unable to set auth handler!")) self.logger.debug(_("Registering commands.")) self.commands.register_command("login", self.login_command, self, "auth.login", default=True) self.commands.register_command("logout", self.logout_command, self, "auth.login", default=True) self.commands.register_command("register", self.register_command, self, "auth.register", default=True) self.commands.register_command("passwd", self.passwd_command, self, "auth.passwd", default=True) self.events.add_callback("PreCommand", self, self.pre_command, 10000) def pre_command(self, event=PreCommand): """ Pre-command hook to remove passwords from the log output. """ self.logger.trace(_("Command: %s") % event.command) if event.command.lower() in ["login", "register"]: if len(event.args) >= 2: split_ = event.printable.split("%s " % event.command) second_split = split_[1].split() second_split[1] = _("[REDACTED]") split_[1] = " ".join(second_split) donestr = "%s " % event.command done = donestr.join(split_) event.printable = done elif event.command.lower() == "passwd": split_ = event.printable.split("%s " % event.command) second_split = split_[1].split() dsplit = [] for x in second_split: dsplit.append(_("[REDACTED]")) split_[1] = " ".join(dsplit) donestr = "%s " % event.command done = donestr.join(split_) event.printable = done def login_command(self, protocol, caller, source, command, raw_args, parsed_args): """ Command handler for the login command - for logging users in. """ args = raw_args.split() # Quick fix for new command handler signature if len(args) < 2: caller.respond(__("Usage: {CHARS}login <username> <password>")) else: if self.auth_h.authorized(caller, source, protocol): caller.respond( __("You're already logged in. " "Try logging out first!")) return username = args[0] password = args[1] result = self.auth_h.login(caller, protocol, username, password) if not result: self.logger.warn( _("%s failed to login as %s") % (caller.nickname, username)) caller.respond(__("Invalid username or password!")) else: self.logger.info( _("%s logged in as %s") % (caller.nickname, username)) caller.respond(__("You are now logged in as %s.") % username) def logout_command(self, protocol, caller, source, command, raw_args, parsed_args): """ Command handler for the logout command - for logging users out. """ if self.auth_h.authorized(caller, source, protocol): self.auth_h.logout(caller, protocol) caller.respond(__("You have been logged out successfully.")) else: caller.respond(__("You're not logged in.")) def register_command(self, protocol, caller, source, command, raw_args, parsed_args): """ Command handler for the register command - for creating new user accounts. """ args = raw_args.split() # Quick fix for new command handler signature if len(args) < 2: caller.respond(__("Usage: {CHARS}register <username> <password>")) return username = args[0] password = args[1] if isinstance(source, Channel): source.respond(__("You can't create an account in a channel.")) caller.respond(__("Don't use this command in a channel!")) caller.respond(__("You should only use it in a private message.")) caller.respond( __("For your security, the password you used has " "been blacklisted.")) self.auth_h.blacklist_password(password, username) return if self.auth_h.user_exists(username): caller.respond(__("That username already exists!")) return if self.auth_h.password_backlisted(password, username): caller.respond( __("That password has been blacklisted. " "Try another!")) return if self.auth_h.create_user(username, password): caller.respond( __("Your account has been created and you will now " "be logged in. Thanks for registering!")) self.perms_h.create_user(username) self.login_command(caller, source, [username, password], protocol, raw_args, parsed_args) else: caller.respond( __("Something went wrong when creating your " "account! You should ask the bot operators " "about this.")) def passwd_command(self, protocol, caller, source, command, raw_args, parsed_args): """ Command handler for the passwd command - for changing passwords. """ args = raw_args.split() # Quick fix for new command handler signature if len(args) < 2: caller.respond( __("Usage: {CHARS}passwd <old password> " "<new password>")) return if not self.auth_h.authorized(caller, source, protocol): caller.respond( __("You must be logged in to change your " "password.")) return username = caller.auth_name old = args[0] new = args[1] if self.auth_h.password_backlisted(new, username): caller.respond( __("That password has been blacklisted. Try " "another!")) return if self.auth_h.change_password(username, old, new): caller.respond(__("Your password has been changed successfully.")) else: caller.respond(__("Old password incorrect - please try again!")) self.logger.warn( _("User %s failed to change the password for %s") % (caller, username)) def get_auth_handler(self): """ API function for getting the auth handler. This will return None if no handler is registered. """ if self.config["use-auth"]: return self.auth_h return None def get_permissions_handler(self): """ API function for getting the permissions handler. This will return None if no handler is registered. """ if self.config["use-permissions"]: return self.perms_h return None def deactivate(self): """ Called when the plugin is deactivated. Does nothing right now. """ if self.config["use-auth"]: if isinstance(self.commands.auth_handler, auth_handler.authHandler): self.commands.auth_handler = None if self.config["use-permissions"]: if isinstance(self.commands.perm_handler, permissions_handler.permissionsHandler): self.commands.perm_handler = None
class Protocol(LineOnlyReceiver, NoChannelsProtocol): __version__ = "0.0.1" __inter_version__ = 3 TYPE = "inter" CHANNELS = False factory = None config = None log = None event_manager = None command_manager = None nickname = "" ourselves = None channel = None can_flood = False control_chars = "." handshake_done = False inter_servers = {} _users = [] def __init__(self, name, factory, config): NoChannelsProtocol.__init__(self, name, factory, config) self.name = name self.log = getLogger(self.name) self.event_manager = EventManager() self.command_manager = CommandManager() reactor.connectTCP( self.config["connection"]["host"], self.config["connection"]["port"], self.factory, 120 ) def connectionMade(self): self.handshake_done = False self.inter_servers = {} self.nickname = "" self.control_chars = self.config["control_char"] self.ourselves = User(self.config["nickname"], self, True) self.channel = Channel(protocol=self) def lineReceived(self, line): try: self.log.trace("<- %s" % repr(line)) message = json.loads(line) except Exception: self.log.exception("Failed to parse line") else: if "version" in message: v = message["version"] if v != self.__inter_version__: self.log.error("Protocol version mismatch!") self.log.error("Ours: %s | Theirs: %s" % (self.__inter_version__, v)) self.factory.manager.remove_protocol(self.name) return self.log.info("Connected to Inter, version %s" % v) message = { "api_key": self.config["connection"]["api_key"] } self.send(message) if "from" in message: origin = message["from"] for case, default in Switch(origin): if case("chat"): source = message["source"] msg = message["message"] user = self.get_user( message["user"], server=source, create=True ) if not user.server: user.server = source if user == self.ourselves: break # Since, well, this is us. if source == self.nickname: break # Since this is also us. event = general_events.PreMessageReceived( self, user, user, msg, "message" # No channels ) self.event_manager.run_callback("PreMessageReceived", event) if event.printable: for line in event.message.split("\n"): self.log.info("<%s> %s" % (user, line)) if not event.cancelled: result = self.command_manager.process_input( event.message, user, user, self, self.control_chars, self.nickname ) for c, d in Switch(result[0]): if c(CommandState.RateLimited): self.log.debug("Command rate-limited") user.respond("That command has been " "rate-limited, please try " "again later.") return # It was a command if c(CommandState.NotACommand): self.log.debug("Not a command") break if c(CommandState.UnknownOverridden): self.log.debug("Unknown command " "overridden") return # It was a command if c(CommandState.Unknown): self.log.debug("Unknown command") break if c(CommandState.Success): self.log.debug("Command ran successfully") return # It was a command if c(CommandState.NoPermission): self.log.debug("No permission to run " "command") return # It was a command if c(CommandState.Error): user.respond("Error running command: " "%s" % result[1]) return # It was a command if d: self.log.debug("Unknown command state: " "%s" % result[0]) break second_event = general_events.MessageReceived( self, user, user, msg, "message" ) self.event_manager.run_callback( "MessageReceived", second_event ) break if case("players"): _type = message["type"] target = message["target"] if _type == "list": if target == "all": # All servers, we can just overwrite the list. self.inter_servers = {} players = message["players"] for key in players: self.inter_servers[key] = [] for user in players[key]: obj = User(user, self, True) obj.server = key self.inter_servers[key].append(obj) if obj not in self._users: self._users.append(obj) self.log.info("Got player list.") for key in self.inter_servers.keys(): self.log.info( "%s: %s players" % ( key, len(self.inter_servers[key]) ) ) event = inter_events.InterServerListReceived( self, self.inter_servers ) self.event_manager.run_callback( "Inter/ServerListReceived", event ) else: # Unexpected! self.log.warn("Unknown list target: %s" % target) elif _type == "offline": player = self.get_user( message["player"], target, True ) player.server = target if target not in self.inter_servers: self.inter_servers[target] = [] if player in self.inter_servers[target]: self.inter_servers[target].remove(player) if player in self._users: self._users.remove(player) self.log.info("%s disconnected from %s." % (player, target)) event = general_events.UserDisconnected( self, player ) self.event_manager.run_callback( "UserDisconnected", event ) second_event = inter_events.InterPlayerDisonnected( self, player ) self.event_manager.run_callback( "Inter/PlayerDisconnected", second_event ) elif _type == "online": player = self.get_user( message["player"], target, True ) player.server = target if target not in self.inter_servers: self.inter_servers[target] = [] if player not in self.inter_servers[target]: self.inter_servers[target].append(player) if player not in self._users: self._users.append(player) self.log.info("%s connected to %s." % (player, target)) event = inter_events.InterPlayerConnected( self, player ) self.event_manager.run_callback( "Inter/PlayerConnected", event ) break if case("auth"): action = message["action"] if action == "authenticated": if not self.handshake_done and "status" in message: status = message["status"] if status == "success": self.nickname = message["name"] self.log.info("Authenticated as %s" % self.nickname) self.handshake_done = True # Get players self.send_get_players() self.send_connect(self.ourselves.nickname) event = inter_events.InterAuthenticated( self ) self.event_manager.run_callback( "Inter/Authenticated", event ) else: error = message["error"] self.log.error("Error authenticating: %s" % error) event = ( inter_events.InterAuthenticationError( self, error ) ) self.event_manager.run_callback( "Inter/AuthenticationError", event ) self.transport.close() self.factory.manager.remove_protocol( self.name ) else: name = message["name"] self.log.info("Server connected to Inter: %s" % name) event = inter_events.InterServerConnected( self, name ) self.event_manager.run_callback( "Inter/ServerConnected", event ) else: name = message["name"] self.log.info("Server disconnected from Inter: %s" % name) event = inter_events.InterServerDisonnected( self, name ) self.event_manager.run_callback( "Inter/ServerDisconnected", event ) if name in self.inter_servers: del self.inter_servers[name] break if case("core"): event = inter_events.InterCoreMessage( self, message ) self.event_manager.run_callback( "Inter/CoreMessage", event ) break if case("ping"): timestamp = message["timestamp"] event = inter_events.InterPing( self, timestamp ) self.event_manager.run_callback( "Inter/Ping", event ) self.send_pong(timestamp) break if default: self.log.warn("Unknown message origin: %s" % origin) event = inter_events.InterUnknownMessage( self, message ) self.event_manager.run_callback( "Inter/UnknownMessage", event ) break def send(self, _json): self.sendLine( json.dumps(_json) ) def sendLine(self, line): self.log.trace("-> %s" % repr(line)) LineOnlyReceiver.sendLine(self, line) def shutdown(self): """ Called when a protocol needs to disconnect. Cleanup should be done here. """ self.transport.loseConnection() def get_user(self, username, server=None, create=False): """ Used to retrieve a user. Return None if we can't find it. :param user: string representing the user we need. """ if server is None: for key in self.inter_servers: for user in self.inter_servers[key]: if user.name.lower() == username.lower(): return user else: if server in self.inter_servers: for user in self.inter_servers[server]: if user.name.lower() == username.lower(): return user if create: return User(username, self) return None def send_msg(self, target, message, target_type=None, use_event=True): """ Send a message. :param target: Ignored. :param message: The message to send. :param target_type: Ignored. :param use_event: Whether to fire the MessageSent event or not. :return: Boolean describing whether the target was found and messaged. """ # Target and target type are ignored here. self.send( { "action": "chat", "message": message, "user": str(self.ourselves), "primaryGroup": self.name, "prefix": "", "suffix": "", "UUID": "N/A", "world": self.name } ) return True def send_msg_other(self, user, message): self.send( { "action": "chat", "message": message, "user": str(user), "primaryGroup": self.name, "prefix": "", "suffix": "", "UUID": "N/A", "world": self.name } ) return True def send_action(self, target, message, target_type=None, use_event=True): # Target and target type are ignored here. return self.send_msg(target, "*%s*" % message, target_type, use_event) def send_action_other(self, user, message): self.send( { "action": "chat", "message": "*%s*" % message, "user": str(user), "primaryGroup": self.name, "prefix": "", "suffix": "", "UUID": "N/A", "world": self.name } ) def send_connect(self, username): message = { "action": "players", "type": "online", "player": username } self.send(message) def send_disconnect(self, username): message = { "action": "players", "type": "offline", "player": username } self.send(message) def send_get_players(self): message = { "action": "players", "type": "list" } self.send(message) def send_pong(self, timestamp): message = { "pong": timestamp, } self.send(message)