Skip to content

Commit

Permalink
[➕] Add UserFlags, FrozenFlags
Browse files Browse the repository at this point in the history
  • Loading branch information
WizzyGeek committed Feb 16, 2022
1 parent 6241dfa commit 58a3425
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 71 deletions.
102 changes: 102 additions & 0 deletions cordy/models/flags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
from __future__ import annotations

from typing import ClassVar, Literal, Union

__all__ = (
"Flag",
"IntFlags"
)

AnyBitLike = Union[bool, Literal[1, 0]]

def populate_flags(cls):
cls.FLAGS = {
name.lower(): flag.value for name, flag in cls.__dict__.items() if isinstance(flag, Flag)
}

cls._LEN_FLAGS = max(cls.FLAGS.values()).bit_length()

return cls

class Flag:
__slots__ = ("value",)

value: int

def __init__(self, value: int):
self.value = value

def __get__(self, inst: IntFlags, _) -> bool:
return (inst.value & self.value) == self.value

def __set__(self, inst, value: AnyBitLike) -> None:
inst.value = (inst.value & ~self.value) | (self.value * (value & 1))

class IntFlags: # For intents
FLAGS: ClassVar[dict[str, int]]
_LEN_FLAGS: ClassVar[int]

value: int

def __init__(self, **kwargs: dict[str, bool]) -> None:
self.value = 0 # start from all disabled

for k, v in kwargs.items():
if v:
try:
self.value |= self.FLAGS[k]
except KeyError:
raise TypeError(f"Unexpected flag keyword argument: {k}")

def __setitem__(self, index: int, value: AnyBitLike) -> None:
if index >= self._LEN_FLAGS:
raise IndexError (f"Index out of range for {type(self)} object.")
mask = 1 << index
self.value = (self.value & ~mask) | ((value << index) & mask)

def __getitem__(self, index: int) -> bool:
return bool((self.value & (1 << index)))

def __or__(self, other):
if not isinstance(other, self.__class__):
raise TypeError(f"unsupported operand type(s) for |: '{type(self)}' and '{type(other)}'")

inst = self.__class__()
inst.value = self.value | other.value
return inst

def __ior__(self, other):
if not isinstance(other, self.__class__):
raise TypeError(f"unsupported operand type(s) for |: '{type(self)}' and '{type(other)}'")

self.value |= other.value

@classmethod
def from_int(cls, value: int):
inst = cls()
inst.value = value
return cls

class FrozenFlags(int): # Read-Only, for user flags
def __new__(cls, data: int = None):
if data is not None:
return super().__new__(cls, data)

def __getitem__(self, index: int) -> bool:
return bool(self & (1 << index))

class FrozenFlag:
__slots__ = ("value",)

value: int

def __init__(self, value: int):
self.value = value

def __set__(self, inst: FrozenFlags, _):
raise TypeError("Cannot set value for a FrozenFlag instance")

def __get__(self, inst: FrozenFlags, _) -> bool:
return bool(inst & self.value)


72 changes: 1 addition & 71 deletions cordy/models/intents.py
Original file line number Diff line number Diff line change
@@ -1,81 +1,11 @@
from __future__ import annotations

from typing import ClassVar, Literal, Union
from .flags import Flag, IntFlags, populate_flags

__all__ = (
"Intents",
)


AnyBitLike = Union[bool, Literal[1, 0]]

def populate_flags(cls):
cls.FLAGS = {
name.lower(): flag.value for name, flag in cls.__dict__.items() if isinstance(flag, Flag)
}

cls._LEN_FLAGS = max(cls.FLAGS.values()).bit_length()

return cls

class Flag:
value: int

def __init__(self, value: int):
self.value = value

def __get__(self, inst: IntFlags, _) -> bool:
return (inst.value & self.value) == self.value

def __set__(self, inst, value: AnyBitLike) -> None:
inst.value = (inst.value & ~self.value) | (self.value * (value & 1))

class IntFlags:
FLAGS: ClassVar[dict[str, int]]
_LEN_FLAGS: ClassVar[int]

value: int

def __init__(self, **kwargs: dict[str, bool]) -> None:
self.value = 0 # start from all disabled

for k, v in kwargs.items():
if v:
try:
self.value |= self.FLAGS[k]
except KeyError:
raise TypeError(f"Unexpected flag keyword argument: {k}")

def __setitem__(self, index: int, value: AnyBitLike) -> None:
if index >= self._LEN_FLAGS:
raise IndexError (f"Index out of range for {type(self)} object.")
mask = 1 << index
self.value = (self.value & ~mask) | ((value << index) & mask)

def __getitem__(self, index: int) -> bool:
return bool((self.value & (1 << index)))

def __or__(self, other):
if not isinstance(other, self.__class__):
raise TypeError(f"unsupported operand type(s) for |: '{type(self)}' and '{type(other)}'")

inst = self.__class__()
inst.value = self.value | other.value
return inst

def __ior__(self, other):
if not isinstance(other, self.__class__):
raise TypeError(f"unsupported operand type(s) for |: '{type(self)}' and '{type(other)}'")

self.value |= other.value

@classmethod
def from_int(cls, value: int):
inst = cls()
inst.value = value
return cls


@populate_flags
class Intents(IntFlags):
guilds = Flag(1 << 0)
Expand Down
42 changes: 42 additions & 0 deletions cordy/models/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from __future__ import annotations

from .flags import FrozenFlag as FF
from .flags import FrozenFlags


class UserFlags(FrozenFlags):
staff = FF(1)
""":class:`bool`: A discord employee"""
partner = FF(1 << 1)
""":class:`bool`: Owner of a partnered server"""
hypesquad_events = FF(1 << 2)
""":class:`bool`: HypeSquad events coordinator"""
bug_hunter_level_1 = FF(1 << 3)
""":class:`bool`: Bug hunter level 1"""
# 4, 5 missing

hypesquad_bravery = FF(1 << 6)
""":class:`bool`: HypeSquad Bravery house member"""
hypesquad_brilliance = FF(1 << 7)
""":class:`bool`: HypeSquad Brilliance house member"""
hypesquad_balance = FF(1 << 8)
""":class:`bool`: HypeSquad Balance house member"""

early_supporter = FF(1 << 9)
""":class:`bool`: Early nitro supporter"""
team_user = FF(1 << 10)
""":class:`bool`: User is a team"""
#11, 12, 13
bug_hunter_level_2 = FF(1 << 14)
""":class:`bool`: Bug Hunter level 2"""
bug_hunter = FF(1 << 3 | 1 << 14)
""":class:`bool`: Bug Hunter level 1 or 2"""
# 15
verified_bot = FF(1 << 16)
""":class:`bool`: Verfied bot account"""
verified_dev = FF(1 << 17)
""":class:`bool`: Early verified bot developer"""
certified_mod = FF(1 << 18)
""":class:`bool`: Discord certified moderator"""
interaction_bot = FF(1 << 19)
""":class:`bool`: The bot exclusively uses http interactions"""

0 comments on commit 58a3425

Please sign in to comment.