Exemplo n.º 1
0
 def setUp(self):
     Database.clear_db()
     self.processor = Processor()
     mapper = UserMapper()
     self.males = mapper.get_all_males()
     self.females = mapper.get_all_females()
     self.others = mapper.get_other_genders()
Exemplo n.º 2
0
 def setUp(self):
     Database.clear_db()
     self.mapper = SongRequestMapper()
     self.song_1 = self.mapper.create_request("song", "message", "url",
                                              "title", "artist")
     self.song_2 = self.mapper.create_request("songa", "messagea", "urla",
                                              "titleaa", "artistaa")
Exemplo n.º 3
0
async def test_unsub():
    subs = [
        Subscription(guild_id=2, channel_id=6, _filter="type:nominate"),
        Subscription(guild_id=2, channel_id=4, _filter="user:someone"),
        Subscription(guild_id=1, channel_id=6, _filter="type:nominate")
    ]
    database = Database(BOT_TEST_DB_NAME)
    for sub in subs:
        database.insert_subscription(sub)
    subscriber.load()

    mock_message = MockMessage(
        channel=MockChannel(_id=6, guild=MockGuild(_id=2)))
    mock_command = MockCommand("unsub", context=mock_message)

    assert all(sub in subscriber.cache for sub in subs)

    assert await receive_command(mock_command)
    assert mock_command.response.startswith("✓")
    assert "🔕" in mock_command.response_embed.fields[0].name.lower()
    assert "unsubscribed from" in mock_command.response_embed.fields[
        0].name.lower()
    assert "type:nominate" in mock_command.response_embed.fields[0].value
    assert "`type:nominate`" in mock_command.response_embed.fields[0].value
    assert subs[0] not in subscriber.cache
    assert subs[1] in subscriber.cache
    assert subs[2] in subscriber.cache
Exemplo n.º 4
0
def setup_function():
    database = Database(BOT_TEST_DB_NAME)
    # Reset database to state before any tests ran.
    database.clear_table_data("subscriptions")
    # Use the test database by default, so we don't clutter the production one.
    subscriber.DEFAULT_DB_NAME = BOT_TEST_DB_NAME
    subscriber.cache = []
Exemplo n.º 5
0
 def setUp(self):
     Database.clear_db()
     self.mapper = UserMapper()
     self.male = self.mapper.create_user("123", "Max", "Kusnadi", "male")
     self.female = self.mapper.create_user("456", "Cindy", "Amelia",
                                           "female")
     self.other = self.mapper.create_user("789", "Bob", "Papatuli", "gay")
Exemplo n.º 6
0
def set_prefix(guild_id: int, prefix: str):
    """Sets the command prefix for this guild id to the given prefix.
    Instead deletes if prefix is None."""
    if prefix is None:
        Database(DEFAULT_DB_NAME).delete_prefix(guild_id)
    else:
        Database(DEFAULT_DB_NAME).insert_prefix(Prefix(guild_id, prefix))
    load()
Exemplo n.º 7
0
def setup_function():
    database = Database(SCRAPER_TEST_DB_NAME)
    # Reset database to state before any tests ran.
    database.clear_table_data("events")
    database.clear_table_data("discussions")
    database.clear_table_data("beatmapsets")
    database.clear_table_data("users")
    database.clear_table_data("beatmapset_modes")
Exemplo n.º 8
0
 def setUp(self):
     Database.clear_db()
     self.map = EventMapper()
     self.active_1 = self.map.create_event("galigo")
     self.active_2 = self.map.create_event("nuansa")
     self.inactive_1 = self.map.create_event("popi")
     self.inactive_2 = self.map.create_event("mkp")
     self.inactive_1 = self.map.set_event_inactive(self.inactive_1.id)
     self.inactive_2 = self.map.set_event_inactive(self.inactive_2.id)
Exemplo n.º 9
0
 def update_user_authorization(self, fb_id, value):
     u = User.query.filter(User.fb_id == fb_id).first()
     if u:
         u.set_authorized(True)
         Database.commit_db()
         logging.debug("Updating {} - {} {} to database".format(
             fb_id, u.first_name, u.last_name))
         return u
     else:
         raise ValueError("User not found", fb_id)
Exemplo n.º 10
0
def test_set_prefix_none():
    assert not prefixes.cache
    set_prefix(3, "&")
    assert Database(BOT_TEST_DB_NAME).retrieve_prefix("guild_id=%s",
                                                      (3, )) == Prefix(3, "&")
    assert prefixes.cache[3] == "&"
    set_prefix(3, None)
    assert Database(BOT_TEST_DB_NAME).retrieve_prefix("guild_id=%s",
                                                      (3, )) is None
    assert not prefixes.cache
Exemplo n.º 11
0
    def _initialize_models(self):
        """
        Set up our database connection and load up the model classes

        :return: None
        """

        self.db = Database(self.settings)
        self.db.run_migrations()
        for channel in self.settings.CHANNEL_LIST:
            self.channel_models[channel] = self.db.get_models(channel)
