46 | {% if success %}
47 |
Transaction was Successful!
48 |

49 |
Please return to the Discord App.
50 | {% else %}
51 |
An Error occurred during the Transaction.
52 |

53 |
Please try again later or contact support.
54 | {% endif %}
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/src/sockets/__init__.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import typing
4 |
5 | if typing.TYPE_CHECKING:
6 | from core import Quotient
7 |
8 | from socketio import AsyncClient
9 |
10 | from core import Cog
11 |
12 | from .app import sio
13 | from .events import (DashboardGate, SocketScrims, SockGuild, SockPrime,
14 | SockSettings)
15 |
16 |
17 | class SocketConnection(Cog):
18 | connected: bool = False
19 | sio: AsyncClient
20 |
21 | def __init__(self, bot: Quotient):
22 | self.bot = bot
23 | self.task = self.bot.loop.create_task(self.__make_connection())
24 |
25 | def cog_unload(self) -> None:
26 | self.bot.loop.create_task(self.__close_connection())
27 |
28 | async def __make_connection(self):
29 | await sio.connect(self.bot.config.SOCKET_URL, auth={"token": self.bot.config.SOCKET_AUTH})
30 |
31 | sio.bot, self.bot.sio = self.bot, sio
32 | self.connected = True
33 |
34 | async def __close_connection(self):
35 | if self.connected:
36 | await sio.disconnect()
37 | self.connected = False
38 |
39 |
40 | async def setup(bot: Quotient):
41 | await bot.add_cog(SocketConnection(bot))
42 | await bot.add_cog(DashboardGate(bot))
43 | await bot.add_cog(SocketScrims(bot))
44 | await bot.add_cog(SockSettings(bot))
45 | await bot.add_cog(SockPrime(bot))
46 | await bot.add_cog(SockGuild(bot))
47 |
--------------------------------------------------------------------------------
/src/sockets/app/__init__.py:
--------------------------------------------------------------------------------
1 | from .app import sio
2 |
--------------------------------------------------------------------------------
/src/sockets/app/app.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import typing
4 |
5 | if typing.TYPE_CHECKING:
6 | from core import Quotient
7 |
8 | import socketio
9 |
10 |
11 | class QuoSocket(socketio.AsyncClient):
12 | bot: Quotient
13 |
14 | def __init__(self, **kwargs):
15 | super().__init__(**kwargs)
16 |
17 | async def emit(self, event, data=None, namespace=None, callback=None):
18 | return await super().emit(
19 | "response__" + event, data=data, namespace=namespace, callback=callback
20 | )
21 |
22 | async def request(self, event, data=None, namespace=None, callback=None):
23 | return await super().emit(event, data=data, namespace=namespace, callback=callback)
24 |
25 | @staticmethod
26 | def int_parse(data):
27 | if not isinstance(data, dict):
28 | return data
29 |
30 | for x, y in data.items():
31 | if isinstance(y, str) and y.isdigit():
32 | data[x] = int(y)
33 |
34 | return data
35 |
36 |
37 | sio = QuoSocket(logger=True, engineio_logger=True)
38 | ignored = ("update_total_votes", "update_votes_leaderboard")
39 |
40 |
41 | @sio.on("*")
42 | async def catch_all(event, data):
43 | if event in ignored:
44 | return
45 |
46 | data = QuoSocket.int_parse(data)
47 |
48 | r, e, u = event.split("__")
49 | data["user__id"] = u
50 | sio.bot.dispatch(r + "__" + e, u, data)
51 |
--------------------------------------------------------------------------------
/src/sockets/events/__init__.py:
--------------------------------------------------------------------------------
1 | from .dashgate import * # noqa: F401, F403
2 | from .scrims import * # noqa: F401, F403
3 | from .settings import * # noqa: F401, F403
4 | from .premium import * # noqa: F401, F403
5 | from .guilds import * # noqa: F401, F403
6 |
--------------------------------------------------------------------------------
/src/sockets/events/dashgate.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING
4 |
5 | if TYPE_CHECKING:
6 | from core import Quotient
7 |
8 | from core import Cog
9 | from models import Guild
10 |
11 | __all__ = ("DashboardGate",)
12 |
13 |
14 | class DashboardGate(Cog):
15 | def __init__(self, bot: Quotient):
16 | self.bot = bot
17 |
18 | @Cog.listener()
19 | async def on_request__latency(self, u, data):
20 | return await self.bot.sio.emit("latency__{0}".format(u), {})
21 |
22 | @Cog.listener()
23 | async def on_request__guild_permissions(self, u, data):
24 |
25 | guild_ids = data["guild_ids"]
26 | user_id = data["user_id"]
27 |
28 | result = {}
29 |
30 | for guild_id in guild_ids:
31 | guild_id = int(guild_id)
32 |
33 | guild = self.bot.get_guild(guild_id)
34 | if not guild:
35 | result[guild_id] = -1
36 | continue
37 |
38 | if not guild.chunked:
39 | self.bot.loop.create_task(guild.chunk())
40 |
41 | member = await self.bot.get_or_fetch_member(guild, user_id)
42 | if not member:
43 | result[guild_id] = -1
44 | continue
45 |
46 | perms = 1
47 |
48 | if member.guild_permissions.manage_guild:
49 | result[guild_id] = 2
50 | continue
51 |
52 | g_record = await Guild.get(pk=guild_id)
53 | _roles = [str(_.id) for _ in member.roles]
54 |
55 | if any(i in g_record.dashboard_access["embed"] for i in _roles):
56 | perms *= 3
57 |
58 | if any(i in g_record.dashboard_access["scrims"] for i in _roles):
59 | perms *= 5
60 |
61 | if any(i in g_record.dashboard_access["tourney"] for i in _roles):
62 | perms *= 7
63 |
64 | if any(i in g_record.dashboard_access["slotm"] for i in _roles):
65 | perms *= 11
66 |
67 | result[guild_id] = perms
68 |
69 | await self.bot.sio.emit(f"guild_permissions__{u}", result)
70 |
--------------------------------------------------------------------------------
/src/sockets/events/guilds.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import typing as T
4 |
5 | import discord
6 |
7 | if T.TYPE_CHECKING:
8 | from core import Quotient
9 |
10 | from core import Cog
11 | from models import Guild
12 |
13 | from ..schemas import QGuild
14 |
15 | __all__ = ("SockGuild",)
16 |
17 |
18 | class SockGuild(Cog):
19 | def __init__(self, bot: Quotient):
20 | self.bot = bot
21 |
22 | @Cog.listener()
23 | async def on_request__get_guilds(self, u, data: dict):
24 | guild_ids = data["guild_ids"]
25 | user_id = data["user_id"]
26 |
27 | results: T.Dict[str, T.List[QGuild]] = {}
28 |
29 | for _id in guild_ids:
30 | guild = self.bot.get_guild(int(_id))
31 | if not guild:
32 | continue
33 |
34 | member = await self.bot.get_or_fetch_member(guild, user_id)
35 | if not member:
36 | continue
37 |
38 | results[str(_id)] = (
39 | await QGuild.from_guild(guild, await self.__guild_permissions(guild, member))
40 | ).dict()
41 |
42 | await self.bot.sio.emit("get_guilds__{0}".format(u), results)
43 |
44 | async def __guild_permissions(self, guild: discord.Guild, user: discord.Member):
45 | perms = 1
46 |
47 | if user.guild_permissions.manage_guild:
48 | return 2
49 |
50 | g = await Guild.get(pk=guild.id)
51 | _roles = [str(_.id) for _ in user.roles]
52 |
53 | if any(i in g.dashboard_access["embed"] for i in _roles):
54 | perms *= 3
55 |
56 | if any(i in g.dashboard_access["scrims"] for i in _roles):
57 | perms *= 5
58 |
59 | if any(i in g.dashboard_access["tourney"] for i in _roles):
60 | perms *= 7
61 |
62 | if any(i in g.dashboard_access["slotm"] for i in _roles):
63 | perms *= 11
64 |
65 | return perms
66 |
--------------------------------------------------------------------------------
/src/sockets/events/scrims.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import typing
4 |
5 | if typing.TYPE_CHECKING:
6 | from core import Quotient
7 |
8 | from core import Cog
9 | from models import Scrim
10 |
11 | from ..schemas import BaseScrim, SockResponse
12 |
13 | __all__ = ("SocketScrims",)
14 |
15 |
16 | class SocketScrims(Cog):
17 | def __init__(self, bot: Quotient):
18 | self.bot = bot
19 |
20 | @Cog.listener()
21 | async def on_request__bot_scrim_create(self, u: str, data: dict):
22 | data: BaseScrim = BaseScrim(**data)
23 |
24 | _v = await data.validate_perms(self.bot)
25 |
26 | if all(_v):
27 | _v = await data.create_scrim(self.bot)
28 |
29 | if not all(_v):
30 | return await self.bot.sio.emit(
31 | "bot_scrim_create__{0}".format(u), SockResponse(ok=False, error=_v[1]).dict()
32 | )
33 |
34 | await self.bot.sio.emit(
35 | "bot_scrim_create__{0}".format(u), SockResponse(data={"id": _v[1].id}).dict()
36 | )
37 |
38 | @Cog.listener()
39 | async def on_request__bot_scrim_edit(self, u: str, data: dict):
40 | data: BaseScrim = BaseScrim(**data)
41 |
42 | _v = await data.validate_perms(self.bot)
43 |
44 | if not all(_v):
45 | return await self.bot.sio.emit(
46 | "bot_scrim_edit__{0}".format(u), SockResponse(ok=False, error=_v[1]).dict()
47 | )
48 |
49 | await data.update_scrim(self.bot)
50 | await self.bot.sio.emit(f"bot_scrim_edit__{u}", SockResponse().dict())
51 |
52 | @Cog.listener()
53 | async def on_request__bot_scrim_delete(self, u: str, data: dict):
54 | guild_id, scrim_id = data["guild_id"], data["scrim_id"]
55 | if scrim_id:
56 | scrim = await Scrim.get_or_none(pk=scrim_id)
57 | if scrim:
58 | await scrim.full_delete()
59 |
60 | else:
61 | scrims = await Scrim.filter(guild_id=guild_id)
62 | for scrim in scrims:
63 | await scrim.full_delete()
64 |
65 | return await self.bot.sio.emit(f"bot_scrim_delete__{u}", SockResponse().dict())
66 |
--------------------------------------------------------------------------------
/src/sockets/events/settings.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING
4 |
5 | if TYPE_CHECKING:
6 | from core import Quotient
7 |
8 | from contextlib import suppress
9 |
10 | import constants
11 | import discord
12 | from core import Cog
13 | from models import Votes
14 |
15 | from ..schemas import SockResponse
16 |
17 |
18 | class SockSettings(Cog):
19 | def __init__(self, bot: Quotient):
20 | self.bot = bot
21 | self.hook = discord.Webhook.from_url(self.bot.config.PUBLIC_LOG, session=self.bot.session)
22 |
23 | @Cog.listener()
24 | async def on_request__prefix_change(self, u, data: dict):
25 | guild_id = data.get("guild_id")
26 | await self.bot.cache.update_guild_cache(int(guild_id))
27 | await self.bot.sio.emit("prefix_change__{0}".format(u), SockResponse().dict())
28 |
29 | @Cog.listener()
30 | async def on_request__new_vote(self, u, data: dict):
31 | user_id = int(data.get("user_id"))
32 | record = await Votes.get(pk=user_id)
33 |
34 | await self.bot.reminders.create_timer(record.expire_time, "vote", user_id=record.user_id)
35 |
36 | member = self.bot.server.get_member(record.user_id)
37 | if member is not None:
38 | await member.add_roles(discord.Object(id=self.bot.config.VOTER_ROLE), reason="They voted for me.")
39 |
40 | else:
41 | member = await self.bot.getch(self.bot.get_user, self.bot.fetch_user, record.pk)
42 |
43 | with suppress(discord.HTTPException, AttributeError):
44 |
45 | embed = discord.Embed(color=discord.Color.green(), description=f"Thanks **{member}** for voting.")
46 | embed.set_image(url=constants.random_thanks())
47 | embed.set_footer(text=f"Your total votes: {record.total_votes}")
48 | await self.hook.send(embed=embed, username="vote-logs", avatar_url=self.bot.user.display_avatar.url)
49 |
50 | @Cog.listener()
51 | async def on_request__get_usernames(self, u, data: dict):
52 | _dict = {}
53 | for _ in data.get("users"):
54 | _dict[str(_)] = str(await self.bot.getch(self.bot.get_user, self.bot.fetch_user, int(_)))
55 |
56 | await self.bot.sio.emit("get_usernames__{0}".format(u), SockResponse(data=_dict).dict())
57 |
--------------------------------------------------------------------------------
/src/sockets/events/tourney.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import TYPE_CHECKING
4 |
5 | if TYPE_CHECKING:
6 | from core import Quotient
7 |
8 | from core import Cog
9 | from models import Tourney
10 |
11 | from ..schemas import SockTourney
12 |
13 | __all__ = ("SockTourney",)
14 |
15 |
16 | class SockTourney(Cog):
17 | def __init__(self, bot: Quotient):
18 | self.bot = bot
19 |
20 | @Cog.listener()
21 | async def on_request__bot_tourney_create(self, u: str, data: dict):
22 | data: SockTourney = SockTourney(**data)
23 |
24 | @Cog.listener()
25 | async def on_request__bot_tourney_edit(self, u: str, data: dict):
26 | data: SockTourney = SockTourney(**data)
27 |
28 | @Cog.listener()
29 | async def on_request__bot_tourney_delete(self, u: str, data: dict):
30 | guild_id, tourney_id = data["guild_id"], data["tourney_id"]
31 | if tourney_id:
32 | tourney = await Tourney.get_or_none(pk=tourney_id)
33 | if tourney:
34 | await tourney.full_delete()
35 |
36 | else:
37 | tourneys = await Tourney.filter(guild_id=guild_id)
38 | for tourney in tourneys:
39 | await tourney.full_delete()
40 |
41 | return await self.bot.sio.emit("bot_tourney_delete__{0}".format(u), SockTourney().dict())
42 |
--------------------------------------------------------------------------------
/src/sockets/schemas/__init__.py:
--------------------------------------------------------------------------------
1 | from ._guild import *
2 | from ._resp import *
3 | from ._scrim import *
4 | from ._tourney import *
5 |
--------------------------------------------------------------------------------
/src/sockets/schemas/_guild.py:
--------------------------------------------------------------------------------
1 | import typing as T
2 |
3 | import discord
4 | from models import Guild
5 | from pydantic import BaseModel
6 |
7 | __all__ = ("QGuild",)
8 |
9 |
10 | class QGuild(BaseModel):
11 | id: str
12 | name: str
13 | icon: str
14 | channels: T.List[dict]
15 | roles: T.List[dict]
16 | boosted_by: dict
17 | dashboard_access: int
18 |
19 | @staticmethod
20 | async def from_guild(guild: discord.Guild, perms: int):
21 | _d = {
22 | "id": str(guild.id),
23 | "name": guild.name,
24 | "dashboard_access": perms,
25 | "icon": getattr(guild.icon, "url", "https://cdn.discordapp.com/embed/avatars/0.png"),
26 | }
27 |
28 | _d["channels"] = [{"id": str(c.id), "name": c.name} for c in guild.text_channels]
29 |
30 | _d["roles"] = [
31 | {"id": str(r.id), "name": r.name, "color": int(r.color), "managed": r.managed} for r in guild.roles
32 | ]
33 | _d["boosted_by"] = {}
34 |
35 | record = await Guild.get(pk=guild.id)
36 |
37 | if record.is_premium:
38 | booster = await record.bot.get_or_fetch_member(guild, record.made_premium_by)
39 | _d["boosted_by"] = {
40 | "id": str(getattr(booster, "id", 12345)),
41 | "username": getattr(booster, "name", "Unknown User"),
42 | "discriminator": getattr(booster, "discriminator", "#0000"),
43 | "avatar": booster.display_avatar.url if booster else "https://cdn.discordapp.com/embed/avatars/0.png",
44 | }
45 |
46 | return QGuild(**_d)
47 |
--------------------------------------------------------------------------------
/src/sockets/schemas/_resp.py:
--------------------------------------------------------------------------------
1 | from pydantic import BaseModel
2 |
3 | __all__ = ("SockResponse",)
4 |
5 |
6 | class SockResponse(BaseModel):
7 | ok: bool = True
8 | error: str = None
9 | data: dict = None
10 |
--------------------------------------------------------------------------------
/src/sockets/schemas/_tourney.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import List, Optional
4 |
5 | from pydantic import BaseModel
6 |
7 |
8 | class SockTourney(BaseModel):
9 | id: Optional[int] = None
10 | guild_id: int
11 | name: str = "Quotient-Tourney"
12 | registration_channel_id: int
13 | confirm_channel_id: int
14 | role_id: int
15 | required_mentions: int = 4
16 | total_slots: int
17 | banned_users: List[int]
18 | host_id: int
19 | multiregister: bool = False
20 | open_role_id: Optional[int] = None
21 | teamname_compulsion: bool = False
22 | ping_role_id: Optional[int] = None
23 | no_duplicate_name: bool = True
24 | autodelete_rejected: bool = True
25 | success_message: Optional[str] = None
26 |
--------------------------------------------------------------------------------
/src/utils/__init__.py:
--------------------------------------------------------------------------------
1 | from .buttons import *
2 | from .converters import *
3 | from .default import *
4 | from .emote import *
5 | from .formats import *
6 | from .inputs import *
7 | from .paginator import *
8 | from .time import *
9 |
--------------------------------------------------------------------------------
/src/utils/buttons.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | from typing import List, NamedTuple, Optional, Union
4 |
5 | import discord
6 |
7 | from .emote import TextChannel, VoiceChannel
8 |
9 |
10 | class LinkType(NamedTuple):
11 | name: Optional[str] = None
12 | url: Optional[str] = None
13 | emoji: Optional[str] = None
14 |
15 |
16 | class LinkButton(discord.ui.View):
17 | def __init__(self, links: Union[LinkType, List[LinkType]]):
18 | super().__init__()
19 |
20 | links = links if isinstance(links, list) else [links]
21 |
22 | for link in links:
23 | self.add_item(discord.ui.Button(label=link.name, url=link.url, emoji=link.emoji))
24 |
25 |
26 | class Prompt(discord.ui.View):
27 | def __init__(self, user_id, timeout=30.0):
28 | super().__init__(timeout=timeout)
29 | self.user_id = user_id
30 | self.value = None
31 |
32 | async def interaction_check(self, interaction: discord.Interaction) -> bool:
33 | if interaction.user.id != self.user_id:
34 | await interaction.response.send_message(
35 | "Sorry, you can't use this interaction as it is not started by you.", ephemeral=True
36 | )
37 | return False
38 | return True
39 |
40 | @discord.ui.button(label="Confirm", style=discord.ButtonStyle.green)
41 | async def confirm(self, interaction: discord.Interaction, _: discord.ui.Button):
42 | await interaction.response.defer()
43 | self.value = True
44 | self.stop()
45 |
46 | @discord.ui.button(label="Cancel", style=discord.ButtonStyle.grey)
47 | async def cancel(self, interaction: discord.Interaction, _: discord.ui.Button):
48 | await interaction.response.defer()
49 | self.value = False
50 | self.stop()
51 |
52 |
53 | class BaseSelector(discord.ui.View):
54 | message: discord.Message
55 |
56 | def __init__(self, author_id, selector: discord.ui.Select, **kwargs):
57 | self.author_id = author_id
58 | self.custom_id = None
59 | super().__init__(timeout=30.0)
60 |
61 | self.add_item(selector(**kwargs))
62 |
63 | async def interaction_check(self, interaction: discord.Interaction) -> bool:
64 | if interaction.user.id != self.author_id:
65 | await interaction.response.send_message(
66 | "Sorry, you can't use this interaction as it is not started by you.", ephemeral=True
67 | )
68 | return False
69 | return True
70 |
71 | async def on_timeout(self) -> None:
72 | if hasattr(self, "message"):
73 | await self.message.delete()
74 |
75 |
76 | class ChannelSelector(discord.ui.Select):
77 | def __init__(self, placeholder: str, channels: List[Union[discord.TextChannel, discord.VoiceChannel]]):
78 |
79 | _options = []
80 | for channel in channels:
81 | _options.append(
82 | discord.SelectOption(
83 | label=channel.name,
84 | value=channel.id,
85 | description=f"{channel.name} ({channel.id})",
86 | emoji=TextChannel if isinstance(channel, discord.TextChannel) else VoiceChannel,
87 | )
88 | )
89 |
90 | super().__init__(placeholder=placeholder, options=_options)
91 |
92 | async def callback(self, interaction: discord.Interaction):
93 | await interaction.response.defer()
94 | self.view.custom_id = interaction.data["values"][0]
95 | self.view.stop()
96 |
97 |
98 | class CustomSelector(discord.ui.Select):
99 | def __init__(self, placeholder: str, options: List[discord.SelectOption]):
100 | super().__init__(placeholder=placeholder, options=options)
101 |
102 | async def callback(self, interaction: discord.Interaction):
103 | await interaction.response.defer()
104 | self.view.custom_id = interaction.data["values"][0]
105 | self.view.stop()
106 |
--------------------------------------------------------------------------------
/src/utils/checks.py:
--------------------------------------------------------------------------------
1 | from typing import Union
2 |
3 | from discord.ext import commands
4 | from discord.ext.commands import CheckFailure, Context, has_any_role
5 |
6 | from models import Guild, User
7 |
8 | from .exceptions import *
9 |
10 |
11 | def has_done_setup():
12 | async def predicate(ctx: Context):
13 | check = await Guild.get_or_none(pk=ctx.guild.id)
14 | if not check.private_ch:
15 | raise NotSetup()
16 |
17 | else:
18 | return True
19 |
20 | return commands.check(predicate)
21 |
22 |
23 | def is_premium_guild():
24 | async def predictate(ctx: Context):
25 | check = await Guild.get_or_none(guild_id=ctx.guild.id)
26 | if not check or check.is_premium is False:
27 | raise NotPremiumGuild()
28 |
29 | else:
30 | return True
31 |
32 | return commands.check(predictate)
33 |
34 |
35 | def is_premium_user():
36 | async def predicate(ctx: Context):
37 | check = await User.get_or_none(user_id=ctx.author.id)
38 | if not check or check.is_premium is False:
39 | raise NotPremiumUser()
40 |
41 | else:
42 | return True
43 |
44 | return commands.check(predicate)
45 |
46 |
47 | def can_use_sm():
48 | """
49 | Returns True if the user has manage roles or scrim-mod role in the server.
50 | """
51 |
52 | async def predicate(ctx: Context):
53 | if ctx.author.guild_permissions.manage_guild or "scrims-mod" in (role.name.lower() for role in ctx.author.roles):
54 | return True
55 | raise SMNotUsable()
56 |
57 | return commands.check(predicate)
58 |
59 |
60 | def can_use_tm():
61 | """
62 | Returns True if the user has manage roles or scrim-mod role in the server.
63 | """
64 |
65 | async def predicate(ctx: Context):
66 | if ctx.author.guild_permissions.manage_guild or "tourney-mod" in (role.name.lower() for role in ctx.author.roles):
67 | return True
68 | raise TMNotUsable()
69 |
70 | return commands.check(predicate)
71 |
72 |
73 | async def has_any_role_check(ctx: Context, *roles: Union[str, int]) -> bool:
74 | """
75 | Returns True if the context's author has any of the specified roles.
76 | `roles` are the names or IDs of the roles for which to check.
77 | False is always returns if the context is outside a guild.
78 | """
79 | try:
80 | return await has_any_role(*roles).predicate(ctx)
81 | except CheckFailure:
82 | return False
83 |
84 |
85 | async def check_guild_permissions(ctx: Context, perms, *, check=all):
86 | is_owner = await ctx.bot.is_owner(ctx.author)
87 | if is_owner:
88 | return True
89 |
90 | if ctx.guild is None:
91 | return False
92 |
93 | resolved = ctx.author.guild_permissions
94 | return check(getattr(resolved, name, None) == value for name, value in perms.items())
95 |
96 |
97 | def is_mod():
98 | async def pred(ctx):
99 | return await check_guild_permissions(ctx, {"manage_guild": True})
100 |
101 | return commands.check(pred)
102 |
103 |
104 | def is_admin():
105 | async def pred(ctx):
106 | return await check_guild_permissions(ctx, {"administrator": True})
107 |
108 | return commands.check(pred)
109 |
110 |
111 | async def check_permissions(ctx: Context, perms, *, check=all):
112 | is_owner = await ctx.bot.is_owner(ctx.author)
113 | if is_owner:
114 | return True
115 |
116 | resolved = ctx.channel.permissions_for(ctx.author)
117 | return check(getattr(resolved, name, None) == value for name, value in perms.items())
118 |
119 |
120 | def has_permissions(*, check=all, **perms):
121 | async def pred(ctx):
122 | return await check_permissions(ctx, perms, check=check)
123 |
124 | return commands.check(pred)
125 |
--------------------------------------------------------------------------------
/src/utils/default.py:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 |
3 | import re
4 | from datetime import datetime
5 | from itertools import islice
6 | from typing import Union
7 | from unicodedata import normalize as nm
8 | import discord
9 |
10 | from constants import IST
11 |
12 |
13 | def get_chunks(iterable, size: int):
14 | it = iter(iterable)
15 | return iter(lambda: tuple(islice(it, size)), ())
16 |
17 |
18 | def split_list(data: list, per_list: int):
19 | data = list(data)
20 |
21 | new = []
22 |
23 | for i in range(0, len(data), per_list):
24 | new.append(data[i : i + per_list])
25 |
26 | return new
27 |
28 |
29 | def find_team(message: discord.Message):
30 | """Finds team name from a message"""
31 | content = message.content.lower()
32 | author = message.author
33 | teamname = re.search(r"team.*", content)
34 | if teamname is None:
35 | return f"{author}'s team"
36 |
37 | # teamname = (re.sub(r"\b[0-9]+\b\s*|team|name|[^\w\s]", "", teamname.group())).strip()
38 | teamname: str = re.sub(r"<@*#*!*&*\d+>|team|name|[^\w\s]", "", teamname.group()).strip()
39 |
40 | teamname = f"Team {teamname.title()}" if teamname else f"{author}'s team"
41 | return teamname
42 |
43 |
44 | def regional_indicator(c: str) -> str:
45 | """Returns a regional indicator emoji given a character."""
46 | return chr(0x1F1E6 - ord("A") + ord(c.upper()))
47 |
48 |
49 | def keycap_digit(c: Union[int, str]) -> str:
50 | """Returns a keycap digit emoji given a character."""
51 | c = int(c)
52 | if 0 < c < 10:
53 | return str(c) + "\U0000FE0F\U000020E3"
54 | if c == 10:
55 | return "\U000FE83B"
56 | raise ValueError("Invalid keycap digit")
57 |
58 |
59 | async def aenumerate(asequence, start=0):
60 | """Asynchronously enumerate an async iterator from a given start value"""
61 | n = start
62 | async for elem in asequence:
63 | yield n, elem
64 | n += 1
65 |
66 |
67 | def get_ipm(bot):
68 | """Returns Quotient's cmds invoke rate per minute"""
69 | time = (datetime.now(tz=IST) - bot.start_time).total_seconds()
70 | per_second = bot.cmd_invokes / time
71 | per_minute = per_second * 60
72 | return per_minute
73 |
--------------------------------------------------------------------------------
/src/utils/emote.py:
--------------------------------------------------------------------------------
1 | # never ask me to remove these :c
2 | red = "<:red:775586906599456779>"
3 | green = "<:green:775586905580240946>"
4 | yellow = "<:yellow:775586904439128064>"
5 | invisible = "<:invisible:775586907680931860>"
6 |
7 | scrimscheck = "<:scrimscheck:839554647861755946>"
8 | scrimsxmark = "<:scrimscross:839554689778712576>"
9 |
10 | info = "<:info2:899020593188462693>"
11 | trash = "<:trashcan:896382424529907742>"
12 | exit = "<:exit:926048897548300339>"
13 |
14 |
15 | add = "<:add:844825523003850772>"
16 | remove = "<:remove:844825861295046661>"
17 | edit = "<:edit:844826616735727616>"
18 |
19 | diamond = "