Merge pull request #44 from strNophix/lastfm-scrobbler

Plugins + lastfm scrobbler
This commit is contained in:
2021-12-08 19:39:01 +01:00
committed by GitHub
16 changed files with 498 additions and 36 deletions
+53
View File
@@ -1,6 +1,16 @@
from abc import ABC
from abc import abstractmethod
from typing import Any
from typing import Optional
from typing import Protocol
from typing import TYPE_CHECKING
from typing import Union
if TYPE_CHECKING:
from tunebot.plugins import ServiceEvent
from discord.ext.commands import Cog
AnyDict = dict[Any, Any]
class GlobalPlaylistSource(ABC):
@@ -53,10 +63,53 @@ class AutoJoin(ABC):
pass
class ServiceBase(Protocol):
async def on_dispatch(self, event: "ServiceEvent", payload: AnyDict):
...
class PluginManagerBase(Protocol):
def get_plugin(self, name: str) -> Union["BasePluginInstance", None]:
...
def remove_plugin(self, name: str):
...
def enable_plugin(self, name: str, plugin: "BasePluginInstance"):
...
async def dispatch(self, event: "ServiceEvent", payload: AnyDict = {}):
...
class PluginLoaderBase(Protocol):
def load_plugin(self, plug_conf: AnyDict) -> "BasePluginInstance":
...
class GlobalUtils(Protocol):
async def raw_table_lookup(self, table_name: str, keys: list[Any]) -> list[Any]:
...
async def raw_table_del_entry(self, table_name: str, keys: list[Any]):
...
class BasePluginInstance(Protocol):
config: AnyDict
services: list["ServiceBase"]
cogs: list[str]
__all__ = (
"GlobalPlaylistSource",
"PlaylistSource",
"GlobalPlaylist",
"GlobalAutoJoin",
"AutoJoin",
"ServiceBase",
"PluginManagerBase",
"PluginLoaderBase",
"GlobalUtils",
"BasePluginInstance",
)
+6
View File
@@ -0,0 +1,6 @@
# noreorder
from tunebot.plugins.exceptions import *
from tunebot.plugins.events import *
from tunebot.plugins.plugin import *
from tunebot.plugins.loader import *
from tunebot.plugins.manager import *
+16
View File
@@ -0,0 +1,16 @@
from enum import auto
from enum import Enum
class ServiceEvent(Enum):
"""
This Enum contains all possible events that can be dispatched to services
Args:
Enum ([type]): [description]
"""
TRACK_ENDED = auto()
__all__ = ("ServiceEvent",)
+5
View File
@@ -0,0 +1,5 @@
class PluginInitFailed(Exception):
pass
__all__ = ("PluginInitFailed",)
+42
View File
@@ -0,0 +1,42 @@
import importlib
from typing import Any
from typing import TYPE_CHECKING
from tunebot.plugins import PluginInstance
from tunebot.plugins.exceptions import PluginInitFailed
if TYPE_CHECKING:
from tunebot import BasePluginInstance
from tunebot import ServiceBase
from bot import TuneBot
AnyDict = dict[Any, Any]
class FileSystemPluginLoader:
def __init__(self, bot: "TuneBot") -> None:
self.bot = bot
def load_plugin(self, plug_conf: AnyDict) -> "BasePluginInstance":
"""
Loads a plugin from configuration and returns a sequence of Services
Raises:
PluginInitFailed: [description]
Returns:
tuple[list["ServiceBase"], list[str]]: [description]
"""
services: list["ServiceBase"] = []
for service_location in plug_conf["services"]:
module = importlib.import_module(service_location)
if not hasattr(module, "setup"):
raise PluginInitFailed('Failed to find setup() for "{location}"')
services.append(module.setup(self.bot, plug_conf))
return PluginInstance(plug_conf["config"], services, plug_conf["cogs"])
__all__ = ("FileSystemPluginLoader",)
+79
View File
@@ -0,0 +1,79 @@
from typing import Any
from typing import TYPE_CHECKING
from typing import Union
from discord.ext.commands.errors import ExtensionAlreadyLoaded
from discord.ext.commands.errors import ExtensionFailed
from discord.ext.commands.errors import ExtensionNotFound
from discord.ext.commands.errors import NoEntryPointError
if TYPE_CHECKING:
from tunebot.abc import BasePluginInstance
from tunebot.plugins import ServiceEvent
from bot import TuneBot
AnyDict = dict[Any, Any]
class SimplePluginManager:
_plugins: dict[str, "BasePluginInstance"] = {}
def __init__(self, bot: "TuneBot") -> None:
self.bot = bot
def get_plugin(self, name: str) -> Union["BasePluginInstance", None]:
"""
Retrieves the corresponding `BasePluginInstance` if it exists
Returns:
Union["BasePluginInstance", None]: [description]
"""
return self._plugins.get(name)
def remove_plugin(self, name: str):
"""
Unloads/Removes all components related to the `BasePluginInstance`
Args:
name (str): [description]
"""
plugin = self.get_plugin(name)
if not plugin:
return
for cog in plugin.cogs:
try:
self.bot.unload_extension(cog)
except Exception:
pass
del self._plugins[name]
def enable_plugin(self, name: str, plugin: "BasePluginInstance"):
"""
Loads/Activates all cogs/services included within the plugin
Args:
plugin_name (str): [description]
services (list[): [description]
cog_names (list[str]): [description]
"""
self._plugins[name] = plugin
for cog_name in plugin.cogs:
self.bot.load_extension(cog_name)
async def dispatch(self, event: "ServiceEvent", payload: AnyDict = {}):
"""
Dispatches an event to all registered services
Args:
event (ServiceEvent): [description]
payload (AnyDict, optional): [description]. Defaults to {}.
"""
for plugin in self._plugins.values():
for service in plugin.services:
await service.on_dispatch(event, payload)
__all__ = ("SimplePluginManager",)
+18
View File
@@ -0,0 +1,18 @@
from dataclasses import dataclass
from typing import Any
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from tunebot import ServiceBase
AnyDict = dict[Any, Any]
@dataclass
class PluginInstance:
config: AnyDict
services: list["ServiceBase"]
cogs: list[str]
__all__ = ("PluginInstance",)
+3 -1
View File
@@ -1,4 +1,6 @@
from tunebot.redis.entity import * # noreorder
# noreorder
from tunebot.redis.entity import *
from tunebot.redis.utils import *
from tunebot.redis.autojoin import *
from tunebot.redis.playlist import *
from tunebot.redis.playlist_source import *
+18
View File
@@ -0,0 +1,18 @@
from typing import Any
from tunebot.redis import RedisBotEntity
class GlobalRedisUtils(RedisBotEntity):
async def raw_table_lookup(self, table_name: str, keys: list[Any]) -> list[Any]:
if len(keys) == 0:
return []
result: list[Any] = await self.redis.hmget(table_name, keys)
return result
async def raw_table_del_entry(self, table_name: str, keys: list[Any]):
if len(keys) == 0:
return
await self.redis.hdel(table_name, *keys)