Disagreement

A Python library for interacting with the Discord API, with a focus on bot development.

Features

  • Internationalization helpers
  • Hybrid context for commands
  • Built-in rate limiting
  • Asynchronous design using aiohttp
  • Gateway and HTTP API clients
  • Slash command framework
  • Message component helpers
  • Message.jump_url property for quick links to messages
  • Built-in caching layer
  • Experimental voice support
  • Helpful error handling utilities

Installation

python -m pip install -U pip
pip install disagreement
# or install from source for development
pip install -e .

Requires Python 3.10 or newer.

To run the example scripts, you'll need the python-dotenv package to load environment variables. Install the development extras with:

pip install "disagreement[dev]"

Basic Usage

import asyncio
import os

import disagreement
from disagreement.ext import commands
from dotenv import load_dotenv
load_dotenv()


class Basics(commands.Cog):
    def __init__(self, client: disagreement.Client) -> None:
        super().__init__(client)

    @commands.command()
    async def ping(self, ctx: commands.CommandContext) -> None:
        await ctx.reply(f"Pong! Gateway Latency: {self.client.latency_ms} ms.")


token = os.getenv("DISCORD_BOT_TOKEN")
if not token:
    raise RuntimeError("DISCORD_BOT_TOKEN environment variable not set")

intents = disagreement.GatewayIntent.default() | disagreement.GatewayIntent.MESSAGE_CONTENT
client = disagreement.Client(token=token, command_prefix="!", intents=intents, mention_replies=True)
async def main() -> None:
    client.add_cog(Basics(client))
    await client.run()


if __name__ == "__main__":
    asyncio.run(main())

Global Error Handling

To ensure unexpected errors don't crash your bot, you can enable the library's global error handler:

import disagreement

disagreement.setup_global_error_handler()

Call this early in your program to log unhandled exceptions instead of letting them terminate the process.

Configuring Logging

Use :func:disagreement.logging_config.setup_logging to configure logging for your bot. The helper accepts a logging level and an optional file path.

import logging
from disagreement.logging_config import setup_logging

setup_logging(logging.INFO)
# Or log to a file
setup_logging(logging.DEBUG, file="bot.log")

HTTP Session Options

Pass additional keyword arguments to aiohttp.ClientSession using the http_options parameter when constructing :class:disagreement.Client:

client = disagreement.Client(
    token=token,
    http_options={"proxy": "http://localhost:8080"},
)

These options are forwarded to HTTPClient when it creates the underlying aiohttp.ClientSession. You can specify a custom connector or any other session parameter supported by aiohttp.

Default Allowed Mentions

Specify default mention behaviour for all outgoing messages when constructing the client:

from disagreement.models import AllowedMentions
client = disagreement.Client(
    token=token,
    allowed_mentions=AllowedMentions.none().to_dict(),
)

This dictionary is used whenever send_message or helpers like Message.reply are called without an explicit allowed_mentions argument.

Defining Subcommands with AppCommandGroup

from disagreement.ext.app_commands import AppCommandGroup, slash_command
from disagreement.ext.app_commands.context import AppCommandContext

settings_group = AppCommandGroup("settings", "Manage settings")
admin_group = AppCommandGroup("admin", "Admin settings", parent=settings_group)


@slash_command(name="show", description="Display a setting.", parent=settings_group)
async def show(ctx: AppCommandContext, key: str):
    ...


@slash_command(name="set", description="Update a setting.", parent=admin_group)
async def set_setting(ctx: AppCommandContext, key: str, value: str):
    ...

Fetching Guilds

Use Client.fetch_guild to retrieve a guild from the Discord API if it isn't already cached. This is useful when working with guild IDs from outside the gateway events.

guild = await client.fetch_guild("123456789012345678")
roles = await client.fetch_roles(guild.id)

Call Client.fetch_guilds to list all guilds the current user has access to.

guilds = await client.fetch_guilds()
for g in guilds:
    print(g.name)

Sharding

To run your bot across multiple gateway shards, pass shard_count when creating the client:

client = disagreement.Client(token=BOT_TOKEN, shard_count=2)

If you want the library to determine the recommended shard count automatically, use AutoShardedClient:

client = disagreement.AutoShardedClient(token=BOT_TOKEN)

See examples/sharded_bot.py for a full example.

Contributing

Contributions are welcome! Please open an issue or submit a pull request.

See the docs directory for detailed guides on components, slash commands, caching, and voice features.

License

This project is licensed under the BSD 3-Clause license. See the LICENSE file for details.

Description
No description provided
Readme BSD-3-Clause 12 MiB
Languages
Python 100%