-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
145 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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""" |