diff --git a/config.json.example b/config.json.example index fcd7588..f72e903 100644 --- a/config.json.example +++ b/config.json.example @@ -148,7 +148,8 @@ "negative": ["I don't like it."] }, "playlistoftheday": { - "commands": ["playlist of the day"] + "commands": ["playlist of the day"], + "not_fresh_reply": "It's not from today but here it is: {url}" }, "plus": { "commands": ["plus"], diff --git a/edmond/bot.py b/edmond/bot.py index 9255b54..907aa40 100644 --- a/edmond/bot.py +++ b/edmond/bot.py @@ -162,7 +162,10 @@ class Bot(irc.client.SimpleIRCClient, Logger): def get_plugin(self, name: str) -> Optional[Plugin]: """Get a loaded plugin by its name (e.g. 'mood'), or None.""" - matching_plugins = filter(lambda plugin: plugin.name == name, self.plugins) + matching_plugins = filter( + lambda plugin: plugin.name == name, + self.plugins, + ) return next(matching_plugins, None) def say(self, target: str, message: str) -> None: diff --git a/edmond/plugins/playlist_of_the_day.py b/edmond/plugins/playlist_of_the_day.py index 202f972..1fc009b 100644 --- a/edmond/plugins/playlist_of_the_day.py +++ b/edmond/plugins/playlist_of_the_day.py @@ -1,11 +1,25 @@ import datetime +import re +from typing import cast, Optional from edmond.plugin import Plugin +from edmond.plugins.shrlok import ShrlokPlugin + + +HTML_TEMPLATE = """\ + + 🎶 + {} + +""" +LINK_RE = re.compile(r"(https?://\S+)") class PlaylistOfTheDayPlugin(Plugin): """Collect music links from other platforms. + This plugin requires a working Shrlok instance to post the playlist. + Plugins that want to feed the playlist must do so by calling the add_link method of the plugin. Later this plugin may feed its playlist using links unhandled by other, more specialized plugins (bandcamp, soundcloud, …). @@ -27,12 +41,26 @@ class PlaylistOfTheDayPlugin(Plugin): def __init__(self, bot): super().__init__(bot) + self._shrlok_plugin = None + + @property + def shrlok_plugin(self) -> Optional[ShrlokPlugin]: + if self._shrlok_plugin is None: + self._shrlok_plugin = cast( + ShrlokPlugin, + self.bot.get_plugin("shrlok"), + ) + return self._shrlok_plugin + + def on_welcome(self, _): + if not (self.shrlok_plugin and self.shrlok_plugin.is_ready): + self.bot.log_w("Shrlok plugin is not available.") + self.is_ready = False def on_pubmsg(self, event): if not self.should_handle_command(event.arguments[0], no_content=True): return False - - # self.post_playlist(event.target) + self.post_playlist(event.target) return True def add_line(self, line: str) -> None: @@ -62,5 +90,29 @@ class PlaylistOfTheDayPlugin(Plugin): today.isoformat(), ) - # def post_playlist(self): - # pass + def post_playlist(self, target): + playlist: list[str] = self.get_storage_value(self.PLAYLIST_KEY, []) + if not playlist: + self.bot.log_e("Playlist empty.") + self.signal_failure(target) + + linkified_items = map(PlaylistOfTheDayPlugin.linkify, playlist) + html_items = map(lambda item: f"
  • {item}
  • ", linkified_items) + html_list = "
      " + "".join(html_items) + "
    " + data = HTML_TEMPLATE.format(html_list).encode() + url = self.shrlok_plugin.post({"type": "raw"}, data) + if not url: + self.bot.log_e("Shrlok returned None.") + self.signal_failure(target) + + date = self.get_storage_value(self.DATE_KEY, "") + if date != datetime.date.today().isoformat(): + reply = self.config["not_fresh_reply"].format(url=url) + else: + reply = url + self.bot.say(target, reply) + + @staticmethod + def linkify(text: str) -> str: + """Put links in A tags.""" + return LINK_RE.sub(r'\1', text) diff --git a/edmond/plugins/shrlok.py b/edmond/plugins/shrlok.py index 381e769..3751dbe 100644 --- a/edmond/plugins/shrlok.py +++ b/edmond/plugins/shrlok.py @@ -1,5 +1,6 @@ import json import socket +from typing import Optional from edmond.bot import Bot from edmond.plugin import Plugin @@ -31,7 +32,7 @@ class ShrlokPlugin(Plugin): self.bot.log_d("No socket path specified, shrlok plugin disabled.") self.is_ready = False - def post(self, header: dict, data: bytes): + def post(self, header: dict, data: bytes) -> Optional[str]: encoded_header = json.dumps(header).encode() try: with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock: @@ -45,4 +46,4 @@ class ShrlokPlugin(Plugin): self.bot.log_e(f"Can't post data: {exc}") return None url = response.decode().replace(self.file_root, self.url_root, 1) - return url + return url or None # returning empty strings could cause confusion