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.
- illest-2.2.0/LICENSE +24 -0
- illest-2.2.0/PKG-INFO +151 -0
- illest-2.2.0/README.md +112 -0
- illest-2.2.0/illest/__init__.py +169 -0
- illest-2.2.0/illest/_limits.py +240 -0
- illest-2.2.0/illest/_version.py +3 -0
- illest-2.2.0/illest/abc.py +257 -0
- illest-2.2.0/illest/activity.py +100 -0
- illest-2.2.0/illest/app_commands/__init__.py +76 -0
- illest-2.2.0/illest/app_commands/checks.py +161 -0
- illest-2.2.0/illest/app_commands/commands.py +807 -0
- illest-2.2.0/illest/app_commands/errors.py +130 -0
- illest-2.2.0/illest/app_commands/namespace.py +31 -0
- illest-2.2.0/illest/app_commands/tree.py +341 -0
- illest-2.2.0/illest/attachment.py +161 -0
- illest-2.2.0/illest/auth.py +286 -0
- illest-2.2.0/illest/buttons.py +160 -0
- illest-2.2.0/illest/client.py +2272 -0
- illest-2.2.0/illest/colour.py +156 -0
- illest-2.2.0/illest/content.py +470 -0
- illest-2.2.0/illest/embeds.py +362 -0
- illest-2.2.0/illest/enums.py +249 -0
- illest-2.2.0/illest/envelope.py +97 -0
- illest-2.2.0/illest/errors.py +165 -0
- illest-2.2.0/illest/ext/__init__.py +1 -0
- illest-2.2.0/illest/ext/commands/__init__.py +127 -0
- illest-2.2.0/illest/ext/commands/bot.py +538 -0
- illest-2.2.0/illest/ext/commands/cog.py +174 -0
- illest-2.2.0/illest/ext/commands/converter.py +412 -0
- illest-2.2.0/illest/ext/commands/core.py +1164 -0
- illest-2.2.0/illest/ext/commands/errors.py +343 -0
- illest-2.2.0/illest/ext/commands/flags.py +156 -0
- illest-2.2.0/illest/ext/commands/help.py +382 -0
- illest-2.2.0/illest/ext/commands/hybrid.py +153 -0
- illest-2.2.0/illest/ext/tasks/__init__.py +391 -0
- illest-2.2.0/illest/file.py +59 -0
- illest-2.2.0/illest/gateway.py +513 -0
- illest-2.2.0/illest/intents.py +190 -0
- illest-2.2.0/illest/interactions.py +564 -0
- illest-2.2.0/illest/media.py +182 -0
- illest-2.2.0/illest/mentions.py +41 -0
- illest-2.2.0/illest/mls.py +843 -0
- illest-2.2.0/illest/models.py +2562 -0
- illest-2.2.0/illest/object.py +70 -0
- illest-2.2.0/illest/permissions.py +317 -0
- illest-2.2.0/illest/pins.py +152 -0
- illest-2.2.0/illest/py.typed +0 -0
- illest-2.2.0/illest/ratelimit.py +113 -0
- illest-2.2.0/illest/replies.py +34 -0
- illest-2.2.0/illest/rpc/__init__.py +45 -0
- illest-2.2.0/illest/rpc/_async.py +355 -0
- illest-2.2.0/illest/rpc/_sync.py +164 -0
- illest-2.2.0/illest/rpc/_wire.py +223 -0
- illest-2.2.0/illest/rpc/errors.py +66 -0
- illest-2.2.0/illest/ui/__init__.py +52 -0
- illest-2.2.0/illest/ui/button.py +126 -0
- illest-2.2.0/illest/ui/components.py +54 -0
- illest-2.2.0/illest/ui/item.py +69 -0
- illest-2.2.0/illest/ui/layout.py +417 -0
- illest-2.2.0/illest/ui/modal.py +636 -0
- illest-2.2.0/illest/ui/select.py +290 -0
- illest-2.2.0/illest/ui/view.py +249 -0
- illest-2.2.0/illest/utils.py +217 -0
- illest-2.2.0/illest.egg-info/PKG-INFO +151 -0
- illest-2.2.0/illest.egg-info/SOURCES.txt +91 -0
- illest-2.2.0/illest.egg-info/dependency_links.txt +1 -0
- illest-2.2.0/illest.egg-info/requires.txt +12 -0
- illest-2.2.0/illest.egg-info/top_level.txt +1 -0
- illest-2.2.0/pyproject.toml +63 -0
- illest-2.2.0/setup.cfg +4 -0
- illest-2.2.0/tests/test_attachment.py +294 -0
- illest-2.2.0/tests/test_bot_handles.py +14 -0
- illest-2.2.0/tests/test_buttons.py +247 -0
- illest-2.2.0/tests/test_catch_up_by_itself.py +388 -0
- illest-2.2.0/tests/test_commands.py +130 -0
- illest-2.2.0/tests/test_commands_framework.py +346 -0
- illest-2.2.0/tests/test_content.py +101 -0
- illest-2.2.0/tests/test_discord_shape.py +687 -0
- illest-2.2.0/tests/test_envelope.py +42 -0
- illest-2.2.0/tests/test_events.py +193 -0
- illest-2.2.0/tests/test_forms.py +168 -0
- illest-2.2.0/tests/test_gateway.py +295 -0
- illest-2.2.0/tests/test_group_spelling.py +49 -0
- illest-2.2.0/tests/test_layout.py +180 -0
- illest-2.2.0/tests/test_limits.py +167 -0
- illest-2.2.0/tests/test_listed_admission.py +237 -0
- illest-2.2.0/tests/test_missed_messages.py +401 -0
- illest-2.2.0/tests/test_pins.py +94 -0
- illest-2.2.0/tests/test_pins_in_use.py +306 -0
- illest-2.2.0/tests/test_reach.py +95 -0
- illest-2.2.0/tests/test_rpc.py +314 -0
- illest-2.2.0/tests/test_secret_files.py +86 -0
- 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
|
+
]
|