Repository: sopel-irc/sopel

Issue:
Allow changing the URL blacklist from IRC
### The problem
You have to restart the bot and modify its config file to change the blacklist.

### The solution
We could make the URL blacklist available from IRC and editable.

https://github.com/Pix1234/ZppixBot-Source/blob/master/modules/urls.py does this and could be inspiration

Edit: in particular, this bit here https://github.com/Pix1234/ZppixBot-Source/blob/master/modules/urls.py#L242-L289


Discussion:
1) That looks like it's based on a pre-7.0 version of `url.py`. It needs updating. No need to handle URL callbacks in your `urls.py` in Sopel 7.

2) Since it sounds like you want to upstream a feature you've already implemented, why not submit a PR to do that?
> * That looks like it's based on a pre-7.0 version of `url.py`. It needs updating. No need to handle URL callbacks in your `urls.py` in Sopel 7.

Yes we plan to once I migrate all the custom hacks off it.

> * Since it sounds like you want to upstream a feature you've already implemented, why not submit a PR to do that?

I have asked the person that built the code to.

I'll do that for 7.1.
We just scrapped our hack as it was refusing to co-operate and decided to not use it until 7.1 but here's the code from it that I hold no resposibility for nor guarntee the usefulness of it if breaks anything.

```
@commands('url')
@example('.url ban http://google.com')
@require_admin
def url_command(bot, trigger):
    """Allow a bot admin to modify the url blacklist. Accepts `ban`, `allow` and `match` as parameters."""
    MESSAGE = {
        'success_ban': "Successfully added ban: %s",
        'success_allow': "Successfully removed ban on: %s",
        'invalid_ban': "Invalid ban format, %s must be valid regex",
        'no_exist': "Could not find %s",
        'results': "Found %s: %s",
        'nope': "I don't know how to do what you're asking!",
    }

    blacklist = set(s for s in bot.config.url.exclude if s != '')
    text = trigger.group().split()

    if len(text) == 3 and text[1] == 'match':
        for entry in blacklist:
            r_entry = re.compile(entry)
            if text[2] in entry or r_entry.search(text[2]):
                bot.reply(MESSAGE['results'] % (text[2], entry))
                return
        bot.reply(MESSAGE['no_exist'] % text[2])

    elif len(text) == 3 and text[1] == 'ban':
        try:
            re.compile(text[2])
            blacklist.add(text[2])
            bot.config.url.exclude = blacklist
            bot.config.save()
            bot.memory['url_exclude'] = [re.compile(s) for s in blacklist]
            bot.reply(MESSAGE['success_ban'] % text[2])
        except Exception:
            bot.reply(MESSAGE['invalid_ban'] % text[2])

    elif len(text) == 3 and text[1] == 'allow':
        if text[2] not in blacklist:
            bot.reply(MESSAGE['no_exist'] % text[2])
            return
        blacklist.remove(text[2])
        bot.config.url.exclude = blacklist
        bot.config.save()
        bot.memory['url_exclude'] = [re.compile(s) for s in blacklist]
        bot.reply(MESSAGE['success_allow'] % text[2])

    else:
        bot.reply(MESSAGE['nope'])

```

Resolving patch:
diff --git a/sopel/modules/url.py b/sopel/modules/url.py
--- a/sopel/modules/url.py
+++ b/sopel/modules/url.py
@@ -153,6 +153,96 @@ def shutdown(bot):
             pass
 
 
+@plugin.command('urlexclude', 'urlpexclude', 'urlban', 'urlpban')
+@plugin.example('.urlpexclude example\\.com/\\w+', user_help=True)
+@plugin.example('.urlexclude example.com/path', user_help=True)
+@plugin.output_prefix('[url] ')
+def url_ban(bot, trigger):
+    """Exclude a URL from auto title.
+
+    Use ``urlpexclude`` to exclude a pattern instead of a URL.
+    """
+    url = trigger.group(2)
+
+    if not url:
+        bot.reply('This command requires a URL to exclude.')
+        return
+
+    if trigger.group(1) in ['urlpexclude', 'urlpban']:
+        # validate regex pattern
+        try:
+            re.compile(url)
+        except re.error as err:
+            bot.reply('Invalid regex pattern: %s' % err)
+            return
+    else:
+        # escape the URL to ensure a valid pattern
+        url = re.escape(url)
+
+    patterns = bot.settings.url.exclude
+
+    if url in patterns:
+        bot.reply('This URL is already excluded from auto title.')
+        return
+
+    # update settings
+    patterns.append(url)
+    bot.settings.url.exclude = patterns  # set the config option
+    bot.settings.save()
+    LOGGER.info('%s excluded the URL pattern "%s"', trigger.nick, url)
+
+    # re-compile
+    bot.memory['url_exclude'] = [re.compile(s) for s in patterns]
+
+    # tell the user
+    bot.reply('This URL is now excluded from auto title.')
+
+
+@plugin.command('urlallow', 'urlpallow', 'urlunban', 'urlpunban')
+@plugin.example('.urlpallow example\\.com/\\w+', user_help=True)
+@plugin.example('.urlallow example.com/path', user_help=True)
+@plugin.output_prefix('[url] ')
+def url_unban(bot, trigger):
+    """Allow a URL for auto title.
+
+    Use ``urlpallow`` to allow a pattern instead of a URL.
+    """
+    url = trigger.group(2)
+
+    if not url:
+        bot.reply('This command requires a URL to allow.')
+        return
+
+    if trigger.group(1) in ['urlpallow', 'urlpunban']:
+        # validate regex pattern
+        try:
+            re.compile(url)
+        except re.error as err:
+            bot.reply('Invalid regex pattern: %s' % err)
+            return
+    else:
+        # escape the URL to ensure a valid pattern
+        url = re.escape(url)
+
+    patterns = bot.settings.url.exclude
+
+    if url not in patterns:
+        bot.reply('This URL was not excluded from auto title.')
+        return
+
+    # update settings
+    patterns.remove(url)
+    bot.settings.url.exclude = patterns  # set the config option
+    bot.settings.save()
+    LOGGER.info('%s allowed the URL pattern "%s"', trigger.nick, url)
+
+    # re-compile
+    bot.memory['url_exclude'] = [re.compile(s) for s in patterns]
+
+    # tell the user
+    bot.reply('This URL is not excluded from auto title anymore.')
+
+
 @plugin.command('title')
 @plugin.example(
     '.title https://www.google.com',
@@ -198,8 +288,8 @@ def title_auto(bot, trigger):
     if not bot.settings.url.enable_auto_title:
         return
 
-    # Avoid fetching links from the "title" command
-    if re.match(bot.config.core.prefix + 'title', trigger):
+    # Avoid fetching links from another command
+    if re.match(bot.config.core.prefix + r'\S+', trigger):
         return
 
     # Avoid fetching known malicious links


Test patch: