modifyself 0.2.0__tar.gz → 0.2.2__tar.gz

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.
Files changed (55) hide show
  1. {modifyself-0.2.0 → modifyself-0.2.2}/PKG-INFO +3 -1
  2. {modifyself-0.2.0 → modifyself-0.2.2}/README.md +30 -30
  3. modifyself-0.2.2/modifyself/models/billing.py +88 -0
  4. modifyself-0.2.2/modifyself/models/message.py +222 -0
  5. modifyself-0.2.2/modifyself/models/relationship.py +47 -0
  6. modifyself-0.2.2/modifyself/models/settings.py +54 -0
  7. modifyself-0.2.2/modifyself/models/webhook.py +168 -0
  8. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself.egg-info/PKG-INFO +3 -1
  9. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself.egg-info/requires.txt +2 -0
  10. {modifyself-0.2.0 → modifyself-0.2.2}/pyproject.toml +34 -30
  11. modifyself-0.2.0/modifyself/models/billing.py +0 -0
  12. modifyself-0.2.0/modifyself/models/message.py +0 -180
  13. modifyself-0.2.0/modifyself/models/relationship.py +0 -0
  14. modifyself-0.2.0/modifyself/models/settings.py +0 -0
  15. modifyself-0.2.0/modifyself/models/webhook.py +0 -0
  16. {modifyself-0.2.0 → modifyself-0.2.2}/LICENSE +0 -0
  17. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/__init__.py +0 -0
  18. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/activity.py +0 -0
  19. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/client.py +0 -0
  20. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/commands/__init__.py +0 -0
  21. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/commands/checks.py +0 -0
  22. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/commands/cog.py +0 -0
  23. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/commands/context.py +0 -0
  24. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/commands/converters.py +0 -0
  25. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/commands/core.py +0 -0
  26. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/components.py +0 -0
  27. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/core/bitfield.py +0 -0
  28. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/core/enums.py +0 -0
  29. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/core/mixins.py +0 -0
  30. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/core/snowflake.py +0 -0
  31. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/errors.py +0 -0
  32. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/gateway/__init__.py +0 -0
  33. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/gateway/dispatcher.py +0 -0
  34. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/gateway/heartbeat.py +0 -0
  35. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/gateway/websocket.py +0 -0
  36. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/headers.py +0 -0
  37. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/http/__init__.py +0 -0
  38. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/http/client.py +0 -0
  39. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/http/ratelimit.py +0 -0
  40. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/http/route.py +0 -0
  41. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/interactions.py +0 -0
  42. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/models/__init__.py +0 -0
  43. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/models/base.py +0 -0
  44. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/models/channel.py +0 -0
  45. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/models/guild.py +0 -0
  46. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/models/member.py +0 -0
  47. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/models/user.py +0 -0
  48. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/state.py +0 -0
  49. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/types.py +0 -0
  50. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/utils.py +0 -0
  51. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself/voice.py +0 -0
  52. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself.egg-info/SOURCES.txt +0 -0
  53. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself.egg-info/dependency_links.txt +0 -0
  54. {modifyself-0.2.0 → modifyself-0.2.2}/modifyself.egg-info/top_level.txt +0 -0
  55. {modifyself-0.2.0 → modifyself-0.2.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modifyself
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: A clean, pythonic Discord self-bot library
5
5
  Author-email: modifyself <modifyself@example.com>
6
6
  License-Expression: MIT
@@ -16,6 +16,8 @@ Requires-Dist: aiohttp>=3.8.0
16
16
  Requires-Dist: plyer>=0.9
17
17
  Requires-Dist: pillow>=10.0
18
18
  Requires-Dist: requests>=2.28
19
+ Requires-Dist: PyNaCl>=1.5.0
20
+ Requires-Dist: opuslib>=3.0.0
19
21
  Dynamic: license-file
20
22
 
21
23
  # modifyself
@@ -1,31 +1,31 @@
1
- # modifyself
2
-
3
- A clean, pythonic Discord self-bot library.
4
-
5
- ## Installation
6
-
7
- pip install modifyself
8
-
9
- ## Quick Example
10
-
11
- rom modifyself import Client, command
12
-
13
- bot = Client(token="your_token")
14
-
15
- @bot.command()
16
- async def ping(ctx):
17
- await ctx.reply("Pong!")
18
-
19
- bot.run()
20
-
21
- ## Features
22
-
23
- - Clean, intuitive API
24
- - Command framework with cogs
25
- - Automatic rate limiting
26
- - Gateway event handling
27
- - Full API coverage
28
-
29
- ## License
30
-
1
+ # modifyself
2
+
3
+ A clean, pythonic Discord self-bot library.
4
+
5
+ ## Installation
6
+
7
+ pip install modifyself
8
+
9
+ ## Quick Example
10
+
11
+ rom modifyself import Client, command
12
+
13
+ bot = Client(token="your_token")
14
+
15
+ @bot.command()
16
+ async def ping(ctx):
17
+ await ctx.reply("Pong!")
18
+
19
+ bot.run()
20
+
21
+ ## Features
22
+
23
+ - Clean, intuitive API
24
+ - Command framework with cogs
25
+ - Automatic rate limiting
26
+ - Gateway event handling
27
+ - Full API coverage
28
+
29
+ ## License
30
+
31
31
  MIT
@@ -0,0 +1,88 @@
1
+ """
2
+ Billing models (payment sources, etc.)
3
+ """
4
+
5
+ from typing import Optional, Dict, Any
6
+ from .base import DiscordObject
7
+
8
+ class PaymentSource(DiscordObject):
9
+ """Represents a payment source (card, PayPal, etc.)."""
10
+
11
+ __slots__ = (
12
+ "type",
13
+ "brand",
14
+ "last_4",
15
+ "expires_month",
16
+ "expires_year",
17
+ "default",
18
+ "invalid",
19
+ "billing_address",
20
+ )
21
+
22
+ def __init__(self, *, state, data: dict):
23
+ super().__init__(state=state, data=data)
24
+ self._update(data)
25
+
26
+ def _update(self, data: dict):
27
+ self.type = data.get("type")
28
+ self.brand = data.get("brand")
29
+ self.last_4 = data.get("last_4")
30
+ self.expires_month = data.get("expires_month")
31
+ self.expires_year = data.get("expires_year")
32
+ self.default = data.get("default", False)
33
+ self.invalid = data.get("invalid", False)
34
+ self.billing_address = data.get("billing_address")
35
+
36
+ @property
37
+ def is_card(self) -> bool:
38
+ return self.type == 1
39
+
40
+ @property
41
+ def is_paypal(self) -> bool:
42
+ return self.type == 2
43
+
44
+ def __repr__(self) -> str:
45
+ return f"<PaymentSource id={self.id} type={self.type} last4={self.last_4}>"
46
+
47
+ class Subscription(DiscordObject):
48
+ """Represents a subscription (Nitro, etc.)."""
49
+
50
+ __slots__ = (
51
+ "type",
52
+ "plan_id",
53
+ "status",
54
+ "current_period_start",
55
+ "current_period_end",
56
+ "renewal_interval",
57
+ "renewal_interval_count",
58
+ "entitlement_id",
59
+ )
60
+
61
+ def __init__(self, *, state, data: dict):
62
+ super().__init__(state=state, data=data)
63
+ self._update(data)
64
+
65
+ def _update(self, data: dict):
66
+ self.type = data.get("type")
67
+ self.plan_id = data.get("plan_id")
68
+ self.status = data.get("status")
69
+ self.current_period_start = data.get("current_period_start")
70
+ self.current_period_end = data.get("current_period_end")
71
+ self.renewal_interval = data.get("renewal_interval")
72
+ self.renewal_interval_count = data.get("renewal_interval_count")
73
+ self.entitlement_id = data.get("entitlement_id")
74
+
75
+ @property
76
+ def is_nitro(self) -> bool:
77
+ return self.type == 1
78
+
79
+ @property
80
+ def is_nitro_classic(self) -> bool:
81
+ return self.type == 2
82
+
83
+ @property
84
+ def is_active(self) -> bool:
85
+ return self.status == 1
86
+
87
+ def __repr__(self) -> str:
88
+ return f"<Subscription id={self.id} type={self.type} status={self.status}>"
@@ -0,0 +1,222 @@
1
+ """
2
+ Discord Message model.
3
+ """
4
+
5
+ from typing import TYPE_CHECKING, Optional, List, Dict, Any, Union
6
+ from datetime import datetime
7
+
8
+ from .base import DiscordObject
9
+ from .user import User
10
+ from .member import Member
11
+ from ..core.enums import MessageType
12
+ from ..utils import parse_time
13
+ from ..components import Component, ActionRow, ComponentType
14
+
15
+ if TYPE_CHECKING:
16
+ from ..state import ConnectionState
17
+
18
+
19
+ class Message(DiscordObject):
20
+ """Represents a Discord message."""
21
+
22
+ __slots__ = (
23
+ "channel_id",
24
+ "author",
25
+ "content",
26
+ "timestamp",
27
+ "edited_timestamp",
28
+ "tts",
29
+ "mention_everyone",
30
+ "mentions",
31
+ "mention_roles",
32
+ "attachments",
33
+ "embeds",
34
+ "reactions",
35
+ "pinned",
36
+ "type",
37
+ "guild_id",
38
+ "member",
39
+ "referenced_message",
40
+ "flags",
41
+ "thread",
42
+ "application",
43
+ "message_reference",
44
+ "interaction",
45
+ "components",
46
+ "sticker_items",
47
+ "position",
48
+ "role_subscription_data",
49
+ "resolved",
50
+ "poll",
51
+ "call",
52
+ "author",
53
+ )
54
+
55
+ def __init__(self, *, state: "ConnectionState", data: dict):
56
+ super().__init__(state=state, data=data)
57
+ self._update(data)
58
+
59
+ def _update(self, data: dict):
60
+ self.id = int(data["id"])
61
+ self.channel_id = int(data["channel_id"])
62
+ self.content = data.get("content", "")
63
+ self.timestamp = parse_time(data["timestamp"])
64
+ self.edited_timestamp = parse_time(data["edited_timestamp"]) if data.get("edited_timestamp") else None
65
+ self.tts = data.get("tts", False)
66
+ self.mention_everyone = data.get("mention_everyone", False)
67
+ self.mentions = [User(state=self._state, data=u) for u in data.get("mentions", [])]
68
+ self.mention_roles = data.get("mention_roles", [])
69
+ self.attachments = data.get("attachments", [])
70
+ self.embeds = data.get("embeds", [])
71
+ self.reactions = data.get("reactions", [])
72
+ self.pinned = data.get("pinned", False)
73
+ self.type = MessageType(data.get("type", 0))
74
+ self.guild_id = int(data["guild_id"]) if data.get("guild_id") else None
75
+ self.flags = data.get("flags", 0)
76
+ self.sticker_items = data.get("sticker_items", [])
77
+ self.position = data.get("position", 0)
78
+ self.role_subscription_data = data.get("role_subscription_data")
79
+ self.resolved = data.get("resolved")
80
+ self.poll = data.get("poll")
81
+ self.call = data.get("call")
82
+
83
+ # Author
84
+ self.author = User(state=self._state, data=data["author"])
85
+
86
+ # Member (if in guild)
87
+ self.member = None
88
+ if data.get("member"):
89
+ self.member = Member(state=self._state, data=data["member"], guild_id=self.guild_id)
90
+
91
+ # Referenced message
92
+ self.referenced_message = None
93
+ if data.get("referenced_message"):
94
+ self.referenced_message = Message(state=self._state, data=data["referenced_message"])
95
+
96
+ # Thread
97
+ self.thread = data.get("thread")
98
+
99
+ # Application
100
+ self.application = data.get("application")
101
+
102
+ # Message reference
103
+ self.message_reference = data.get("message_reference")
104
+
105
+ # Interaction
106
+ self.interaction = data.get("interaction")
107
+
108
+ # Components
109
+ self._components = []
110
+ for comp_data in data.get("components", []):
111
+ if comp_data.get("type") == ComponentType.ACTION_ROW:
112
+ row = ActionRow()
113
+ for child_data in comp_data.get("components", []):
114
+ row.components.append(Component(child_data))
115
+ self._components.append(row)
116
+
117
+ @property
118
+ def jump_url(self) -> str:
119
+ """The jump URL for this message."""
120
+ return f"https://discord.com/channels/{self.guild_id or '@me'}/{self.channel_id}/{self.id}"
121
+
122
+ @property
123
+ def clean_content(self) -> str:
124
+ """The content of the message with mentions cleaned."""
125
+ content = self.content
126
+ for user in self.mentions:
127
+ content = content.replace(f"<@{user.id}>", f"@{user.name}")
128
+ content = content.replace(f"<@!{user.id}>", f"@{user.name}")
129
+ for role_id in self.mention_roles:
130
+ content = content.replace(f"<@&{role_id}>", f"@<role>")
131
+ if self.mention_everyone:
132
+ content = content.replace("@everyone", "@everyone")
133
+ content = content.replace("@here", "@here")
134
+ return content
135
+
136
+ @property
137
+ def components(self) -> List[ActionRow]:
138
+ """The components in this message."""
139
+ return self._components
140
+
141
+ async def edit(
142
+ self,
143
+ content: Optional[str] = None,
144
+ embeds: Optional[List[Dict[str, Any]]] = None,
145
+ components: Optional[List[ActionRow]] = None,
146
+ **kwargs,
147
+ ) -> "Message":
148
+ """Edit this message."""
149
+ payload = {}
150
+ if content is not None:
151
+ payload["content"] = content
152
+ if embeds is not None:
153
+ payload["embeds"] = embeds
154
+ if components is not None:
155
+ payload["components"] = [c.to_dict() for c in components]
156
+ payload.update(kwargs)
157
+
158
+ data = await self._state.http.edit_message(
159
+ self.channel_id,
160
+ self.id,
161
+ **payload,
162
+ )
163
+ self._update(data)
164
+ return self
165
+
166
+ async def delete(self) -> None:
167
+ """Delete this message."""
168
+ await self._state.http.delete_message(self.channel_id, self.id)
169
+
170
+ async def reply(
171
+ self,
172
+ content: Optional[str] = None,
173
+ embeds: Optional[List[Dict[str, Any]]] = None,
174
+ components: Optional[List[ActionRow]] = None,
175
+ **kwargs,
176
+ ) -> "Message":
177
+ """Reply to this message."""
178
+ payload = {
179
+ "message_reference": {"message_id": self.id},
180
+ }
181
+ if content is not None:
182
+ payload["content"] = content
183
+ if embeds is not None:
184
+ payload["embeds"] = embeds
185
+ if components is not None:
186
+ payload["components"] = [c.to_dict() for c in components]
187
+ payload.update(kwargs)
188
+
189
+ data = await self._state.http.send_message(
190
+ self.channel_id,
191
+ **payload,
192
+ )
193
+ return self._state._store_message(data)
194
+
195
+ async def add_reaction(self, emoji: str) -> None:
196
+ """Add a reaction to this message."""
197
+ await self._state.http.add_reaction(self.channel_id, self.id, emoji)
198
+
199
+ async def remove_reaction(self, emoji: str) -> None:
200
+ """Remove a reaction from this message."""
201
+ await self._state.http.remove_reaction(self.channel_id, self.id, emoji)
202
+
203
+ async def clear_reactions(self) -> None:
204
+ """Clear all reactions from this message."""
205
+ await self._state.http.clear_reactions(self.channel_id, self.id)
206
+
207
+ async def pin(self) -> None:
208
+ """Pin this message."""
209
+ await self._state.http.pin_message(self.channel_id, self.id)
210
+
211
+ async def unpin(self) -> None:
212
+ """Unpin this message."""
213
+ await self._state.http.unpin_message(self.channel_id, self.id)
214
+
215
+ async def get_reaction_users(self, emoji: str, limit: int = 100) -> List[User]:
216
+ """Get users who reacted with a specific emoji."""
217
+ # This would need to be implemented in the HTTP client
218
+ # For now, just return an empty list
219
+ return []
220
+
221
+ def __repr__(self) -> str:
222
+ return f"<Message id={self.id} content={self.content[:50]!r}>"
@@ -0,0 +1,47 @@
1
+ """
2
+ Relationship models (friends, blocks, etc.)
3
+ """
4
+
5
+ from typing import Optional, Dict, Any
6
+ from .base import DiscordObject
7
+
8
+ class Relationship(DiscordObject):
9
+ """Represents a relationship (friend, block, etc.)."""
10
+
11
+ __slots__ = (
12
+ "user_id",
13
+ "type",
14
+ "nickname",
15
+ "since",
16
+ "user",
17
+ )
18
+
19
+ def __init__(self, *, state, data: dict):
20
+ super().__init__(state=state, data=data)
21
+ self._update(data)
22
+
23
+ def _update(self, data: dict):
24
+ self.user_id = int(data.get("user_id", 0))
25
+ self.type = data.get("type", 0) # 1=friends, 2=blocked, 3=incoming, 4=outgoing
26
+ self.nickname = data.get("nickname")
27
+ self.since = data.get("since")
28
+ self.user = data.get("user")
29
+
30
+ @property
31
+ def is_friend(self) -> bool:
32
+ return self.type == 1
33
+
34
+ @property
35
+ def is_blocked(self) -> bool:
36
+ return self.type == 2
37
+
38
+ @property
39
+ def is_incoming_request(self) -> bool:
40
+ return self.type == 3
41
+
42
+ @property
43
+ def is_outgoing_request(self) -> bool:
44
+ return self.type == 4
45
+
46
+ def __repr__(self) -> str:
47
+ return f"<Relationship id={self.id} type={self.type}>"
@@ -0,0 +1,54 @@
1
+ """
2
+ User settings models.
3
+ """
4
+
5
+ from typing import Optional, Dict, Any, List
6
+ from .base import DiscordObject
7
+
8
+ class GuildSettings(DiscordObject):
9
+ """Settings for a specific guild."""
10
+
11
+ __slots__ = (
12
+ "guild_id",
13
+ "channel_overwrites",
14
+ "muted",
15
+ "mute_config",
16
+ "mobile_push",
17
+ "message_notifications",
18
+ "flags",
19
+ "notify_highlights",
20
+ )
21
+
22
+ def __init__(self, *, state, data: dict):
23
+ super().__init__(state=state, data=data)
24
+ self._update(data)
25
+
26
+ def _update(self, data: dict):
27
+ self.guild_id = int(data.get("guild_id", 0))
28
+ self.channel_overwrites = data.get("channel_overwrites", {})
29
+ self.muted = data.get("muted", False)
30
+ self.mute_config = data.get("mute_config", {})
31
+ self.mobile_push = data.get("mobile_push", False)
32
+ self.message_notifications = data.get("message_notifications", 0)
33
+ self.flags = data.get("flags", 0)
34
+ self.notify_highlights = data.get("notify_highlights", {})
35
+
36
+ def __repr__(self) -> str:
37
+ return f"<GuildSettings guild_id={self.guild_id}>"
38
+
39
+ class UserSettings:
40
+ """Container for user settings (not a model)."""
41
+
42
+ def __init__(self, data: dict):
43
+ self.theme = data.get("theme", "dark")
44
+ self.locale = data.get("locale", "en-US")
45
+ self.status = data.get("status", "online")
46
+ self.developer_mode = data.get("developer_mode", False)
47
+ self.afk_timeout = data.get("afk_timeout", 600)
48
+ self.animate_emoji = data.get("animate_emoji", True)
49
+ self.animate_stickers = data.get("animate_stickers", True)
50
+ self.enable_tts = data.get("enable_tts", False)
51
+ self.guilds = data.get("guild_settings", {})
52
+
53
+ def __repr__(self) -> str:
54
+ return f"<UserSettings theme={self.theme} status={self.status}>"
@@ -0,0 +1,168 @@
1
+ """
2
+ Discord Webhook models.
3
+ """
4
+
5
+ from typing import TYPE_CHECKING, Optional, List, Dict, Any, Union
6
+ from .base import DiscordObject
7
+ from .user import User
8
+ from .channel import Channel
9
+ from ..utils import parse_time
10
+
11
+ if TYPE_CHECKING:
12
+ from ..state import ConnectionState
13
+
14
+
15
+ class Webhook(DiscordObject):
16
+ """Represents a Discord webhook."""
17
+
18
+ __slots__ = (
19
+ "type",
20
+ "name",
21
+ "avatar",
22
+ "channel_id",
23
+ "guild_id",
24
+ "user",
25
+ "token",
26
+ "source_guild",
27
+ "source_channel",
28
+ "url",
29
+ )
30
+
31
+ def __init__(self, *, state: "ConnectionState", data: dict):
32
+ super().__init__(state=state, data=data)
33
+ self._update(data)
34
+
35
+ def _update(self, data: dict):
36
+ self.id = int(data["id"])
37
+ self.type = data.get("type", 1) # 1=incoming, 2=channel follower
38
+ self.name = data.get("name")
39
+ self.avatar = data.get("avatar")
40
+ self.channel_id = int(data["channel_id"])
41
+ self.guild_id = int(data["guild_id"]) if data.get("guild_id") else None
42
+ self.token = data.get("token")
43
+ self.source_guild = data.get("source_guild")
44
+ self.source_channel = data.get("source_channel")
45
+
46
+ self.user = None
47
+ if data.get("user"):
48
+ self.user = User(state=self._state, data=data["user"])
49
+
50
+ self.url = data.get("url")
51
+
52
+ @property
53
+ def avatar_url(self) -> Optional[str]:
54
+ """The URL of the webhook's avatar."""
55
+ if self.avatar:
56
+ return f"https://cdn.discordapp.com/avatars/{self.id}/{self.avatar}.png"
57
+ return None
58
+
59
+ @property
60
+ def display_name(self) -> str:
61
+ """The display name of the webhook."""
62
+ return self.name or "Unknown Webhook"
63
+
64
+ async def send(
65
+ self,
66
+ content: Optional[str] = None,
67
+ embeds: Optional[List[Dict[str, Any]]] = None,
68
+ username: Optional[str] = None,
69
+ avatar_url: Optional[str] = None,
70
+ tts: bool = False,
71
+ components: Optional[List] = None,
72
+ **kwargs,
73
+ ) -> Dict[str, Any]:
74
+ """Send a message with this webhook."""
75
+ payload = {}
76
+ if content is not None:
77
+ payload["content"] = content
78
+ if embeds is not None:
79
+ payload["embeds"] = embeds
80
+ if username is not None:
81
+ payload["username"] = username
82
+ if avatar_url is not None:
83
+ payload["avatar_url"] = avatar_url
84
+ if tts:
85
+ payload["tts"] = tts
86
+ if components is not None:
87
+ payload["components"] = [c.to_dict() for c in components]
88
+ payload.update(kwargs)
89
+
90
+ url = f"/webhooks/{self.id}/{self.token}"
91
+ return await self._state.http.request(
92
+ method="POST",
93
+ url=url,
94
+ json=payload,
95
+ )
96
+
97
+ async def edit(
98
+ self,
99
+ name: Optional[str] = None,
100
+ avatar: Optional[str] = None,
101
+ channel_id: Optional[int] = None,
102
+ **kwargs,
103
+ ) -> "Webhook":
104
+ """Edit this webhook."""
105
+ payload = {}
106
+ if name is not None:
107
+ payload["name"] = name
108
+ if avatar is not None:
109
+ payload["avatar"] = avatar
110
+ if channel_id is not None:
111
+ payload["channel_id"] = str(channel_id)
112
+ payload.update(kwargs)
113
+
114
+ data = await self._state.http.request(
115
+ method="PATCH",
116
+ url=f"/webhooks/{self.id}",
117
+ json=payload,
118
+ )
119
+ self._update(data)
120
+ return self
121
+
122
+ async def delete(self) -> None:
123
+ """Delete this webhook."""
124
+ await self._state.http.request(
125
+ method="DELETE",
126
+ url=f"/webhooks/{self.id}",
127
+ )
128
+
129
+ def __repr__(self) -> str:
130
+ return f"<Webhook id={self.id} name={self.name!r}>"
131
+
132
+
133
+ class WebhookMessage:
134
+ """A message sent via webhook."""
135
+
136
+ def __init__(self, data: dict):
137
+ self.id = int(data["id"])
138
+ self.channel_id = int(data["channel_id"])
139
+ self.content = data.get("content", "")
140
+ self.timestamp = parse_time(data["timestamp"])
141
+ self.edited_timestamp = parse_time(data["edited_timestamp"]) if data.get("edited_timestamp") else None
142
+ self.embeds = data.get("embeds", [])
143
+ self.attachments = data.get("attachments", [])
144
+ self.components = data.get("components", [])
145
+ self.flags = data.get("flags", 0)
146
+ self.webhook_id = int(data.get("webhook_id", 0))
147
+
148
+ async def edit(
149
+ self,
150
+ content: Optional[str] = None,
151
+ embeds: Optional[List[Dict[str, Any]]] = None,
152
+ components: Optional[List] = None,
153
+ **kwargs,
154
+ ) -> "WebhookMessage":
155
+ """Edit this webhook message."""
156
+ payload = {}
157
+ if content is not None:
158
+ payload["content"] = content
159
+ if embeds is not None:
160
+ payload["embeds"] = embeds
161
+ if components is not None:
162
+ payload["components"] = [c.to_dict() for c in components]
163
+ payload.update(kwargs)
164
+ return self
165
+
166
+ async def delete(self) -> None:
167
+ """Delete this webhook message."""
168
+ pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modifyself
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: A clean, pythonic Discord self-bot library
5
5
  Author-email: modifyself <modifyself@example.com>
6
6
  License-Expression: MIT
@@ -16,6 +16,8 @@ Requires-Dist: aiohttp>=3.8.0
16
16
  Requires-Dist: plyer>=0.9
17
17
  Requires-Dist: pillow>=10.0
18
18
  Requires-Dist: requests>=2.28
19
+ Requires-Dist: PyNaCl>=1.5.0
20
+ Requires-Dist: opuslib>=3.0.0
19
21
  Dynamic: license-file
20
22
 
21
23
  # modifyself
@@ -4,3 +4,5 @@ aiohttp>=3.8.0
4
4
  plyer>=0.9
5
5
  pillow>=10.0
6
6
  requests>=2.28
7
+ PyNaCl>=1.5.0
8
+ opuslib>=3.0.0
@@ -1,30 +1,34 @@
1
- [build-system]
2
- requires = ["setuptools>=61.0", "wheel"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "modifyself"
7
- version = "0.2.0"
8
- description = "A clean, pythonic Discord self-bot library"
9
- readme = "README.md"
10
- license = "MIT"
11
- authors = [
12
- {name = "modifyself", email = "modifyself@example.com"}
13
- ]
14
- requires-python = ">=3.9"
15
- dependencies = [
16
- "websockets>=10.0",
17
- "wreq>=0.1.0",
18
- "aiohttp>=3.8.0",
19
- "plyer>=0.9",
20
- "pillow>=10.0",
21
- "requests>=2.28",
22
- ]
23
-
24
- [project.urls]
25
- Homepage = "https://github.com/clashings/modifyself"
26
- Bug-Reports = "https://github.com/clashings/modifyself/issues"
27
- Source = "https://github.com/clashings/modifyself"
28
-
29
- [tool.setuptools.packages.find]
30
- include = ["modifyself*"]
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "modifyself"
7
+ version = "0.2.2"
8
+ description = "A clean, pythonic Discord self-bot library"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ authors = [
12
+ {name = "modifyself", email = "modifyself@example.com"}
13
+ ]
14
+ requires-python = ">=3.9"
15
+ dependencies = [
16
+ "websockets>=10.0",
17
+ "wreq>=0.1.0",
18
+ "aiohttp>=3.8.0",
19
+ "plyer>=0.9",
20
+ "pillow>=10.0",
21
+ "requests>=2.28",
22
+ "PyNaCl>=1.5.0",
23
+ "opuslib>=3.0.0",
24
+ ]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/clashings/modifyself"
28
+ Bug-Reports = "https://github.com/clashings/modifyself/issues"
29
+ Source = "https://github.com/clashings/modifyself"
30
+
31
+ [tool.setuptools.packages.find]
32
+ where = ["."]
33
+ include = ["modifyself*"]
34
+ exclude = ["tests*", "examples*", "docs*"]
File without changes
@@ -1,180 +0,0 @@
1
- """
2
- Discord Message model.
3
- """
4
-
5
- from typing import TYPE_CHECKING
6
-
7
- from .base import DiscordObject
8
- from .user import User
9
- from ..core.snowflake import Snowflake
10
- from ..utils import parse_time
11
-
12
- if TYPE_CHECKING:
13
- from ..state import ConnectionState
14
- from .member import Member
15
- from .channel import Channel
16
- from .guild import Guild
17
-
18
-
19
- class Message(DiscordObject):
20
- """Represents a message in Discord."""
21
-
22
- __slots__ = (
23
- "channel_id",
24
- "author",
25
- "content",
26
- "timestamp",
27
- "edited_at",
28
- "tts",
29
- "mention_everyone",
30
- "mentions",
31
- "mention_roles",
32
- "attachments",
33
- "embeds",
34
- "reactions",
35
- "pinned",
36
- "type",
37
- "guild_id",
38
- "member",
39
- "referenced_message",
40
- "_cs_channel",
41
- "_cs_guild",
42
- )
43
-
44
- def __init__(self, *, state: "ConnectionState", data: dict):
45
- super().__init__(state=state, data=data)
46
- self._cs_channel = None
47
- self._cs_guild = None
48
- self._update(data)
49
-
50
- def _update(self, data: dict):
51
- self.channel_id = Snowflake(data.get("channel_id", 0))
52
- author_data = data.get("author", {})
53
- self.author = User(state=self._state, data=author_data)
54
- self.content = data.get("content", "")
55
- self.timestamp = parse_time(data["timestamp"]) if "timestamp" in data else None
56
- self.edited_at = (
57
- parse_time(data["edited_timestamp"]) if data.get("edited_timestamp") else None
58
- )
59
- self.tts = data.get("tts", False)
60
- self.mention_everyone = data.get("mention_everyone", False)
61
- self.mentions = [
62
- User(state=self._state, data=u) for u in data.get("mentions", [])
63
- ]
64
- self.mention_roles = [int(r) for r in data.get("mention_roles", [])]
65
- self.attachments = data.get("attachments", [])
66
- self.embeds = data.get("embeds", [])
67
- self.reactions = data.get("reactions", [])
68
- self.pinned = data.get("pinned", False)
69
- self.type = data.get("type", 0)
70
- self.guild_id = (
71
- Snowflake(data["guild_id"]) if data.get("guild_id") else None
72
- )
73
-
74
- # Member info if present
75
- member_data = data.get("member")
76
- if member_data and self.guild_id:
77
- from .member import Member
78
- self.member = Member(
79
- state=self._state, data={**member_data, "user": author_data},
80
- guild_id=self.guild_id
81
- )
82
- else:
83
- self.member = None
84
-
85
- # Referenced message
86
- ref_data = data.get("referenced_message")
87
- if ref_data:
88
- self.referenced_message = Message(state=self._state, data=ref_data)
89
- else:
90
- self.referenced_message = None
91
-
92
- def __repr__(self) -> str:
93
- return f"<Message id={self.id} author={self.author!r} channel={self.channel_id}>"
94
-
95
- def __str__(self) -> str:
96
- return self.content
97
-
98
- @property
99
- def channel(self) -> "Channel | None":
100
- if self._cs_channel is None:
101
- self._cs_channel = self._state._channels.get(self.channel_id)
102
- return self._cs_channel
103
-
104
- @property
105
- def guild(self) -> "Guild | None":
106
- if self.guild_id is None:
107
- return None
108
- if self._cs_guild is None:
109
- self._cs_guild = self._state._guilds.get(self.guild_id)
110
- return self._cs_guild
111
-
112
- @property
113
- def jump_url(self) -> str:
114
- guild_id = self.guild_id or "@me"
115
- return f"https://discord.com/channels/{guild_id}/{self.channel_id}/{self.id}"
116
-
117
- @property
118
- def created_at(self):
119
- return self.id.created_at
120
-
121
- @property
122
- def edited_timestamp(self):
123
- return self.edited_at
124
-
125
- @property
126
- def clean_content(self) -> str:
127
- """The message content with mentions replaced with names."""
128
- content = self.content
129
- for user in self.mentions:
130
- content = content.replace(f"<@{user.id}>", f"@{user.display_name}")
131
- content = content.replace(f"<@!{user.id}>", f"@{user.display_name}")
132
- return content
133
-
134
- async def delete(self, *, delay: float | None = None):
135
- """Delete this message."""
136
- if delay:
137
- import asyncio
138
- await asyncio.sleep(delay)
139
- await self._state.http.delete_message(self.channel_id, self.id)
140
-
141
- async def edit(self, content: str | None = None, **kwargs):
142
- """Edit this message."""
143
- data = await self._state.http.edit_message(
144
- self.channel_id, self.id, content, **kwargs
145
- )
146
- self._update(data)
147
- return self
148
-
149
- async def reply(self, content: str | None = None, **kwargs):
150
- """Reply to this message."""
151
- ref = {
152
- "type": 0,
153
- "message_id": str(self.id),
154
- "channel_id": str(self.channel_id),
155
- }
156
- if self.guild_id:
157
- ref["guild_id"] = str(self.guild_id)
158
- kwargs["message_reference"] = ref
159
- data = await self._state.http.send_message(self.channel_id, content, **kwargs)
160
- return self._state._store_message(data)
161
-
162
- async def add_reaction(self, emoji: str):
163
- """Add a reaction to this message."""
164
- await self._state.http.add_reaction(self.channel_id, self.id, emoji)
165
-
166
- async def remove_reaction(self, emoji: str):
167
- """Remove a reaction from this message."""
168
- await self._state.http.remove_reaction(self.channel_id, self.id, emoji)
169
-
170
- async def clear_reactions(self):
171
- """Clear all reactions from this message."""
172
- await self._state.http.clear_reactions(self.channel_id, self.id)
173
-
174
- async def pin(self):
175
- """Pin this message."""
176
- await self._state.http.pin_message(self.channel_id, self.id)
177
-
178
- async def unpin(self):
179
- """Unpin this message."""
180
- await self._state.http.unpin_message(self.channel_id, self.id)
File without changes
File without changes
File without changes
File without changes
File without changes