illest 2.2.0__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 (93) hide show
  1. illest-2.2.0/LICENSE +24 -0
  2. illest-2.2.0/PKG-INFO +151 -0
  3. illest-2.2.0/README.md +112 -0
  4. illest-2.2.0/illest/__init__.py +169 -0
  5. illest-2.2.0/illest/_limits.py +240 -0
  6. illest-2.2.0/illest/_version.py +3 -0
  7. illest-2.2.0/illest/abc.py +257 -0
  8. illest-2.2.0/illest/activity.py +100 -0
  9. illest-2.2.0/illest/app_commands/__init__.py +76 -0
  10. illest-2.2.0/illest/app_commands/checks.py +161 -0
  11. illest-2.2.0/illest/app_commands/commands.py +807 -0
  12. illest-2.2.0/illest/app_commands/errors.py +130 -0
  13. illest-2.2.0/illest/app_commands/namespace.py +31 -0
  14. illest-2.2.0/illest/app_commands/tree.py +341 -0
  15. illest-2.2.0/illest/attachment.py +161 -0
  16. illest-2.2.0/illest/auth.py +286 -0
  17. illest-2.2.0/illest/buttons.py +160 -0
  18. illest-2.2.0/illest/client.py +2272 -0
  19. illest-2.2.0/illest/colour.py +156 -0
  20. illest-2.2.0/illest/content.py +470 -0
  21. illest-2.2.0/illest/embeds.py +362 -0
  22. illest-2.2.0/illest/enums.py +249 -0
  23. illest-2.2.0/illest/envelope.py +97 -0
  24. illest-2.2.0/illest/errors.py +165 -0
  25. illest-2.2.0/illest/ext/__init__.py +1 -0
  26. illest-2.2.0/illest/ext/commands/__init__.py +127 -0
  27. illest-2.2.0/illest/ext/commands/bot.py +538 -0
  28. illest-2.2.0/illest/ext/commands/cog.py +174 -0
  29. illest-2.2.0/illest/ext/commands/converter.py +412 -0
  30. illest-2.2.0/illest/ext/commands/core.py +1164 -0
  31. illest-2.2.0/illest/ext/commands/errors.py +343 -0
  32. illest-2.2.0/illest/ext/commands/flags.py +156 -0
  33. illest-2.2.0/illest/ext/commands/help.py +382 -0
  34. illest-2.2.0/illest/ext/commands/hybrid.py +153 -0
  35. illest-2.2.0/illest/ext/tasks/__init__.py +391 -0
  36. illest-2.2.0/illest/file.py +59 -0
  37. illest-2.2.0/illest/gateway.py +513 -0
  38. illest-2.2.0/illest/intents.py +190 -0
  39. illest-2.2.0/illest/interactions.py +564 -0
  40. illest-2.2.0/illest/media.py +182 -0
  41. illest-2.2.0/illest/mentions.py +41 -0
  42. illest-2.2.0/illest/mls.py +843 -0
  43. illest-2.2.0/illest/models.py +2562 -0
  44. illest-2.2.0/illest/object.py +70 -0
  45. illest-2.2.0/illest/permissions.py +317 -0
  46. illest-2.2.0/illest/pins.py +152 -0
  47. illest-2.2.0/illest/py.typed +0 -0
  48. illest-2.2.0/illest/ratelimit.py +113 -0
  49. illest-2.2.0/illest/replies.py +34 -0
  50. illest-2.2.0/illest/rpc/__init__.py +45 -0
  51. illest-2.2.0/illest/rpc/_async.py +355 -0
  52. illest-2.2.0/illest/rpc/_sync.py +164 -0
  53. illest-2.2.0/illest/rpc/_wire.py +223 -0
  54. illest-2.2.0/illest/rpc/errors.py +66 -0
  55. illest-2.2.0/illest/ui/__init__.py +52 -0
  56. illest-2.2.0/illest/ui/button.py +126 -0
  57. illest-2.2.0/illest/ui/components.py +54 -0
  58. illest-2.2.0/illest/ui/item.py +69 -0
  59. illest-2.2.0/illest/ui/layout.py +417 -0
  60. illest-2.2.0/illest/ui/modal.py +636 -0
  61. illest-2.2.0/illest/ui/select.py +290 -0
  62. illest-2.2.0/illest/ui/view.py +249 -0
  63. illest-2.2.0/illest/utils.py +217 -0
  64. illest-2.2.0/illest.egg-info/PKG-INFO +151 -0
  65. illest-2.2.0/illest.egg-info/SOURCES.txt +91 -0
  66. illest-2.2.0/illest.egg-info/dependency_links.txt +1 -0
  67. illest-2.2.0/illest.egg-info/requires.txt +12 -0
  68. illest-2.2.0/illest.egg-info/top_level.txt +1 -0
  69. illest-2.2.0/pyproject.toml +63 -0
  70. illest-2.2.0/setup.cfg +4 -0
  71. illest-2.2.0/tests/test_attachment.py +294 -0
  72. illest-2.2.0/tests/test_bot_handles.py +14 -0
  73. illest-2.2.0/tests/test_buttons.py +247 -0
  74. illest-2.2.0/tests/test_catch_up_by_itself.py +388 -0
  75. illest-2.2.0/tests/test_commands.py +130 -0
  76. illest-2.2.0/tests/test_commands_framework.py +346 -0
  77. illest-2.2.0/tests/test_content.py +101 -0
  78. illest-2.2.0/tests/test_discord_shape.py +687 -0
  79. illest-2.2.0/tests/test_envelope.py +42 -0
  80. illest-2.2.0/tests/test_events.py +193 -0
  81. illest-2.2.0/tests/test_forms.py +168 -0
  82. illest-2.2.0/tests/test_gateway.py +295 -0
  83. illest-2.2.0/tests/test_group_spelling.py +49 -0
  84. illest-2.2.0/tests/test_layout.py +180 -0
  85. illest-2.2.0/tests/test_limits.py +167 -0
  86. illest-2.2.0/tests/test_listed_admission.py +237 -0
  87. illest-2.2.0/tests/test_missed_messages.py +401 -0
  88. illest-2.2.0/tests/test_pins.py +94 -0
  89. illest-2.2.0/tests/test_pins_in_use.py +306 -0
  90. illest-2.2.0/tests/test_reach.py +95 -0
  91. illest-2.2.0/tests/test_rpc.py +314 -0
  92. illest-2.2.0/tests/test_secret_files.py +86 -0
  93. illest-2.2.0/tests/test_sender.py +221 -0
