Skip to content

PyDrocsid.permission

BasePermission

Bases: Enum

Source code in PyDrocsid/permission.py
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
class BasePermission(Enum):
    @property
    def description(self) -> str:
        raise NotImplementedError

    @property
    def cog(self) -> str:
        return cast(str, sys.modules[self.__class__.__module__].__package__).split(".")[-1]

    @property
    def fullname(self) -> str:
        return self.cog + "." + self.name

    @property
    def _default_level(self) -> BasePermissionLevel:
        from PyDrocsid.config import Config

        # get default level from overrides or use the global default
        return Config.DEFAULT_PERMISSION_OVERRIDES.get(self.cog, {}).get(self.name, Config.DEFAULT_PERMISSION_LEVEL)

    async def resolve(self) -> BasePermissionLevel:
        """Get the configured permission level of this permission."""

        from PyDrocsid.config import Config

        value: int = await PermissionModel.get(self.fullname, self._default_level.level)
        for level in Config.PERMISSION_LEVELS:  # type: BasePermissionLevel
            if level.level == value:
                return level
        raise ValueError(f"permission level not found: {value}")

    async def set(self, level: BasePermissionLevel) -> None:
        """Configure the permission level of this permission."""

        await PermissionModel.set(self.fullname, level.level)

    async def check_permissions(self, member: User | Member) -> bool:
        """Return whether this permission is granted to a given member."""

        return await (await self.resolve()).check_permissions(member)

    @property
    def check(self) -> Callable[[Context[Bot]], Awaitable[bool]]:
        """Decorator for bot commands to require this permission when invoking this command."""

        return check_permission_level(self)

check property

check() -> Callable[[Context[Bot]], Awaitable[bool]]

Decorator for bot commands to require this permission when invoking this command.

Source code in PyDrocsid/permission.py
107
108
109
110
111
@property
def check(self) -> Callable[[Context[Bot]], Awaitable[bool]]:
    """Decorator for bot commands to require this permission when invoking this command."""

    return check_permission_level(self)

check_permissions async

check_permissions(member: User | Member) -> bool

Return whether this permission is granted to a given member.

Source code in PyDrocsid/permission.py
102
103
104
105
async def check_permissions(self, member: User | Member) -> bool:
    """Return whether this permission is granted to a given member."""

    return await (await self.resolve()).check_permissions(member)

resolve async

resolve() -> BasePermissionLevel

Get the configured permission level of this permission.

Source code in PyDrocsid/permission.py
86
87
88
89
90
91
92
93
94
95
async def resolve(self) -> BasePermissionLevel:
    """Get the configured permission level of this permission."""

    from PyDrocsid.config import Config

    value: int = await PermissionModel.get(self.fullname, self._default_level.level)
    for level in Config.PERMISSION_LEVELS:  # type: BasePermissionLevel
        if level.level == value:
            return level
    raise ValueError(f"permission level not found: {value}")

set async

set(level: BasePermissionLevel) -> None

Configure the permission level of this permission.

Source code in PyDrocsid/permission.py
 97
 98
 99
100
async def set(self, level: BasePermissionLevel) -> None:
    """Configure the permission level of this permission."""

    await PermissionModel.set(self.fullname, level.level)

BasePermissionLevel

Bases: Enum

Source code in PyDrocsid/permission.py
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
class BasePermissionLevel(Enum):
    @property
    def level(self) -> int:
        return cast(int, self.value.level)

    @property
    def aliases(self) -> list[str]:
        return cast(list[str], self.value.aliases)

    @property
    def description(self) -> str:
        return cast(str, self.value.description)

    @property
    def guild_permissions(self) -> list[str]:
        return cast(list[str], self.value.guild_permissions)

    @property
    def roles(self) -> list[str]:
        return cast(list[str], self.value.roles)

    @classmethod
    async def get_permission_level(cls, member: User | Member) -> BasePermissionLevel:
        """Get the permission level of a given member without (takes permission_override into account)."""

        if override := permission_override.get(None):
            return override

        return await cls._get_permission_level(member)

    @classmethod
    async def _get_permission_level(cls, member: User | Member) -> BasePermissionLevel:
        """Get the permission level of a given member."""

        raise NotImplementedError

    async def check_permissions(self, member: User | Member) -> bool:
        """Return whether this permission level is granted to a given member."""

        level: BasePermissionLevel = await self.get_permission_level(member)
        return level.level >= self.level

    @property
    def check(self) -> Callable[[Context[Bot]], Awaitable[bool]]:
        """Decorator for bot commands to require this permission level when invoking this command."""

        return check_permission_level(self)

    @classmethod
    def max(cls) -> BasePermissionLevel:
        """Returns the highest permission level available."""

        return max(cast(Iterable[BasePermissionLevel], cls), key=lambda x: x.level)

