diff --git a/src/cpl_discord/container/__init__.py b/src/cpl_discord/container/__init__.py new file mode 100644 index 00000000..425ab6c1 --- /dev/null +++ b/src/cpl_discord/container/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/src/cpl_discord/container/category_channel.py b/src/cpl_discord/container/category_channel.py new file mode 100644 index 00000000..3c9ad3cf --- /dev/null +++ b/src/cpl_discord/container/category_channel.py @@ -0,0 +1,11 @@ +import discord + +from cpl_discord.container.container import Container +from cpl_discord.helper.ToContainersConverter import ToContainersConverter +from cpl_query.extension import List + + +class CategoryChannel(discord.CategoryChannel, Container): + + def __init__(self, _t: discord.CategoryChannel): + Container.__init__(self, _t, CategoryChannel) diff --git a/src/cpl_discord/container/container.py b/src/cpl_discord/container/container.py new file mode 100644 index 00000000..a32479d6 --- /dev/null +++ b/src/cpl_discord/container/container.py @@ -0,0 +1,30 @@ +from typing import Callable + + +class Container: + + def __init__(self, _o: object, _t: type): + self._object = _o + self._type = _t + + def __to_type(_f: Callable, _t: type): + def wrapper(*args, **kwargs): + result = _f(*args, **kwargs) + return _t(result) + + return wrapper + + def __getitem__(self, item): + result = self._object[item] + if isinstance(result, type(self._guild)): + result = self._type(result) + return result + + def __getattr__(self, item): + result = getattr(self._object, item) + if callable(result): + result = self.__to_type(result, self._type) + return result + + def __repr__(self): + return repr(self._object) diff --git a/src/cpl_discord/container/guild.py b/src/cpl_discord/container/guild.py new file mode 100644 index 00000000..b69af4ed --- /dev/null +++ b/src/cpl_discord/container/guild.py @@ -0,0 +1,42 @@ +import discord + +from cpl_discord.container.category_channel import CategoryChannel +from cpl_discord.container.container import Container +from cpl_discord.container.member import Member +from cpl_discord.container.role import Role +from cpl_discord.container.text_channel import TextChannel +from cpl_discord.container.voice_channel import VoiceChannel +from cpl_discord.helper.ToContainersConverter import ToContainersConverter +from cpl_query.extension import List + + +class Guild(discord.Guild, Container): + + def __init__(self, _t: discord.Guild): + self._object: discord.Guild = _t + + Container.__init__(self, _t, Guild) + + @property + def categories(self) -> List[CategoryChannel]: + return List(CategoryChannel, ToContainersConverter.convert(self._object.categories, CategoryChannel)) + + @property + def members(self) -> List[Member]: + return List(Member, ToContainersConverter.convert(self._object.members, Member)) + + @property + def roles(self) -> List[Role]: + return List(Role, ToContainersConverter.convert(self._object.roles, Role)) + + @property + def text_channels(self) -> List[TextChannel]: + return List(TextChannel, ToContainersConverter.convert(self._object.text_channels, TextChannel)) + + @property + def threads(self) -> List[TextChannel]: + return List(TextChannel, ToContainersConverter.convert(self._object.threads, TextChannel)) + + @property + def voice_channels(self) -> List[VoiceChannel]: + return List(VoiceChannel, ToContainersConverter.convert(self._object.voice_channels, VoiceChannel)) diff --git a/src/cpl_discord/container/member.py b/src/cpl_discord/container/member.py new file mode 100644 index 00000000..0fe775f3 --- /dev/null +++ b/src/cpl_discord/container/member.py @@ -0,0 +1,11 @@ +import discord + +from cpl_discord.container.container import Container +from cpl_discord.helper.ToContainersConverter import ToContainersConverter +from cpl_query.extension import List + + +class Member(discord.Member, Container): + + def __init__(self, _t: discord.Member): + Container.__init__(self, _t, Member) diff --git a/src/cpl_discord/container/role.py b/src/cpl_discord/container/role.py new file mode 100644 index 00000000..27251ef0 --- /dev/null +++ b/src/cpl_discord/container/role.py @@ -0,0 +1,16 @@ +import discord + +from cpl_discord.container.container import Container +from cpl_discord.container.member import Member +from cpl_discord.helper.ToContainersConverter import ToContainersConverter +from cpl_query.extension import List + + +class Role(discord.Role, Container): + + def __init__(self, _t: discord.Role): + Container.__init__(self, _t, Role) + + @property + def members(self) -> List[discord.Member]: + return List(discord.Member, ToContainersConverter.convert(self._object.members, Member)) diff --git a/src/cpl_discord/container/text_channel.py b/src/cpl_discord/container/text_channel.py new file mode 100644 index 00000000..1051a3a5 --- /dev/null +++ b/src/cpl_discord/container/text_channel.py @@ -0,0 +1,11 @@ +import discord + +from cpl_discord.container.container import Container +from cpl_discord.helper.ToContainersConverter import ToContainersConverter +from cpl_query.extension import List + + +class TextChannel(discord.TextChannel, Container): + + def __init__(self, _t: discord.TextChannel): + Container.__init__(self, _t, TextChannel) diff --git a/src/cpl_discord/container/voice_channel.py b/src/cpl_discord/container/voice_channel.py new file mode 100644 index 00000000..ccc5ac4f --- /dev/null +++ b/src/cpl_discord/container/voice_channel.py @@ -0,0 +1,11 @@ +import discord + +from cpl_discord.container.container import Container +from cpl_discord.helper.ToContainersConverter import ToContainersConverter +from cpl_query.extension import List + + +class VoiceChannel(discord.VoiceChannel, Container): + + def __init__(self, _t: discord.VoiceChannel): + Container.__init__(self, _t, VoiceChannel) diff --git a/src/cpl_discord/helper/ToContainersConverter.py b/src/cpl_discord/helper/ToContainersConverter.py new file mode 100644 index 00000000..70d8bfec --- /dev/null +++ b/src/cpl_discord/helper/ToContainersConverter.py @@ -0,0 +1,13 @@ +from typing import Union, Sequence + +from cpl_discord.container.container import Container + + +class ToContainersConverter: + + @staticmethod + def convert(_l: Union[list[object], Sequence[object]], _t: type) -> list[Container]: + values = [] + for e in _l: + values.append(_t(e)) + return values diff --git a/src/cpl_discord/helper/__init__.py b/src/cpl_discord/helper/__init__.py new file mode 100644 index 00000000..425ab6c1 --- /dev/null +++ b/src/cpl_discord/helper/__init__.py @@ -0,0 +1 @@ +# imports diff --git a/src/cpl_discord/service/discord_bot_service.py b/src/cpl_discord/service/discord_bot_service.py index 07682187..e2928c5e 100644 --- a/src/cpl_discord/service/discord_bot_service.py +++ b/src/cpl_discord/service/discord_bot_service.py @@ -5,8 +5,11 @@ from cpl_core.console import Console from cpl_core.environment import ApplicationEnvironmentABC from cpl_core.logging import LoggerABC, LoggingSettings, LoggingLevelEnum from cpl_discord.configuration.discord_bot_settings import DiscordBotSettings +from cpl_discord.container.guild import Guild +from cpl_discord.helper.ToContainersConverter import ToContainersConverter from cpl_discord.service.discord_bot_service_abc import DiscordBotServiceABC from cpl_discord.service.discord_service_abc import DiscordServiceABC +from cpl_query.extension.list import List class DiscordBotService(DiscordBotServiceABC): @@ -32,6 +35,7 @@ class DiscordBotService(DiscordBotServiceABC): # setup super DiscordBotServiceABC.__init__(self, command_prefix=self._discord_settings.prefix, help_command=None, intents=discord.Intents().all()) + self._base = super(DiscordBotServiceABC, self) @staticmethod def _is_string_invalid(x): @@ -77,3 +81,7 @@ class DiscordBotService(DiscordBotServiceABC): self._logger.debug(__name__, f'Finished syncing commands') await self._discord_service.on_ready() + + @property + def guilds(self) -> List[Guild]: + return List(Guild, ToContainersConverter.convert(self._base.guilds, Guild)) diff --git a/src/cpl_discord/service/discord_bot_service_abc.py b/src/cpl_discord/service/discord_bot_service_abc.py index 1f54f21e..b96a5ee8 100644 --- a/src/cpl_discord/service/discord_bot_service_abc.py +++ b/src/cpl_discord/service/discord_bot_service_abc.py @@ -1,8 +1,10 @@ -from abc import ABC, abstractmethod +from abc import abstractmethod -import discord from discord.ext import commands +from cpl_discord.container.guild import Guild +from cpl_query.extension.list import List + class DiscordBotServiceABC(commands.Bot): @@ -17,3 +19,7 @@ class DiscordBotServiceABC(commands.Bot): @abstractmethod async def on_ready(self): pass + + @property + @abstractmethod + def guilds(self) -> List[Guild]: pass diff --git a/src/cpl_query/_query/select.py b/src/cpl_query/_query/select.py new file mode 100644 index 00000000..0fb07ad2 --- /dev/null +++ b/src/cpl_query/_query/select.py @@ -0,0 +1,19 @@ +from typing import Callable + +from cpl_query.extension.iterable_abc import IterableABC + + +def select_query(_list: IterableABC, _f: Callable) -> any: + result = IterableABC() + result.extend(_f(_o) for _o in _list) + return result + + +def select_many_query(_list: IterableABC, _f: Callable) -> any: + result = IterableABC() + # The line below is pain. I don't understand anything of it... + # written on 09.11.2022 by Sven Heidemann + elements = [_a for _o in _list for _a in _f(_o)] + + result.extend(elements) + return result diff --git a/src/cpl_query/extension/iterable.py b/src/cpl_query/extension/iterable.py index ff835382..5c98fde7 100644 --- a/src/cpl_query/extension/iterable.py +++ b/src/cpl_query/extension/iterable.py @@ -14,6 +14,7 @@ from cpl_query._query.for_each import for_each_query from cpl_query._query.max_min import max_query, min_query from cpl_query._query.order_by import order_by_descending_query, order_by_query from cpl_query._query.reverse import reverse_query +from cpl_query._query.select import select_query, select_many_query from cpl_query._query.single import single_or_default_query, single_query from cpl_query._query.skip_take import (skip_last_query, skip_query, take_last_query, take_query) @@ -94,6 +95,12 @@ class Iterable(IterableABC): def single_or_default(self) -> Optional[any]: return single_or_default_query(self) + def select(self, _f: Callable) -> IterableABC: + return self.__to_self(select_query(self, _f)) + + def select_many(self, _f: Callable) -> IterableABC: + return self.__to_self(select_many_query(self, _f)) + def skip(self, index: int) -> IterableABC: return self.__to_self(skip_query(self, index)) diff --git a/src/tests/custom/discord/src/modules/hello_world/on_ready_event.py b/src/tests/custom/discord/src/modules/hello_world/on_ready_event.py index 306ccf98..10d3453c 100644 --- a/src/tests/custom/discord/src/modules/hello_world/on_ready_event.py +++ b/src/tests/custom/discord/src/modules/hello_world/on_ready_event.py @@ -1,12 +1,33 @@ +import discord + from cpl_core.logging import LoggerABC from cpl_discord.events.on_ready_abc import OnReadyABC +from cpl_discord.service.discord_bot_service_abc import DiscordBotServiceABC class OnReadyEvent(OnReadyABC): - def __init__(self, logger: LoggerABC): + def __init__(self, logger: LoggerABC, bot: DiscordBotServiceABC): OnReadyABC.__init__(self) self._logger = logger + self._bot = bot + + def _log(self, _t: str, _o: object, _type: type = None): + self._logger.debug(__name__, f'{_t} {_o} {_type}') async def on_ready(self): self._logger.info(__name__, 'Hello World') + for g in self._bot.guilds: + self._log('-Guild', g, type(g)) + for r in g.roles: + self._log('--Role', r, type(r)) + for rm in r.members: + self._log('---Rolemembers', rm, type(rm)) + + for m in g.members: + self._log('--Member', m, type(m)) + + select = self._bot.guilds.select(lambda guild: (guild.name, guild.id)) + self._logger.warn(__name__, f'Does cpl.query select work? {select}') + select_many = self._bot.guilds.select_many(lambda guild: guild.roles).where(lambda role: role.name == "Tester").first() + self._logger.warn(__name__, f'Does cpl.query select_many work? {select_many}')