illest-2.2.0/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 VERAKS
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ The ILLEST name and marks are trademarks of VERAKS and are not licensed by
24
+ this notice.
illest-2.2.0/PKG-INFO ADDED
@@ -0,0 +1,151 @@
1
+ Metadata-Version: 2.4
2
+ Name: illest
3
+ Version: 2.2.0
4
+ Summary: The Python library for ILLEST: bots with commands, slash commands and buttons, end-to-end encrypted, and Rich Presence.
5
+ Author: VERAKS
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://illest.world
8
+ Project-URL: Documentation, https://developer.illest.world/docs/introduction
9
+ Project-URL: Changelog, https://developer.illest.world/docs/changelog
10
+ Keywords: illest,illest.py,bot,chat bot,rich presence,e2ee,mls
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Framework :: AsyncIO
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: Communications :: Chat
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.10
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: websockets>=12.0
29
+ Requires-Dist: cryptography>=42.0
30
+ Requires-Dist: illest-crypto<3,>=2.1
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest>=8.0; extra == "dev"
33
+ Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
34
+ Provides-Extra: docs
35
+ Requires-Dist: mkdocs>=1.6; extra == "docs"
36
+ Requires-Dist: mkdocs-material>=9.5; extra == "docs"
37
+ Requires-Dist: mkdocstrings[python]>=0.26; extra == "docs"
38
+ Dynamic: license-file
39
+
40
+ # illest.py
41
+
42
+ The Python library for [ILLEST](https://illest.world): bots with prefix commands, slash commands, buttons, menus and forms, all
43
+ end-to-end encrypted, and Rich Presence for games and tools that show what their player is doing.
44
+
45
+ ```bash
46
+ pip install illest
47
+ ```
48
+
49
+ Python 3.10 or newer. The encryption engine, `illest-crypto`, comes with it as a compiled wheel for Windows and Linux (x86-64
50
+ and ARM64).
51
+
52
+ ## A bot in twenty lines
53
+
54
+ ```python
55
+ import illest
56
+ from illest import app_commands
57
+ from illest.ext import commands
58
+
59
+ bot = commands.Bot(command_prefix="!", intents=illest.Intents.default())
60
+
61
+
62
+ @bot.event
63
+ async def setup_hook():
64
+ await bot.tree.sync() # registers the slash commands below
65
+
66
+
67
+ @bot.event
68
+ async def on_ready():
69
+ print(f"signed in as {bot.user}")
70
+
71
+
72
+ @bot.command()
73
+ async def ping(ctx: commands.Context):
74
+ await ctx.reply("pong")
75
+
76
+
77
+ @bot.tree.command(description="Roll a die")
78
+ @app_commands.describe(sides="How many sides it has")
79
+ async def roll(interaction: illest.Interaction, sides: app_commands.Range[int, 2, 100] = 6):
80
+ import random
81
+ await interaction.response.send_message(f"{random.randint(1, sides)}")
82
+
83
+
84
+ bot.run("illb_...")
85
+ ```
86
+
87
+ Make the bot and its token in ILLEST under **Settings → Developers**. Open the bot there, copy its invite link from
88
+ **Installation**, and add it to a server you manage.
89
+
90
+ To run against your own server instead of `chat.illest.world`, set `ILLEST_GATEWAY_URL` (for example
91
+ `ws://localhost:8766/gateway`) or pass `server_url=` to the client.
92
+
93
+ ## What you get
94
+
95
+ - **The client:** `Client`, `commands.Bot`, `Intents`, `@bot.event`, `wait_for`, `setup_hook`.
96
+ - **Prefix commands** (`ext.commands`): commands, groups, cogs, extensions, checks, cooldowns, converters, `Greedy`, `Range`,
97
+ flags, the help command, and hybrid commands that are slash commands too.
98
+ - **Slash commands** (`app_commands`): `CommandTree`, `@tree.command`, `Group`, `describe`, `choices`, `autocomplete`, `rename`,
99
+ `Range`, `Transform`, context menus, `default_permissions`, `guild_only`, checks and cooldowns, `tree.sync(guild=...)`.
100
+ - **Interactions:** `response.send_message`, `defer` (with "thinking"), `edit_message`, `send_modal`, `followup`,
101
+ `original_response`.
102
+ - **Components** (`ui`): `View`, `Button`, `Select`, `UserSelect`, `RoleSelect`, `ChannelSelect`, `MentionableSelect`, `Modal`,
103
+ `TextInput`, and persistent views (`timeout=None` and `custom_id`, `bot.add_view`).
104
+ - **Background tasks** (`ext.tasks.loop`) with `before_loop`, `after_loop`, `change_interval` and times of day.
105
+ - **Messages:** `Embed`, `File`, `AllowedMentions`, `Colour`, `Permissions`, `PermissionOverwrite`,
106
+ `utils.get/find/format_dt/oauth_url`.
107
+ - **Moderation:** `Member.ban/kick/timeout/add_roles/remove_roles/edit`, `Guild.unban/bans/audit_logs`, `TextChannel.purge`,
108
+ and creating and editing channels, categories, roles and invites, behind the bot's scopes and the server's roles.
109
+ - **Rich Presence** (`illest.rpc`): `Presence` and `AsyncPresence` tell the person's friends what they are doing in your game or
110
+ tool, through the ILLEST desktop app.
111
+ - Rate limits, reconnects and `run()`'s logging set-up, handled for you.
112
+
113
+ ## What end-to-end encryption changes
114
+
115
+ ILLEST is end-to-end encrypted: the server stores ciphertext and holds no keys. A bot is built for that, and each of these is
116
+ deliberate. Moving a bot from discord.py? [The migration guide](docs/differences.md) has the full list, with what to write instead.
117
+
118
+ - **A bot is a device.** Its token enrols a device the first time it runs; the device key is kept under `~/.illest/bots/`. Keep
119
+ that folder: losing it is losing the device.
120
+ - **A bot reads from when it joined.** `channel.history()` and `fetch_message()` return the messages this bot holds, from when it
121
+ was let into the channel's group. There is nothing older to fetch: the server cannot read it either.
122
+ - **Commands, presses, picks and forms travel sealed**, as messages in the conversation that only the bot opens. The server never
123
+ sees which command was used or with what.
124
+ - **`ephemeral=True` is a rendering rule.** Every honest client shows the message to its one reader only; inside an encrypted
125
+ group nothing stronger exists, and the library does not pretend otherwise.
126
+ - **Who sent it is what their device proves.** `message.author_verified` and `interaction.sender_verified` say whether the
127
+ sender's own device vouched for the account. Authorise nothing on a sender that is not verified.
128
+ - **Permissions are ILLEST's bits.** `illest.Permissions(ban_members=True).value` is the number to use in an ILLEST invite
129
+ link; build it with `illest.Permissions` or the bot's Installation page rather than copying a number from elsewhere.
130
+ - **Intents filter, scopes grant.** An intent decides which events reach your handlers; the bot's scopes (set by its owner under
131
+ Developers → Bot) decide what it may read and do. `Intents.default()` includes members and message content.
132
+ - **No voice.** A bot cannot join a call: calls are encrypted from each person's device. `VoiceChannel.connect()` raises.
133
+ - **No sharding.** One connection serves any number of servers; `AutoShardedBot` is `Bot`.
134
+ - **`tree.sync()` takes effect at once**, global commands included.
135
+
136
+ ## Examples
137
+
138
+ `examples/` holds runnable bots: [basic](examples/basic_bot.py) (events and prefix commands), [slash commands](examples/slash_commands.py)
139
+ (groups, choices, autocomplete, context menus), [views](examples/views.py) (buttons, select menus, a persistent view, a modal),
140
+ [moderation](examples/moderation.py), [tasks and cogs](examples/tasks_and_cogs.py), and [a test bot](examples/test_bot.py) that
141
+ touches every part once. Each reads its token from `ILLEST_TOKEN`. [Rich Presence](examples/rich_presence.py) reads an application
142
+ id from `ILLEST_APP_ID` instead.
143
+
144
+ ## Documentation
145
+
146
+ The documentation is at [developer.illest.world](https://developer.illest.world/docs/introduction). To build it here with
147
+ MkDocs: `pip install "illest[docs]"`, then `mkdocs serve` in this folder.
148
+
149
+ ## Licence
150
+
151
+ MIT. `illest-crypto`, the compiled encryption engine it installs, is free to use and redistribute unmodified; see its own licence.
illest-2.2.0/README.md ADDED
@@ -0,0 +1,112 @@
1
+ # illest.py
2
+
3
+ The Python library for [ILLEST](https://illest.world): bots with prefix commands, slash commands, buttons, menus and forms, all
4
+ end-to-end encrypted, and Rich Presence for games and tools that show what their player is doing.
5
+
6
+ ```bash
7
+ pip install illest
8
+ ```
9
+
10
+ Python 3.10 or newer. The encryption engine, `illest-crypto`, comes with it as a compiled wheel for Windows and Linux (x86-64
11
+ and ARM64).
12
+
13
+ ## A bot in twenty lines
14
+
15
+ ```python
16
+ import illest
17
+ from illest import app_commands
18
+ from illest.ext import commands
19
+
20
+ bot = commands.Bot(command_prefix="!", intents=illest.Intents.default())
21
+
22
+
23
+ @bot.event
24
+ async def setup_hook():
25
+ await bot.tree.sync() # registers the slash commands below
26
+
27
+
28
+ @bot.event
29
+ async def on_ready():
30
+ print(f"signed in as {bot.user}")
31
+
32
+
33
+ @bot.command()
34
+ async def ping(ctx: commands.Context):
35
+ await ctx.reply("pong")
36
+
37
+
38
+ @bot.tree.command(description="Roll a die")
39
+ @app_commands.describe(sides="How many sides it has")
40
+ async def roll(interaction: illest.Interaction, sides: app_commands.Range[int, 2, 100] = 6):
41
+ import random
42
+ await interaction.response.send_message(f"{random.randint(1, sides)}")
43
+
44
+
45
+ bot.run("illb_...")
46
+ ```
47
+
48
+ Make the bot and its token in ILLEST under **Settings → Developers**. Open the bot there, copy its invite link from
49
+ **Installation**, and add it to a server you manage.
50
+
51
+ To run against your own server instead of `chat.illest.world`, set `ILLEST_GATEWAY_URL` (for example
52
+ `ws://localhost:8766/gateway`) or pass `server_url=` to the client.
53
+
54
+ ## What you get
55
+
56
+ - **The client:** `Client`, `commands.Bot`, `Intents`, `@bot.event`, `wait_for`, `setup_hook`.
57
+ - **Prefix commands** (`ext.commands`): commands, groups, cogs, extensions, checks, cooldowns, converters, `Greedy`, `Range`,
58
+ flags, the help command, and hybrid commands that are slash commands too.
59
+ - **Slash commands** (`app_commands`): `CommandTree`, `@tree.command`, `Group`, `describe`, `choices`, `autocomplete`, `rename`,
60
+ `Range`, `Transform`, context menus, `default_permissions`, `guild_only`, checks and cooldowns, `tree.sync(guild=...)`.
61
+ - **Interactions:** `response.send_message`, `defer` (with "thinking"), `edit_message`, `send_modal`, `followup`,
62
+ `original_response`.
63
+ - **Components** (`ui`): `View`, `Button`, `Select`, `UserSelect`, `RoleSelect`, `ChannelSelect`, `MentionableSelect`, `Modal`,
64
+ `TextInput`, and persistent views (`timeout=None` and `custom_id`, `bot.add_view`).
65
+ - **Background tasks** (`ext.tasks.loop`) with `before_loop`, `after_loop`, `change_interval` and times of day.
66
+ - **Messages:** `Embed`, `File`, `AllowedMentions`, `Colour`, `Permissions`, `PermissionOverwrite`,
67
+ `utils.get/find/format_dt/oauth_url`.
68
+ - **Moderation:** `Member.ban/kick/timeout/add_roles/remove_roles/edit`, `Guild.unban/bans/audit_logs`, `TextChannel.purge`,
69
+ and creating and editing channels, categories, roles and invites, behind the bot's scopes and the server's roles.
70
+ - **Rich Presence** (`illest.rpc`): `Presence` and `AsyncPresence` tell the person's friends what they are doing in your game or
71
+ tool, through the ILLEST desktop app.
72
+ - Rate limits, reconnects and `run()`'s logging set-up, handled for you.
73
+
74
+ ## What end-to-end encryption changes
75
+
76
+ ILLEST is end-to-end encrypted: the server stores ciphertext and holds no keys. A bot is built for that, and each of these is
77
+ deliberate. Moving a bot from discord.py? [The migration guide](docs/differences.md) has the full list, with what to write instead.
78
+
79
+ - **A bot is a device.** Its token enrols a device the first time it runs; the device key is kept under `~/.illest/bots/`. Keep
80
+ that folder: losing it is losing the device.
81
+ - **A bot reads from when it joined.** `channel.history()` and `fetch_message()` return the messages this bot holds, from when it
82
+ was let into the channel's group. There is nothing older to fetch: the server cannot read it either.
83
+ - **Commands, presses, picks and forms travel sealed**, as messages in the conversation that only the bot opens. The server never
84
+ sees which command was used or with what.
85
+ - **`ephemeral=True` is a rendering rule.** Every honest client shows the message to its one reader only; inside an encrypted
86
+ group nothing stronger exists, and the library does not pretend otherwise.
87
+ - **Who sent it is what their device proves.** `message.author_verified` and `interaction.sender_verified` say whether the
88
+ sender's own device vouched for the account. Authorise nothing on a sender that is not verified.
89
+ - **Permissions are ILLEST's bits.** `illest.Permissions(ban_members=True).value` is the number to use in an ILLEST invite
90
+ link; build it with `illest.Permissions` or the bot's Installation page rather than copying a number from elsewhere.
91
+ - **Intents filter, scopes grant.** An intent decides which events reach your handlers; the bot's scopes (set by its owner under
92
+ Developers → Bot) decide what it may read and do. `Intents.default()` includes members and message content.
93
+ - **No voice.** A bot cannot join a call: calls are encrypted from each person's device. `VoiceChannel.connect()` raises.
94
+ - **No sharding.** One connection serves any number of servers; `AutoShardedBot` is `Bot`.
95
+ - **`tree.sync()` takes effect at once**, global commands included.
96
+
97
+ ## Examples
98
+
99
+ `examples/` holds runnable bots: [basic](examples/basic_bot.py) (events and prefix commands), [slash commands](examples/slash_commands.py)
100
+ (groups, choices, autocomplete, context menus), [views](examples/views.py) (buttons, select menus, a persistent view, a modal),
101
+ [moderation](examples/moderation.py), [tasks and cogs](examples/tasks_and_cogs.py), and [a test bot](examples/test_bot.py) that
102
+ touches every part once. Each reads its token from `ILLEST_TOKEN`. [Rich Presence](examples/rich_presence.py) reads an application
103
+ id from `ILLEST_APP_ID` instead.
104
+
105
+ ## Documentation
106
+
107
+ The documentation is at [developer.illest.world](https://developer.illest.world/docs/introduction). To build it here with
108
+ MkDocs: `pip install "illest[docs]"`, then `mkdocs serve` in this folder.
109
+
110
+ ## Licence
111
+
112
+ MIT. `illest-crypto`, the compiled encryption engine it installs, is free to use and redistribute unmodified; see its own licence.
@@ -0,0 +1,169 @@
1
+ """illest: the Python library for ILLEST, for bots and Rich Presence.
2
+
3
+ from illest.ext import commands
4
+
5
+ bot = commands.Bot(command_prefix="/", token="illb_...")
6
+
7
+ @bot.command(name="ping")
8
+ async def ping(ctx):
9
+ await ctx.reply("pong")
10
+
11
+ bot.run()
12
+
13
+ ## The everyday names
14
+
15
+ `Client`, `Intents`, `Embed`, `ButtonStyle`, `ui.Button`, `ext.commands.Bot`,
16
+ `Context`, `@bot.event`, `@bot.command()`, `ctx.send/reply`,
17
+ `message.reply`, `embed=`, `view=`, `ephemeral=True`. Moving a bot written
18
+ for another library: the migration guide on the developer site.
19
+
20
+ ## What is different, and why
21
+
22
+ This network is end-to-end encrypted. The server stores ciphertext and holds
23
+ no keys, so:
24
+
25
+ * **A bot is a real device.** Its token ENROLs it; its device key (generated
26
+ once, kept under `~/.illest/bots/`) is what proves it at sign-in. Losing
27
+ that file is losing the device, which is losing history only it held.
28
+ * **A bot joins groups like a member.** Invite it to your server; somebody's
29
+ client lets it into each channel's group. "Invited" and "able to read" are
30
+ different moments: usually seconds apart while it is online.
31
+ * **A bot that was stopped catches up by itself.** While it is away its groups move on (members join and leave, keys are
32
+ replaced). When it starts, and when it signs in again after the server no longer held its session, it brings every group it
33
+ holds up to the server's epoch, a channel at a time and unhurried, before it handles anything; a channel it cannot catch up is
34
+ logged by its id and the rest go on. A message that arrives sealed at an epoch ahead of the bot's makes it catch that channel up
35
+ once and try the message again; if that fails too the message is dropped, and logged without its content. Messages sent while the
36
+ bot was away are not fetched: it reads what arrives while it is connected.
37
+ * **Who sent it is what their device proves, not what the server says.** Every message and every button press carries
38
+ `author_id` (`sender_id` on an `Interaction`) from the server, and the library checks it against the sender's leaf in the group:
39
+ a frame that names an account other than the one the leaf proves is dropped. A sender whose leaf proves no account (a bot, or
40
+ a device with no certificate) is still delivered, but `Message.author_verified` (`Interaction.sender_verified`,
41
+ `Context.author_verified`) is False, and `author_id` is then only the server's word: do not authorise anything by it.
42
+ * **An account is the key it first showed.** A certificate proves that some account key vouched for a device, and anybody can
43
+ make a key. So the library remembers the first key each account shows (in `pins.json`, in its state folder) and holds the
44
+ account to it: an invitation written by a device under another key is turned down, a member the server lists who lets
45
+ themselves in under another key is not taken in, and a message from a device under another key is delivered with
46
+ `author_verified` False and `author_key_changed` True. The change is said once in the log and to `on_key_change(user_id)`.
47
+ It happens when a person sets their account up again, and when somebody else writes as them: ask them another way, then call
48
+ `client.accept_key_change(user_id)`. An invitation is opened only from a device the channel lists.
49
+ * **A button answers only to who could see it.** `@bot.on_button` runs only for a press on a message this bot sent, naming a
50
+ button that message carried and left enabled, from somebody who could see it (and, for a message meant for one reader, who is
51
+ proven to be that reader). The library remembers its buttons in its state folder, so they survive a restart.
52
+ * **Ephemeral is a rendering rule.** `ephemeral=True` marks a message
53
+ visible only to its one reader on every client that honours the rule. It
54
+ is not cryptography against other members of the group (no such thing
55
+ exists inside an MLS group), and this library says so rather than implying
56
+ otherwise.
57
+ """
58
+
59
+ from ._version import __version__
60
+ from . import abc, app_commands, ui, utils
61
+ from .activity import Activity, BaseActivity, CustomActivity, Game, Streaming
62
+ from .attachment import AttachmentError
63
+ from .client import Client
64
+ from .colour import Color, Colour
65
+ from .content import Button, ContentError
66
+ from .embeds import Embed
67
+ from .enums import * # noqa: F401,F403
68
+ from .enums import __all__ as _enums
69
+ from .errors import (
70
+ AuthFailed,
71
+ BadRequest,
72
+ BotForbidden,
73
+ ClientException,
74
+ ConnectionClosed,
75
+ DiscordException,
76
+ DiscordServerError,
77
+ Forbidden,
78
+ GatewayError,
79
+ GatewayNotFound,
80
+ HTTPException,
81
+ IllestException,
82
+ InteractionResponded,
83
+ InvalidData,
84
+ LoginFailure,
85
+ MissingScope,
86
+ NotFound,
87
+ PrivilegedIntentsRequired,
88
+ RateLimited,
89
+ ServerError,
90
+ StaleEpoch,
91
+ UnknownAction,
92
+ )
93
+ from .file import File
94
+ from .replies import AskReply
95
+ from .ui.layout import MediaGalleryItem, SeparatorSpacing, UnfurledMediaItem
96
+ from .intents import Intents
97
+ from .interactions import Interaction, InteractionCallbackResponse, InteractionResponse, Webhook, WebhookMessage
98
+ from .mentions import AllowedMentions
99
+ from .models import * # noqa: F401,F403
100
+ from .models import __all__ as _models
101
+ from .object import Object
102
+ from .permissions import PermissionOverwrite, Permissions
103
+ from .ui.select import SelectOption
104
+ from .utils import setup_logging
105
+
106
+ #: The scopes a bot token can hold, as the server names them (`actions::bots::SCOPES`).
107
+ SCOPES = ("messages.read", "messages.write", "guilds.read", "members.read", "moderate", "guilds.manage")
108
+
109
+ __all__ = [
110
+ "SCOPES",
111
+ "AllowedMentions",
112
+ "Activity",
113
+ "AttachmentError",
114
+ "AuthFailed",
115
+ "BadRequest",
116
+ "BaseActivity",
117
+ "BotForbidden",
118
+ "Button",
119
+ "Client",
120
+ "ClientException",
121
+ "Color",
122
+ "Colour",
123
+ "ConnectionClosed",
124
+ "ContentError",
125
+ "CustomActivity",
126
+ "DiscordException",
127
+ "DiscordServerError",
128
+ "AskReply",
129
+ "Embed",
130
+ "MediaGalleryItem",
131
+ "SeparatorSpacing",
132
+ "UnfurledMediaItem",
133
+ "File",
134
+ "Forbidden",
135
+ "Game",
136
+ "GatewayError",
137
+ "GatewayNotFound",
138
+ "HTTPException",
139
+ "IllestException",
140
+ "Intents",
141
+ "Interaction",
142
+ "InteractionCallbackResponse",
143
+ "InteractionResponded",
144
+ "InteractionResponse",
145
+ "InvalidData",
146
+ "LoginFailure",
147
+ "MissingScope",
148
+ "NotFound",
149
+ "Object",
150
+ "PermissionOverwrite",
151
+ "Permissions",
152
+ "SelectOption",
153
+ "PrivilegedIntentsRequired",
154
+ "RateLimited",
155
+ "ServerError",
156
+ "StaleEpoch",
157
+ "Streaming",
158
+ "UnknownAction",
159
+ "Webhook",
160
+ "WebhookMessage",
161
+ "__version__",
162
+ "abc",
163
+ "app_commands",
164
+ "setup_logging",
165
+ "ui",
166
+ "utils",
167
+ *_enums,
168
+ *_models,
169
+ ]