check property

check() -> Callable[[Context[Bot]], Awaitable[bool]]

Decorator for bot commands to require this permission level when invoking this command.

Source code in PyDrocsid/permission.py
159
160
161
162
163
@property
def check(self) -> Callable[[Context[Bot]], Awaitable[bool]]:
    """Decorator for bot commands to require this permission level when invoking this command."""

    return check_permission_level(self)

check_permissions async

check_permissions(member: User | Member) -> bool

Return whether this permission level is granted to a given member.

Source code in PyDrocsid/permission.py
153
154
155
156
157
async def check_permissions(self, member: User | Member) -> bool:
    """Return whether this permission level is granted to a given member."""

    level: BasePermissionLevel = await self.get_permission_level(member)
    return level.level >= self.level

get_permission_level classmethod async

get_permission_level(member: User | Member) -> BasePermissionLevel

Get the permission level of a given member without (takes permission_override into account).

Source code in PyDrocsid/permission.py
138
139
140
141
142
143
144
145
@classmethod
async def get_permission_level(cls, member: User | Member) -> BasePermissionLevel:
    """Get the permission level of a given member without (takes permission_override into account)."""

    if override := permission_override.get(None):
        return override

    return await cls._get_permission_level(member)

max classmethod

max() -> BasePermissionLevel

Returns the highest permission level available.

Source code in PyDrocsid/permission.py
165
166
167
168
169
@classmethod
def max(cls) -> BasePermissionLevel:
    """Returns the highest permission level available."""

    return max(cast(Iterable[BasePermissionLevel], cls), key=lambda x: x.level)

PermissionModel

Bases: Base

Source code in PyDrocsid/permission.py
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
class PermissionModel(Base):
    __tablename__ = "permissions"

    permission: Mapped[str] = Column(String(64), primary_key=True, unique=True)
    level: Mapped[int] = Column(Integer)

    @staticmethod
    async def create(permission: str, level: int) -> PermissionModel:
        row = PermissionModel(permission=permission, level=level)
        await db.add(row)
        return row

    @staticmethod
    async def get(permission: str, default: int) -> int:
        """Get the configured level of a given permission."""

        if (value := await redis.get(rkey := f"permissions:{permission}")) is not None:
            return int(value)

        if (row := await db.get(PermissionModel, permission=permission)) is None:
            row = await PermissionModel.create(permission, default)

        await redis.setex(rkey, CACHE_TTL, row.level)

        return row.level

    @staticmethod
    async def set(permission: str, level: int) -> PermissionModel:
        """Configure the level of a given permission."""

        await redis.setex(f"permissions:{permission}", CACHE_TTL, level)

        if (row := await db.get(PermissionModel, permission=permission)) is None:
            return await PermissionModel.create(permission, level)

        row.level = level
        return row

get async staticmethod

get(permission: str, default: int) -> int

Get the configured level of a given permission.

Source code in PyDrocsid/permission.py
39
40
41
42
43
44
45
46
47
48
49
50
51
@staticmethod
async def get(permission: str, default: int) -> int:
    """Get the configured level of a given permission."""

    if (value := await redis.get(rkey := f"permissions:{permission}")) is not None:
        return int(value)

    if (row := await db.get(PermissionModel, permission=permission)) is None:
        row = await PermissionModel.create(permission, default)

    await redis.setex(rkey, CACHE_TTL, row.level)

    return row.level

set async staticmethod

set(permission: str, level: int) -> PermissionModel

Configure the level of a given permission.

Source code in PyDrocsid/permission.py
53
54
55
56
57
58
59
60
61
62
63
@staticmethod
async def set(permission: str, level: int) -> PermissionModel:
    """Configure the level of a given permission."""

    await redis.setex(f"permissions:{permission}", CACHE_TTL, level)

    if (row := await db.get(PermissionModel, permission=permission)) is None:
        return await PermissionModel.create(permission, level)

    row.level = level
    return row

check_permission_level

check_permission_level(level: BasePermission | BasePermissionLevel) -> Callable[[Context[Bot]], Awaitable[bool]]

Discord commmand check to require a given level when invoking the command.

Source code in PyDrocsid/permission.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
def check_permission_level(level: BasePermission | BasePermissionLevel) -> Callable[[Context[Bot]], Awaitable[bool]]:
    """Discord commmand check to require a given level when invoking the command."""

    async def inner(ctx: Context[Bot]) -> bool:
        member: User | Member = ctx.author
        if not isinstance(member, Member):
            member = ctx.bot.guilds[0].get_member(ctx.author.id) or member
        if not await level.check_permissions(member):
            raise CheckFailure(t.g.not_allowed)

        return True

    inner.level = level  # type: ignore

    return cast(Callable[[Context[Bot]], Awaitable[bool]], check(inner))