Exemplo n.º 12
0
 def set_event_active(self, event_id):
     try:
         event = self.get_event_by_id(event_id)
     except ValueError as err:
         logging.error(err)
         logging.error(err.args)
         raise ValueError(NOT_FOUND.format("Event"), event_id)
     else:
         event.set_active()
         Database.commit_db()
         return event
Exemplo n.º 13
0
 def create_event(self, title, description=None, link=None):
     try:
         if is_input_not_empty(title):
             e = Event(title, description=description, link=link)
     except ValueError as err:
         logging.error(err)
         logging.error(err.args)
         raise ValueError(UNABLE_TO_CREATE.format("Event"), title)
     else:
         Database.add_to_db(e)
         return e
Exemplo n.º 14
0
 def create_order(self, fb_id, description, quantity):
     try:
         user = self.userMapper.get_user_by_fb_id(fb_id)
     except ValueError as err:
         logging.error(err)
         logging.error(err.args)
         raise ValueError(UNABLE_TO_CREATE.format("Order"), fb_id)
     else:
         order = Order(user, description, quantity)
         order.set_time_auto()
         Database.add_to_db(order)
         return order
Exemplo n.º 15
0
    def update_order_status_by_order_id(self, order_id, status):
        try:
            order = self.get_order_by_order_id(order_id)
        except ValueError as err:
            logging.error(err)
            logging.error(err.args)
            raise err

        order.set_status(status)
        order.set_time_auto()
        Database.commit_db()
        return order
Exemplo n.º 16
0
def test_load():
    sub1 = Subscription(guild_id=1, channel_id=1, _filter="type:nominate")
    sub2 = Subscription(guild_id=1, channel_id=2, _filter="type:ranked")

    database = Database(BOT_TEST_DB_NAME)
    database.insert_subscription(sub1)
    database.insert_subscription(sub2)

    subscriber.load()

    assert sub1 in subscriber.cache
    assert sub2 in subscriber.cache
Exemplo n.º 17
0
def set_permission_filter(guild_id: int, command_wrapper: FunctionWrapper,
                          permission_filter: str) -> None:
    """Updates the permission filter in this guild for this command to the given value.
    If given `None`, the permission entry is deleted."""
    if permission_filter is None:
        Database(DEFAULT_DB_NAME).delete_permission(guild_id,
                                                    command_wrapper.names[0])
    else:
        Database(DEFAULT_DB_NAME).insert_permission(
            CommandPermission(guild_id, command_wrapper.names[0],
                              permission_filter))
    load()
Exemplo n.º 18
0
def bot_test_database():
    database = Database(BOT_TEST_DB_NAME)
    database.clear_table_data("subscriptions")
    database.clear_table_data("prefixes")
    database.clear_table_data("permissions")
    db_module.clear_cache(BOT_TEST_DB_NAME)
    return database
Exemplo n.º 19
0
    async def recent(self, ctx, filter: Option(
        str,
        "The first event matching this will be sent in the channel.",
        required=False,
        default=None)):
        """Returns the most recent event gathered, optionally matching `filter`."""

        if filter and not await validate_filter(ctx, filter, filter_context):
            return  # `validate_filter` will respond for us.

        await ctx.defer(ephemeral=False)

        matching_filter_str = f" matching `{filter}`" if filter else ""

        filter_query, filter_values = filter_to_sql(filter)
        database = Database(SCRAPER_DB_NAME)
        try:
            event = await database.retrieve_event(
                where=filter_query,
                where_values=filter_values,
                order_by="time DESC",
                extensive=True if filter else False)
        except TimeoutError:
            await ctx.followup.send(
                f"✗ Took too long to find an event{matching_filter_str}.")
            return

        if not event:
            await ctx.followup.send(
                f"✗ No event{matching_filter_str} could be found.")
            return

        await ctx.followup.send(
            f"✓ Most recent event{matching_filter_str}:\r\n{format_link(event)}",
            embed=await format_embed(event))
Exemplo n.º 20
0
def load() -> None:
    """Retrieves all subscriptions from the database and appends them to the internal list."""
    global cache
    cache = []

    for sub in Database(DEFAULT_DB_NAME).retrieve_subscriptions():
        cache.append(sub)
Exemplo n.º 21
0
 def update_event(self, event_id, title=None, description=None, link=None):
     try:
         event = self.get_event_by_id(event_id)
     except ValueError as err:
         logging.error(err)
         logging.error(err.args)
         raise ValueError(NOT_FOUND.format("Event"), event_id)
     else:
         if title:
             event.update_title(title)
         if description:
             event.update_description(description)
         if link:
             event.update_link(link)
         Database.commit_db()
         return event
Exemplo n.º 22
0
def retrieve_with_timeout(db_name,
                          table,
                          where="TRUE",
                          selection="*",
                          group_by: str = None,
                          order_by: str = None,
                          limit: int = None):
    try:
        db = Database(db_name)
        return db.retrieve_table_data(table=table,
                                      where=where,
                                      selection=selection,
                                      group_by=group_by,
                                      order_by=order_by,
                                      limit=limit)[0][0]
    except TimeoutError:
        return "(timed out)"
Exemplo n.º 23
0
def add_subscription(sub: Subscription) -> None:
    """Inserts a subscription into the subscription table of the database and reloads the cache.
    Causes any new events passing the filter to be sent to the channel."""
    if sub.guild_id is None:
        # Prevents excessive discord rate limiting (5 DMs per second globally).
        raise ValueError("Cannot subscribe in DM channels.")

    Database(DEFAULT_DB_NAME).insert_subscription(sub)
    load()
Exemplo n.º 24
0
    async def on_message(self, message):
        full_cmd = message.content.split()
        if not full_cmd:
            return
        cmd = full_cmd[0]
        if not cmd:
            return
        if not cmd[0] == settings["prefix"]:
            return
        cmd = cmd[len(settings["prefix"]):]  # skip past "!"

        # check cooldown for this channel.
        guild = message.guild
        if not guild:
            return
        channel = str(
            guild.id)  # this is a unique int representing this discord server.
        author = message.author
        is_mod = (author.guild_permissions.administrator
                  if author.guild_permissions else False)
        context = CommandContext(
            author=AuthorInfo(name=message.author.display_name, is_mod=is_mod),
            channel=channel,
            platform="discord",
        )
        db = Database.get()
        content = " ".join(full_cmd[1:] or [])
        if check_cooldown(db, context.channel):
            # Enforce cooldown for this channel.
            return
        try:
            resp = self.logic.exec(context, cmd, content)
            if resp:
                embed = discord.Embed(
                    title="LogicEFTBot",
                    url="https://eft.bot",
                    description="The Free Tarkov Bot",
                    color=0x780A81,
                )
                # embed.set_thumbnail(url="") #Will be implimented soon
                embed.add_field(name=cmd.capitalize() + " check",
                                value=resp,
                                inline=True)
                await message.channel.send(embed=embed)
                reset_cooldown(context.channel)
        except CommandNotFoundException:
            # just be silent if we don't know the command.
            pass
        except Exception as e:
            # Log all other exceptions.
            log.error(
                f"Exception processing command ({cmd}) for channel '{guild.name}' (id={context.channel}) -"
            )
            log.error(e)
            traceback.print_exc()
Exemplo n.º 25
0
    def _initialize_models(self):
        """
        Set up our database connection and load up the model classes

        :return: None
        """

        self.db = Database(self.settings)
        self.db.run_migrations()
        for channel in self.settings.CHANNEL_LIST:
            self.channel_models[channel] = self.db.get_models(channel)
Exemplo n.º 26
0
def load() -> None:
    """Loads the guild-specific command permissions from the database into the cache."""
    cache.clear()
    for perm_obj in Database(DEFAULT_DB_NAME).retrieve_permissions():
        if perm_obj.guild_id not in cache:
            cache[perm_obj.guild_id] = {
                perm_obj.command_name: perm_obj.permission_filter
            }
        else:
            cache[perm_obj.guild_id][
                perm_obj.command_name] = perm_obj.permission_filter
Exemplo n.º 27
0
    async def db_connect(self) -> None:
        """Estabolish connection with the database."""
        self.database = Database(config.DATABASE)
        connected = await self.database.connect()
        while not connected:
            logger.warning("Retrying to connect to database in 5s")
            # Synchronous sleep function to stop everything until db is connecting
            time.sleep(5)
            connected = await self.database.connect()

        await self.database.load_tables(self.db_table_list, self)
Exemplo n.º 28
0
    def setUp(self):
        Database.clear_db()
        self.processor = Processor()

        mapper = UserMapper()
        male1 = mapper.create_user("123", "M", "K", "male")
        male2 = mapper.create_user("123", "M", "K", "male")
        male3 = mapper.create_user("123", "M", "K", "male")

        female1 = mapper.create_user("123", "M", "K", "female")
        female2 = mapper.create_user("123", "M", "K", "female")
        female3 = mapper.create_user("123", "M", "K", "female")

        other1 = mapper.create_user("123", "M", "K", "gay")
        other2 = mapper.create_user("123", "M", "K", "gay")
        other3 = mapper.create_user("123", "M", "K", "gay")

        self.males = mapper.get_all_males()
        self.females = mapper.get_all_females()
        self.others = mapper.get_other_genders()
Exemplo n.º 29
0
def test_load():
    permissions.load()
    assert not permissions.cache

    Database(BOT_TEST_DB_NAME).insert_permission(
        CommandPermission(guild_id=3,
                          command_name="test1",
                          permission_filter="filter"))

    permissions.load()
    assert permissions.cache
    assert permissions.cache[3]["test1"] == "filter"
Exemplo n.º 30
0
def test_correct_setup():
    database = Database(SCRAPER_TEST_DB_NAME)
    assert not database.retrieve_table_data("events")
    assert not database.retrieve_table_data("discussions")
    assert not database.retrieve_table_data("beatmapsets")
    assert not database.retrieve_table_data("users")
    assert not database.retrieve_table_data("beatmapset_modes")
