disagreement 0.1.0rc3__py3-none-any.whl → 0.3.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
disagreement/__init__.py CHANGED
@@ -14,10 +14,10 @@ __title__ = "disagreement"
14
14
  __author__ = "Slipstream"
15
15
  __license__ = "BSD 3-Clause License"
16
16
  __copyright__ = "Copyright 2025 Slipstream"
17
- __version__ = "0.1.0rc3"
17
+ __version__ = "0.3.0b1"
18
18
 
19
19
  from .client import Client, AutoShardedClient
20
- from .models import Message, User, Reaction
20
+ from .models import Message, User, Reaction, AuditLogEntry
21
21
  from .voice_client import VoiceClient
22
22
  from .audio import AudioSource, FFmpegAudioSource
23
23
  from .typing import Typing
disagreement/audio.py CHANGED
@@ -114,3 +114,20 @@ class FFmpegAudioSource(AudioSource):
114
114
  if isinstance(self.source, io.IOBase):
115
115
  with contextlib.suppress(Exception):
116
116
  self.source.close()
117
+
118
+ class AudioSink:
119
+ """Abstract base class for audio sinks."""
120
+
121
+ def write(self, user, data):
122
+ """Write a chunk of PCM audio.
123
+
124
+ Subclasses must implement this. The data is raw PCM at 48kHz
125
+ stereo.
126
+ """
127
+
128
+ raise NotImplementedError
129
+
130
+ def close(self) -> None:
131
+ """Cleanup the sink when the voice client disconnects."""
132
+
133
+ return None
disagreement/cache.py CHANGED
@@ -4,7 +4,8 @@ import time
4
4
  from typing import TYPE_CHECKING, Dict, Generic, Optional, TypeVar
5
5
 
6
6
  if TYPE_CHECKING:
7
- from .models import Channel, Guild
7
+ from .models import Channel, Guild, Member
8
+ from .caching import MemberCacheFlags
8
9
 
9
10
  T = TypeVar("T")
10
11
 
@@ -53,3 +54,32 @@ class GuildCache(Cache["Guild"]):
53
54
 
54
55
  class ChannelCache(Cache["Channel"]):
55
56
  """Cache specifically for :class:`Channel` objects."""
