You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

119 lines
4.1 KiB

import datetime
import re
from typing import cast, Optional
from edmond.plugin import Plugin
from edmond.plugins.shrlok import ShrlokPlugin
HTML_TEMPLATE = """\
<html>
<head> <meta charset="utf-8"> <title>🎶</title> </head>
<body> {} </body>
</html>
"""
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, …).
The current behaviour for the playlist is that links are added for a same day, so
the date at which the latest link was added is also stored. If a link is the first
to be added on a new day (by using this date as reference), the previous list is
emptied. But if the list is requested even if no links have been posted today and
the playlist is therefore outdated, it is still posted by the bot, along with a
message explaining that it's not the current day's playlist.
Note that the playlist itself is just of bunch of lines, which should of course
contain links, but can also contain titles and other metadata.
"""
REQUIRED_CONFIGS = ["commands"]
DATE_KEY = "date_of_latest_link"
PLAYLIST_KEY = "current_playlist"
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)
return True
def add_line(self, line: str) -> None:
"""Add this line to the current playlist."""
today = datetime.date.today()
last_update_str = self.get_storage_value(self.DATE_KEY)
if last_update_str:
last_update_date = datetime.date.fromisoformat(last_update_str)
else:
last_update_date = today
if last_update_date == today:
current_playlist: list[str] = self.get_storage_value(
self.PLAYLIST_KEY,
default=[],
)
current_playlist.append(line)
else:
current_playlist = [line]
self.set_storage_value(
self.PLAYLIST_KEY,
current_playlist,
skip_save=True, # save one write
)
self.set_storage_value(
self.DATE_KEY,
today.isoformat(),
)
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"<li>{item}</li>", linkified_items)
html_list = "<ol>" + "".join(html_items) + "</ol>"
data = HTML_TEMPLATE.format(html_list).encode()
url = self.shrlok_plugin.post({"type": "raw", "ext": "html"}, 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'<a href="\1">\1</a>', text)