Exemplo n.º 31
0
async def test_recent():
    beatmapset = Beatmapset(1,
                            "artist",
                            "title",
                            creator=User(2, "sometwo"),
                            modes=["osu"])
    event1 = Event("nominate",
                   from_string("2020-01-01 00:00:00"),
                   beatmapset,
                   user=User(1, "someone"))
    event2 = Event("qualify",
                   from_string("2020-01-01 01:00:00"),
                   beatmapset,
                   user=User(4, "somefour"),
                   content="nicely done")

    database = Database(SCRAPER_TEST_DB_NAME)
    database.insert_event(event1)
    database.insert_event(event2)

    mock_message = MockMessage(
        channel=MockChannel(_id=6, guild=MockGuild(_id=2)))
    mock_command = MockCommand("recent",
                               "type:(nominate or qualify)",
                               context=mock_message)

    assert await receive_command(mock_command)

    assert mock_command.response.startswith("✓")
    assert "https://osu.ppy.sh/beatmapsets/1" in mock_command.response
    assert mock_command.response_embed
    assert mock_command.response_embed.fields
    assert mock_command.response_embed.fields[0].name.startswith(
        ":heart:\u2000Qualified (**")
    assert mock_command.response_embed.fields[0].name.endswith("** ago)")
    assert "artist - title" in mock_command.response_embed.fields[0].value
    assert "sometwo" in mock_command.response_embed.fields[0].value
    assert mock_command.response_embed.footer.text == "somefour \"nicely done\""
    assert mock_command.response_embed.footer.icon_url == "https://a.ppy.sh/4"