57
+
58
+
59
+ class MemberCache(Cache["Member"]):
60
+ """
61
+ A cache for :class:`Member` objects that respects :class:`MemberCacheFlags`.
62
+ """
63
+
64
+ def __init__(self, flags: MemberCacheFlags, ttl: Optional[float] = None) -> None:
65
+ super().__init__(ttl)
66
+ self.flags = flags
67
+
68
+ def _should_cache(self, member: Member) -> bool:
69
+ """Determines if a member should be cached based on the flags."""
70
+ if self.flags.all:
71
+ return True
72
+ if self.flags.none:
73
+ return False
74
+
75
+ if self.flags.online and member.status != "offline":
76
+ return True
77
+ if self.flags.voice and member.voice_state is not None:
78
+ return True
79
+ if self.flags.joined and getattr(member, "_just_joined", False):
80
+ return True
81
+ return False
82
+
83
+ def set(self, key: str, value: Member) -> None:
84
+ if self._should_cache(value):
85
+ super().set(key, value)
@@ -0,0 +1,120 @@
1
+ from __future__ import annotations
2
+
3
+ import operator
4
+ from typing import Any, Callable, ClassVar, Dict, Iterator, Tuple
5
+
6
+
7
+ class _MemberCacheFlagValue:
8
+ flag: int
9
+
10
+ def __init__(self, func: Callable[[Any], bool]):
11
+ self.flag = getattr(func, 'flag', 0)
12
+ self.__doc__ = func.__doc__
13
+
14
+ def __get__(self, instance: 'MemberCacheFlags', owner: type) -> Any:
15
+ if instance is None:
16
+ return self
17
+ return instance.value & self.flag != 0
18
+
19
+ def __set__(self, instance: Any, value: bool) -> None:
20
+ if value:
21
+ instance.value |= self.flag
22
+ else:
23
+ instance.value &= ~self.flag
24
+
25
+ def __repr__(self) -> str:
26
+ return f'<{self.__class__.__name__} flag={self.flag}>'
27
+
28
+
29
+ def flag_value(flag: int) -> Callable[[Callable[[Any], bool]], _MemberCacheFlagValue]:
30
+ def decorator(func: Callable[[Any], bool]) -> _MemberCacheFlagValue:
31
+ setattr(func, 'flag', flag)
32
+ return _MemberCacheFlagValue(func)
33
+ return decorator
34
+
35
+
36
+ class MemberCacheFlags:
37
+ __slots__ = ('value',)
38
+
39
+ VALID_FLAGS: ClassVar[Dict[str, int]] = {
40
+ 'joined': 1 << 0,
41
+ 'voice': 1 << 1,
42
+ 'online': 1 << 2,
43
+ }
44
+ DEFAULT_FLAGS: ClassVar[int] = 1 | 2 | 4
45
+ ALL_FLAGS: ClassVar[int] = sum(VALID_FLAGS.values())
46
+
47
+ def __init__(self, **kwargs: bool):
48
+ self.value = self.DEFAULT_FLAGS
49
+ for key, value in kwargs.items():
50
+ if key not in self.VALID_FLAGS:
51
+ raise TypeError(f'{key!r} is not a valid member cache flag.')
52
+ setattr(self, key, value)
53
+
54
+ @classmethod
55
+ def _from_value(cls, value: int) -> MemberCacheFlags:
56
+ self = cls.__new__(cls)
57
+ self.value = value
58
+ return self
59
+
60
+ def __eq__(self, other: object) -> bool:
61
+ return isinstance(other, MemberCacheFlags) and self.value == other.value
62
+
63
+ def __ne__(self, other: object) -> bool:
64
+ return not self.__eq__(other)
65
+
66
+ def __hash__(self) -> int:
67
+ return hash(self.value)
68
+
69
+ def __repr__(self) -> str:
70
+ return f'<MemberCacheFlags value={self.value}>'
71
+
72
+ def __iter__(self) -> Iterator[Tuple[str, bool]]:
73
+ for name in self.VALID_FLAGS:
74
+ yield name, getattr(self, name)
75
+
76
+ def __int__(self) -> int:
77
+ return self.value
78
+
79
+ def __index__(self) -> int:
80
+ return self.value
81
+
82
+ @classmethod
83
+ def all(cls) -> MemberCacheFlags:
84
+ """A factory method that creates a :class:`MemberCacheFlags` with all flags enabled."""
85
+ return cls._from_value(cls.ALL_FLAGS)
86
+
87
+ @classmethod
88
+ def none(cls) -> MemberCacheFlags:
89
+ """A factory method that creates a :class:`MemberCacheFlags` with all flags disabled."""
90
+ return cls._from_value(0)
91
+
92
+ @classmethod
93
+ def only_joined(cls) -> MemberCacheFlags:
94
+ """A factory method that creates a :class:`MemberCacheFlags` with only the `joined` flag enabled."""
95
+ return cls._from_value(cls.VALID_FLAGS['joined'])
96
+
97
+ @classmethod
98
+ def only_voice(cls) -> MemberCacheFlags:
99
+ """A factory method that creates a :class:`MemberCacheFlags` with only the `voice` flag enabled."""
100
+ return cls._from_value(cls.VALID_FLAGS['voice'])
101
+
102
+ @classmethod
103
+ def only_online(cls) -> MemberCacheFlags:
104
+ """A factory method that creates a :class:`MemberCacheFlags` with only the `online` flag enabled."""
105
+ return cls._from_value(cls.VALID_FLAGS['online'])
106
+
107
+ @flag_value(1 << 0)
108
+ def joined(self) -> bool:
109
+ """Whether to cache members that have just joined the guild."""
110
+ return False
111
+
112
+ @flag_value(1 << 1)
113
+ def voice(self) -> bool:
114
+ """Whether to cache members that are in a voice channel."""
115
+ return False
116
+
117
+ @flag_value(1 << 2)
118
+ def online(self) -> bool:
119
+ """Whether to cache members that are online."""
120
+ return False