Metadata-Version: 2.4
Name: illest.py
Version: 2.1.0
Summary: Write bots for ILLEST the way you write them for discord: discord.py's shape, end-to-end encrypted.
Author: VERAKS
License-Expression: MIT
Project-URL: Homepage, https://illest.world
Keywords: illest,bot,discord.py,chat,e2ee,mls
Classifier: Development Status :: 5 - Production/Stable
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Communications :: Chat
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: websockets>=12.0
Requires-Dist: cryptography>=42.0
Requires-Dist: illest-crypto<3,>=2.1
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
Provides-Extra: docs
Requires-Dist: mkdocs>=1.6; extra == "docs"
Requires-Dist: mkdocs-material>=9.5; extra == "docs"
Requires-Dist: mkdocstrings[python]>=0.26; extra == "docs"
Dynamic: license-file

# illest.py

Write bots for [ILLEST](https://illest.world) the way you write them for discord. `illest.py` has discord.py's shape: the same
classes, decorators, events and arguments. Code written for discord.py 2.x usually runs once its import is changed.

```bash
pip install illest.py
```

Python 3.10 or newer. The encryption engine, `illest-crypto`, comes with it as a compiled wheel for Windows and Linux (x86-64
and ARM64).

## A bot in twenty lines

```python
import illest
from illest import app_commands
from illest.ext import commands

bot = commands.Bot(command_prefix="!", intents=illest.Intents.default())


@bot.event
async def setup_hook():
    await bot.tree.sync()  # registers the slash commands below


@bot.event
async def on_ready():
    print(f"signed in as {bot.user}")


@bot.command()
async def ping(ctx: commands.Context):
    await ctx.reply("pong")


@bot.tree.command(description="Roll a die")
@app_commands.describe(sides="How many sides it has")
async def roll(interaction: illest.Interaction, sides: app_commands.Range[int, 2, 100] = 6):
    import random
    await interaction.response.send_message(f"{random.randint(1, sides)}")


bot.run("illb_...")
```

Make the bot and its token in ILLEST under **Settings → Developers**. Open the bot there, copy its invite link from
**Installation**, and add it to a server you manage.

To run against your own server instead of `chat.illest.world`, set `ILLEST_GATEWAY_URL` (for example
`ws://localhost:8766/gateway`) or pass `server_url=` to the client.

## What you get

| discord.py | illest.py |
|---|---|
| `Client`, `commands.Bot`, `Intents`, `@bot.event`, `wait_for`, `setup_hook` | the same |
| `ext.commands`: commands, groups, cogs, extensions, checks, cooldowns, converters, `Greedy`, `Range`, flags, the help command, hybrid commands | the same |
| `app_commands`: `CommandTree`, `@tree.command`, `Group`, `describe`, `choices`, `autocomplete`, `rename`, `Range`, `Transform`, context menus, `default_permissions`, `guild_only`, checks and cooldowns, `tree.sync(guild=...)` | the same |
| `Interaction`: `response.send_message`, `defer` (with "thinking"), `edit_message`, `send_modal`, `followup`, `original_response` | the same |
| `ui`: `View`, `Button`, `Select`, `UserSelect`, `RoleSelect`, `ChannelSelect`, `MentionableSelect`, `Modal`, `TextInput`, persistent views (`timeout=None` and `custom_id`, `bot.add_view`) | the same |
| `ext.tasks.loop` with `before_loop`, `after_loop`, `change_interval`, times of day | the same |
| `Embed`, `File`, `AllowedMentions`, `Colour`, `Permissions`, `PermissionOverwrite`, `utils.get/find/format_dt/oauth_url` | the same |
| Moderation: `Member.ban/kick/timeout/add_roles/remove_roles/edit`, `Guild.unban/bans/audit_logs`, `TextChannel.purge`, creating and editing channels, categories, roles and invites | the same, behind the bot's scopes and the server's roles |
| Rate limits, reconnects, `run()`'s logging set-up | handled for you, as discord.py does |

## Where it differs, and why

ILLEST is end-to-end encrypted: the server stores ciphertext and holds no keys. Most differences follow from that, and each is
deliberate. The full list, with what to write instead, is in [the documentation](docs/differences.md).

- **A bot is a device.** Its token enrols a device the first time it runs; the device key is kept under `~/.illest/bots/`. Keep
  that folder: losing it is losing the device.
- **A bot reads from when it joined.** `channel.history()` and `fetch_message()` return the messages this bot holds, from when it
  was let into the channel's group. There is nothing older to fetch: the server cannot read it either.
- **Commands, presses, picks and forms travel sealed**, as messages in the conversation that only the bot opens. The server never
  sees which command was used or with what.
- **`ephemeral=True` is a rendering rule.** Every honest client shows the message to its one reader only; inside an encrypted
  group nothing stronger exists, and the library does not pretend otherwise.
- **Who sent it is what their device proves.** `message.author_verified` and `interaction.sender_verified` say whether the
  sender's own device vouched for the account. Authorise nothing on a sender that is not verified.
- **Permissions have discord's names and ILLEST's bits.** `illest.Permissions(ban_members=True).value` is the number to use in an
  ILLEST invite link; a number from a discord permissions calculator means something else here.
- **Intents filter, scopes grant.** An intent decides which events reach your handlers; the bot's scopes (set by its owner under
  Developers → Bot) decide what it may read and do. `Intents.default()` includes members and message content.
- **No voice.** A bot cannot join a call: calls are encrypted from each person's device. `VoiceChannel.connect()` raises.
- **No sharding.** One connection serves any number of servers; `AutoShardedBot` is `Bot`.
- **`tree.sync()` takes effect at once**, global commands included.

## Examples

`examples/` holds runnable bots: [basic](examples/basic_bot.py) (events and prefix commands), [slash commands](examples/slash_commands.py)
(groups, choices, autocomplete, context menus), [views](examples/views.py) (buttons, select menus, a persistent view, a modal),
[moderation](examples/moderation.py) and [tasks and cogs](examples/tasks_and_cogs.py). Each reads its token from `ILLEST_TOKEN`.

## Documentation

The API reference is built with MkDocs: `pip install "illest.py[docs]"`, then `mkdocs serve` in this folder.

## Licence

MIT. `illest-crypto`, the compiled encryption engine it installs, is free to use and redistribute unmodified; see its own licence.