Exemplo n.º 32
0
class Bot(object):
    """A bot instance"""

    CONSOLE_BOT_PREFIX = " --- "

    def __init__(self, settings=None, ui_queues=None, wrapper=None,
                 irc_wrapper=None,
                 logger=None, wrap_irc=True):
        self.settings = settings
        self.wrapper = wrapper
        self.logger = logger
        self.ui_queues = ui_queues

        if irc_wrapper:
            iw = irc_wrapper(
                logger,
                self.wrapper,
                settings,
                settings.CHANNEL_LIST,
                settings.USER,
                settings.HOST,
                settings.OAUTH_TOKEN,
                settings.PORT,
                settings.COMMAND_PREFIX
            )

            if wrap_irc:
                self.ircWrapper = ThreadCallRelay()
                self.ircWrapper.set_call_object(iw)
                iw.set_call_relay(self.ircWrapper)
            else:
                self.ircWrapper = iw
        else:
            self.ircWrapper = None

        self.command_managers = {}
        self.blacklist_managers = {}
        self.channel_models = {}
        self.db = None

    #
    # Public API
    #

    def run(self):
        """
        Run the bot until we want to stop
        :return: None
        """

        self.logger.info(u"Starting bot...")

        self._initialize_models()

        self._initialize_command_managers()

        self._initialize_blacklists()

        self._send_initial_ui_data()

        self.logger.info(u"Starting IRC connection")
        self.ircWrapper.start()

        for channel in self.settings.CHANNEL_LIST:
            self.console(channel, "Bot started")

        # Run until we want to exit
        self.wrapper.loop()

        self._stop()

    def _stop(self):
        """
        Stop everything we're doing

        :return:
        """
        if self.ircWrapper:
            self.ircWrapper.stop()

        for key in self.command_managers:
            self.command_managers[key].stop_timers()


    def get_settings(self):
        """
        Get the bot settings, needed due to ThreadCallRelay

        :return:
        """

        return self.settings

    def get_irc(self):
        """
        Get the IRC wrapper object

        :return:
        """

        return self.ircWrapper

    def console(self, channel, message):
        self.ui_queues["in"].put(ConsoleMsg(channel, message))

    def bot_console(self, channel, message):
        self.ui_queues["in"].put(ConsoleMsg(
            channel,
            self.CONSOLE_BOT_PREFIX + " " + message
        ))

    def chat_message(self, channel, nick, text, timestamp):
        """
        Process a non-command line from the chat

        :param channel: The channel where the command was issued on
        :param nick: The nick of the user that issued the command
        :param text: The text content of the message
        :param timestamp: The unixtime for when the event happened
        :return:
        """

        self.console(channel, "<{0}> {1}".format(nick, text))

        user_level = self._get_user_level(channel, nick)
        if user_level not in ("mod", "owner"):
            mgr = self.blacklist_managers[channel]
            res, rule_id, ban_time = mgr.is_blacklisted(text)
            if res:
                self.logger.info(
                    u"{nick} will be timed out for {time} due to blacklist "
                    u"rule #{id}".format(
                        nick=nick,
                        time=human_readable_time(ban_time),
                        id=rule_id
                    )
                )
                self.timeout(channel, nick, ban_time)

                message = u"{nick}, you triggered blacklist rule #{id}, " \
                          u"you were timed out for {time}".format(
                    nick=nick,
                    id=rule_id,
                    time=human_readable_time(ban_time)
                )

                self._message(channel, message)


    def irc_command(self, channel, nick, command, args, timestamp):
        """
        Process a command from the chat

        :param channel: The channel where the command was issued on
        :param nick: The nick of the user that issued the command
        :param command: The command issued
        :param args: All the words on the line after the command
        :param timestamp: The unixtime for when the event happened
        :return: If this was a valid command that was executed
        """

        try:
            self.logger.debug(u"Got command {0} from {1} in {2}, with args: "
                              u"{3}".format(command, nick, channel,
                                            " ".join(args)))

            if not self._is_core_command(command):
                cm = self.command_managers[channel]
                if cm.is_valid_command(command):
                    self._handle_custom_command(
                        channel, nick, command, args, timestamp
                    )
                return False

            if not self._is_allowed_to_run_command(channel, nick, command):
                self.logger.info(u"Command access denied")
                message = u"{0}, sorry, but you are not allowed to use that " \
                          u"command."
                self._message(channel, message.format(nick))
                return False

            if command == u"addquote":
                self._add_quote(channel, nick, args)
            elif command == u"delquote":
                self._del_quote(channel, nick, args)
            elif command == u"quote":
                self._show_quote(channel, nick, args)
            elif command == u"reg":
                self._manage_regulars(channel, nick, args)
            elif command == u"def" or command == u"com":
                cm = self.command_managers[channel]

                if command == u"def":
                    added, channel, command, flags, user_level, code = \
                        cm.add_command(
                            args
                        )
                else:
                    added, channel, command, flags, user_level, code = \
                        cm.add_simple_command(
                            args
                        )

                if added:
                    message = u"{0}, added command {1} for user level " \
                              u"{2}".format(
                        nick, command, user_level
                    )
                else:
                    message = u"{0}, removed command {1}".format(
                        nick, command, user_level
                    )

                self.set_command(
                    channel, command, flags, user_level, code
                )

                self._message(channel, message)
            elif command == u"blacklist":
                message = self._add_to_blacklist(channel, nick, args)
                self._message(channel, message)
            elif command == u"whitelist":
                message = self._add_to_whitelist(channel, nick, args)
                self._message(channel, message)
            elif command == u"unblacklist":
                message = self._remove_from_blacklist(channel, nick, args)
                self._message(channel, message)
            elif command == u"unwhitelist":
                message = self._remove_from_whitelist(channel, nick, args)
                self._message(channel, message)

            return True

        except BaseException as e:
            message = u"{0}, {1} error: {2}"
            exception_text = str(e)
            exception_text = exception_text.replace(u"<", "")
            exception_text = exception_text.replace(u">", "")

            self._message(channel, message.format(
                nick, e.__class__.__name__, exception_text
            ))
            self.logger.error(u"I caught a booboo .. waah!", exc_info=True)

        return False

    def set_command(self, channel, command, flags, user_level, code):
        """
        Save a new custom command or update existing one in the database

        :param channel: The channel the command is for
        :param command: What is the command called
        :param flags: Command flags
        :param user_level: The minimum user level to run the command
        :param code: The Lua code for the custom command
        :return: None
        """

        self.bot_console(channel, "Updating command {0}".format(command))

        self._set_command(channel, command, flags, user_level, code)

    def update_global_value(self, channel, key, value):
        """
        Set a global persistent value on the channel

        :param channel: The channel the value is for
        :param key: The key for the value
        :param value: The value to store
        :return: None
        """

        self._update_channel_data(channel, key, value)

    def timeout(self, channel, nick, seconds):
        """
        Timeout the given user for the given amount of seconds
        :param channel:
        :param nick:
        :param seconds:
        :return:
        """

        message = ".timeout {nick} {seconds}".format(
            nick=nick, seconds=seconds
        )

        self.bot_console(channel, "Timing out {0}".format(nick))

        self._message(channel, message)

    def get_regulars(self, channel):
        model = self._get_model(channel, "regulars")
        regulars = list(model.select())

        result = []
        for regular in regulars:
            result.append(regular.nick)

        return result

    def send_regulars_to_ui(self, channel):
        q = self.ui_queues["in"]

        regulars = self.wrapper.get_regulars(channel)
        for regular in regulars:
            q.put(AddRegularToListMsg(regular))

    #
    # Internal API
    #

    def _message(self, channel, message):
        """
        Deliver a message to the channel

        :param channel: The channel the message is to be delivered on
        :param message: The message text
        :return: None
        """

        self.logger.debug(u"Sending message to {0}: {1}".format(
            channel, message
        ))

        self.bot_console(channel, "Saying: {0}".format(message))

        self.ircWrapper.message(channel, message)

    def _is_core_command(self, command):
        """
        Check if the given command is implemented in the "core" instead of
        e.g. being a custom one.

        :param command: The name of the command
        :return: True or False

        >>> from bot.bot import Bot
        >>> b = Bot()
        >>> b._is_core_command(u"def")
        True
        >>> b._is_core_command(u"get_fucked")
        False
        """

        return command in [
            u"addquote",
            u"delquote",
            u"blacklist",
            u"whitelist",
            u"unblacklist",
            u"unwhitelist",
            u"quote",
            u"reg",
            u"def",
            u"com"
        ]

    def _get_user_level(self, channel, nick):
        """
        Determine the nick's user level on the channel

        :param channel: Which channel
        :param nick: Whose user level
        :return: String "user", "reg", "mod", or "owner"
        """

        level = "user"

        if self._is_owner(nick):
            level = "owner"
        elif self._is_mod(channel, nick):
            level = "mod"
        elif self._is_regular(channel, nick):
            level = "reg"

        return level

    def _is_allowed_to_run_command(self, channel, nick, command):
        """
        Check if the given user has the permissions to run the given core
        command.

        :param channel: The channel the command was run on
        :param nick: Who is running the command
        :param command: The command being run
        :return: True or False
        """

        user_level = self._get_user_level(channel, nick)

        if user_level in ("mod", "owner"):
            # Mods and owners can run any and all core commands
            return True
        elif command in (u"addquote", u"delquote", u"quote"):
            if user_level == "reg":
                return True

        return False

    def _is_mod(self, channel, nick):
        """
        Check if the given nick is a moderator on the given channel

        :param channel: The name of the channel
        :param nick: The nick
        :return: True of False
        """

        return self.ircWrapper.is_oper(channel, nick)

    def _is_regular(self, channel, nick):
        """
        Check if the given nick is a regular on the given channel

        :param channel: The name of the channel
        :param nick: The nick
        :return: True of False
        """

        model = self._get_model(channel, "regulars")
        return model.filter(nick=nick).exists()

    def _is_owner(self, nick):
        """
        Check if the given nick belongs to a bot owner

        :param nick: The nick
        :return: True or False
        """

        return nick in self.settings.OWNER_USERS

    #
    # Chat commands
    #

    def _handle_custom_command(self, channel, nick, command, args, timestamp):
        """
        Handle execution of custom commands triggered via chat

        :param channel: The channel the command was triggered on
        :param nick: The nick that triggered it
        :param command: The command to be triggered
        :param args: The words on the line after the command
        :param timestamp: The unixtime for when the event happened
        :return: None
        """

        user_level = self._get_user_level(channel, nick)
        cm = self.command_managers[channel]

        message = None

        try:
            cm.run_command(nick, user_level, command, args, timestamp)
        except CommandPermissionError:
            message = u"{0}, you don't have permissions to run that " \
                      u"command".format(nick)
        except CommandCooldownError:
            self.logger.debug(u"Ignoring call to {0} due to cooldown".format(
                command
            ))
        except LuaError as e:
            message = u"{0}, oops, got Lua error: {1}".format(
                nick, str(e)
            )

        if message:
            self._message(channel, message)

    def _manage_regulars(self, channel, nick, args):
        """
        Handler for the "reg" -command, allows management of regulars

        :param channel: The channel the command was triggered on
        :param nick: The nick that triggered it
        :param args: The words on the line after the command
        :return: None
        """

        ok = True
        if len(args) != 2:
            ok = False

        action = args[0].lower()
        regular = args[1].lower()

        if not action in (u'add', u'del'):
            ok = False

        if not ok:
            self.logger.warn(u"Manage regulars got invalid args?")
            message = u"{0}, that doesn't look like a valid command?"
            self._message(channel, message.format(nick))

            return

        if action == u'add':
            if self._is_regular(channel, regular):
                self.logger.info(
                    u"Trying to add {0} to {1} regulars, but they were "
                    u"already one.".format(
                        regular, channel
                    )
                )
                message = u"{0}, {1} is already a regular?"
                self._message(channel, message.format(nick, regular))
                return

            self._add_regular(channel, regular)
            message = u"{0}, Added new regular: {1}"
            self._message(channel, message.format(nick, regular))
        elif action == u'del':
            if not self._is_regular(channel, regular):
                self.logger.info(
                    u"Trying to remove {0} from {1} regulars, but they "
                    u"weren't "
                    u"a regular there.".format(
                        regular, channel
                    )
                )
                message = u"{0}, {1} is not a regular?"
                self._message(channel, message.format(nick, regular))
                return

            self._remove_regular(channel, regular)
            message = u"{0}, Removed regular: {1}"
            self._message(channel, message.format(nick, regular))

    def _show_quote(self, channel, nick, args):
        """
        Handler for the "quote" -command, shows a quote on the channel

        :param channel: The channel the command was triggered on
        :param nick: The nick that triggered it
        :param args: The words on the line after the command
        :return: None
        """

        model = self._get_model(channel, u"quotes")
        quote_id, quote = model.get_random_quote()
        if quote:
            message = u"Quote #{0}: {1}".format(quote_id, quote)
            self._message(channel, message)

            self.logger.info(u"Showed quote for channel {0}: {1}".format(
                channel, quote
            ))
        else:
            message = u"No quotes in the database. Maybe you should add one?"
            self._message(channel, message)

            self.logger.info(u"No quotes for channel {0}".format(channel))

    def _add_quote(self, channel, nick, args, timestamp=None):
        """
        Handler for the "addquote" -command, adds a quote to the database

        :param channel: The channel the command was triggered on
        :param nick: The nick that triggered it
        :param args: The words on the line after the command
        :return: None
        """

        quote_text = " ".join(args)
        if len(quote_text) == 0:
            self.logger.info(u"Got 0 length addquote call from {0} in "
                             u"{1}?".format(nick, channel))

            message = u"{0}, ehh .. you gave me no quote?"
            self._message(channel, message.format(nick))
            return

        if not timestamp:
            timestamp = datetime.now()

        model = self._get_model(channel, "quotes")
        quote = model.create(
            quote=quote_text,
            year=int(timestamp.strftime("%Y")),
            month=int(timestamp.strftime("%m")),
            day=int(timestamp.strftime("%d"))
        )

        message = u"{0}, New quote added."
        self._message(channel, message.format(nick))

        self.logger.info(u"Added quote for {0}: {1}".format(channel, quote))
        self.bot_console(channel, "Added quote: {0}".format(quote))


    def _del_quote(self, channel, nick, args):
        """
        Handler for the "delquote" command, removes a quote from the database

        :param channel: The channel the command was triggered on
        :param nick: The nick that triggered it
        :param args: The words on the line after the command
        :return: None
        """

        if len(args) == 0:
            self.logger.info(u"Got 0 length delquote call from {0} in "
                             u"{1}?".format(nick, channel))

            message = u"{0}, ehh .. you gave me no quote ID?"
            self._message(channel, message.format(nick))
            return

        quote_id = args[0]

        model = self._get_model(channel, "quotes")
        quote = model.filter(id=quote_id).first()

        if quote:
            quote.delete_instance()
            message = u"{0}, Quote removed.".format(nick)
            self.logger.info(
                u"Removed quote {0} for {1}".format(quote_id, channel)
            )
            self.bot_console(channel, "Deleted quote: {0}".format(quote_id))
        else:
            message = u"{0}, no quote found with ID {1}".format(nick, quote_id)

        self._message(channel, message)

    #
    # Internal helper methods
    #

    def _set_command(self, channel, command, flags, user_level, code):
        """
        Save a command on the channel's database

        :param channel: Which channel
        :param command: What command
        :param flags: Command flags
        :param user_level: Minimum user level to access this command
        :param code: The Lua code for the command
        :return: None
        """

        model = self._get_model(channel, "commands")
        cmd = model.filter(command=command).first()

        if not cmd:
            cmd = model()
            cmd.command = command

        cmd.flags = json.dumps(flags)
        cmd.user_level = user_level
        cmd.code = code

        cmd.save()

        self.logger.info(u"Updated command {0} with user level {1}".format(
            command, user_level
        ))

    def _add_regular(self, channel, nick):
        """
        Add a regular to the channel

        :param channel: Which channel
        :param nick: The nick of the new regular
        :return: None
        """

        model = self._get_model(channel, "regulars")
        model.create(
            nick=nick
        )

        self.logger.info(u"Added regular {0} to {1}".format(nick, channel))
        self.bot_console(channel, "Added regular: {0}".format(nick))
        self.ui_queues["in"].put(AddRegularToListMsg(nick))

    def _remove_regular(self, channel, nick):
        """
        Remove a regular from the channel

        :param channel: Which channel
        :param nick: The nick of the old regular
        :return: None
        """

        model = self._get_model(channel, "regulars")
        regular = model.filter(nick=nick).first()

        if regular:
            regular.delete_instance()
            self.logger.info(u"Removed regular {0} from {1}".format(
                nick, channel
            ))

            self.bot_console(channel, "Removed regular: {0}".format(nick))
            self.ui_queues["in"].put(RemoveRegularFromListMsg(nick))

    def _add_to_blacklist(self, channel, nick, args):
        """
        Add an item to the blacklist
        :param channel:
        :param nick:
        :param args:
        :return:
        """

        parser = ArgumentParser()
        parser.add_argument("-b", "--banTime", default="10m")
        parser.add_argument("match", nargs='*')

        options = parser.parse_args(args)

        model = self._get_model(channel, "blacklist")

        rule = model()
        rule.match = " ".join(options.match)
        rule.banTime = options.banTime
        rule.save()

        self.blacklist_managers[channel].add_blacklist(rule)

        message = u"{nick}, added blacklist rule {match} with ID {id}".format(
            nick=nick, match=rule.match, id=rule.id
        )

        self.bot_console(channel, "Added to blacklist: {0}".format(rule.match))

        return message

    def _add_to_whitelist(self, channel, nick, args):
        """
        Add an item to the whitelist
        :param channel:
        :param nick:
        :param args:
        :return:
        """
        model = self._get_model(channel, "whitelist")

        rule = model()
        rule.match = " ".join(args)
        rule.save()

        self.blacklist_managers[channel].add_whitelist(rule)

        message = u"{nick}, added whitelist rule {match} with ID {id}".format(
            nick=nick, match=rule.match, id=rule.id
        )

        self.bot_console(channel, "Added to whitelist: {0}".format(rule.match))

        return message

    def _remove_from_blacklist(self, channel, nick, args):
        if len(args) == 0:
            self.logger.info(u"Got 0 length unblacklist call from {0} in "
                             u"{1}?".format(nick, channel))

            message = u"{0}, ehh .. you gave me no ID?"
            self._message(channel, message.format(nick))
            return

        row_id = args[0]

        model = self._get_model(channel, "blacklist")
        item = model.filter(id=row_id).first()

        if item:
            item.delete_instance()

            self.blacklist_managers[channel].remove_blacklist(row_id)

            message = u"{0}, blacklist item removed.".format(nick)
            self.bot_console(channel, "Removed from blacklist: {0}".format(
                row_id
            ))

            self.logger.info(u"Removed blacklist item {0} for {1}".format(
                row_id, channel
            ))
        else:
            message = u"{0}, no blacklist item found with ID {1}".format(
                nick, row_id
            )

        return message

    def _remove_from_whitelist(self, channel, nick, args):
        if len(args) == 0:
            self.logger.info(u"Got 0 length unwhitelist call from {0} in "
                             u"{1}?".format(nick, channel))

            message = u"{0}, ehh .. you gave me no ID?"
            self._message(channel, message.format(nick))
            return

        row_id = args[0]

        model = self._get_model(channel, "whitelist")
        item = model.filter(id=row_id).first()

        if item:
            item.delete_instance()

            self.blacklist_managers[channel].remove_whitelist(row_id)

            message = u"{0}, whitelist item removed.".format(nick)
            self.logger.info(u"Removed whitelist item {0} for {1}".format(
                row_id, channel
            ))

            self.bot_console(channel, "Removed from whitelist: {0}".format(
                row_id
            ))

        else:
            message = u"{0}, no whitelist item found with ID {1}".format(
                nick, row_id
            )

        return message

    def _update_channel_data(self, channel, key, value):
        """
        Save a single value to the channel's database

        :param channel: Which channel
        :param key: The name of the value
        :param value: The data to store
        :return: None
        """

        model = self._get_model(channel, "data")
        data = model.filter(key=key).first()

        if not data:
            data = model()
            data.key = key

        data.value = json.dumps(value)

        data.save()

    def _load_channel_data(self, channel):
        """
        Load all the channel's data values

        :param channel: Which channel
        :return: Python dict of all the stored values
        """

        model = self._get_model(channel, "data")
        entries = list(model.select())

        data = {}
        for entry in entries:
            data[entry.key] = json.loads(entry.value)

        return data

    def _initialize_command_managers(self):
        """
        Initialize all the command managers for all the channels, load our
        global Lua files in their Lua interpreters, and load the channel
        data and commands.

        :return: None
        """

        lua_files = self._find_lua_files()

        for channel in self.settings.CHANNEL_LIST:
            channel_data = self._load_channel_data(channel)
            cm = CommandManager(
                channel,
                self.wrapper,
                self.settings,
                channel_data,
                self.logger
            )

            for filename in lua_files:
                with open(filename, 'r') as handle:
                    code = handle.read()
                    self.logger.debug(u"Loading Lua for {0} from {1}".format(
                        channel, filename
                    ))
                    cm.load_lua(code)

            model = self._get_model(channel, "commands")
            commands = list(model.select())

            for command in commands:
                cm.load_command(
                    command.command,
                    json.loads(command.flags),
                    command.user_level,
                    command.code,
                    set=False
                )

            self.command_managers[channel] = cm

    def _initialize_blacklists(self):
        """
        Set up blacklist managers for all the channels
        :return:
        """

        for channel in self.settings.CHANNEL_LIST:
            manager = BlacklistManager(logger=self.logger)

            blacklist_model = self._get_model(channel, "blacklist")
            whitelist_model = self._get_model(channel, "whitelist")

            blacklist = list(blacklist_model.select())
            whitelist = list(whitelist_model.select())

            manager.set_data(blacklist, whitelist)

            self.blacklist_managers[channel] = manager

    def _find_lua_files(self):
        """
        Locate all Lua files we want to be globally included in our Lua runtime

        :return: Python list of the paths to the Lua files to be included
        """

        return glob(self.settings.LUA_INCLUDE_GLOB)

    def _initialize_models(self):
        """
        Set up our database connection and load up the model classes

        :return: None
        """

        self.db = Database(self.settings)
        self.db.run_migrations()
        for channel in self.settings.CHANNEL_LIST:
            self.channel_models[channel] = self.db.get_models(channel)

    def _get_model(self, channel, table):
        """
        Get the model instance for the given channel table

        :param channel: Which channel
        :param table: The name of the table, "regulars", "data", "commands",
                      or "quotes"
        :return: A peewee model for the table
        """

        return self.channel_models[channel][table]

    def _send_initial_ui_data(self):
        channels = []
        for channel in self.settings.CHANNEL_LIST:
            channels.append(channel)

        self.ui_queues["in"].put(SetChannelsMsg(channels))
Exemplo n.º 33
0
#!/usr/bin/env python
"""
Tool to extract all the quotes from the database.

Relies on settings.py to have the correct settings for database, and channels.
"""

from bot.database import Database
from argparse import ArgumentParser
import settings


if __name__ == "__main__":
    ap = ArgumentParser(description=__doc__)
    ap.add_argument(
        "channel", help="The channel to extract quotes from"
    )
    options = ap.parse_args()

    db = Database(settings)
    models = db.get_models(options.channel)

    for row in models["quotes"].select():
        print(row.quote)