Lightbulb
repository·master·Indexed 42 days ago
https://github.com/tandemdude/hikari-lightbulbAn easy-to-use command handler library designed to integrate with Hikari, a Discord API wrapper for Python. Lightbulb provides a structured way to create and register SlashCommands, UserCommands, and MessageCommands, including support for command options and hierarchical command groups. As of version 3, prefix command support has been removed.
What's inside Lightbulb
- Lightbulb is a command handler library specifically designed to work with Hikari. It provides a high-level, easy-to-use interface for building and managing commands for Discord bots using the Hikari framework.
Use Ephemeral Dependencies for runtime registration
masterBecause registries are frozen once a container is created, you cannot add new dependencies to a registry after the fact. However, you can add ephemeral dependencies directly to an active container.
Ephemeral dependencies follow the lifecycle of the container and are destroyed when the container is closed. Use these for objects that are only known at runtime (like a specific command context).
Methods to add ephemeral dependencies:
.Container.add_value(type, value).Container.add_factory(type, factory)
Accessing DI Containers
masterWhen a container is active, it can be injected using the type hint
linkd.containers.Container. This will return the most-recently activated container for the current context.Lightbulb provides specific container types for different contexts:
lightbulb.di.DefaultContainer: The default global container.lightbulb.di.CommandContainer: The container for a command execution.lightbulb.di.AutocompleteContainer: The container for an autocomplete flow.lightbulb.di.ListenerContainer: The container for a listener.lightbulb.di.TaskContainer: The container for a task.
You can inject these containers to register ephemeral dependencies that only exist within that specific context.
import linkd.containers @lightbulb.invoke async def invoke(self, ctx: lightbulb.Context, container: linkd.containers.Container): # container will be a lightbulb.di.CommandContainer in this context ...How the Dependency Injection context hierarchy works
masterLightbulb's DI system uses a hierarchical context model. Dependencies registered in a base context are inherited by all derived contexts.
lightbulb.di.Contexts.DEFAULT: The base context. Dependencies registered here are 'globally' available during all Lightbulb-controlled flows.lightbulb.di.Contexts.COMMAND: Available only during command execution (including hooks and error handlers).lightbulb.di.Contexts.AUTOCOMPLETE: Available only during autocomplete handler execution.lightbulb.di.Contexts.LISTENER: Available only during listener execution (specifically for listeners registered toLoaders).lightbulb.di.Contexts.TASK: Available only during task execution.
When a 'Flow' (like a command or task) starts, a flow-specific context is created, inheriting from the
DEFAULTcontext. This allows you to define dependencies that are scoped specifically to the lifecycle of a single command or task.Register dependencies with dependencies (Factory Injection)
masterWhen registering a dependency via a factory, the factory method can require other registered dependencies. Lightbulb will resolve and provide those dependencies before calling your factory.
Factory Requirements: To ensure successful resolution, every parameter in your factory method:
- MUST NOT have a default value, unless the default is exactly
lightbulb.di.INJECTED. - MUST NOT be positional-only, var positional (
*args), or var keyword (**kwargs). - SHOULD have a type-hint. If no type-hint is provided, Lightbulb attempts to resolve the dependency by the parameter name (this only applies to factory methods; for injectable methods, type-hints are mandatory).
import aiohttp import lightbulb class Config: base_url: str client = lightbulb.client_from_app(...) registry = client.di.registry_for(lightbulb.di.Contexts.DEFAULT) registry.register_value(Config, Config()) # The factory 'create_client_session' depends on 'Config' def create_client_session(cfg: Config) -> aiohttp.ClientSession: return aiohttp.ClientSession(cfg.base_url) registry.register_factory(aiohttp.ClientSession, create_client_session)- MUST NOT have a default value, unless the default is exactly
Modify library behavior with Feature Flags
masterLightbulb includes afeaturesmodule containing feature flags. These flags allow you to modify various portions of the library's behavior to better suit your specific use case. You can enable these flags when creating yourClientinstance. For a list of available flags, refer to thelightbulb.featuresmodule.Configure Lightbulb using files and environment variables
masterLightbulb supports parsing configuration fromyaml,toml, orjsonfiles into Python objects usingmsgspec. The configuration system also supports environment variable substitution evaluation. For detailed schema and usage information, refer to thelightbulb.configmodule documentation.Implement error handlers for command execution
masterError handlers allow your bot to respond to errors occurring during command execution (or within command hooks) instead of simply failing silently.
Key Constraints:
- Handlers only apply to errors raised during command execution. For errors occurring during listener invocation, you must create a listener for Hikari's
ExceptionEventinstead. - An error handler must be an
asyncfunction. - The first argument must be an instance of
lightbulb.exceptions.ExecutionPipelineFailedException. - The function must return a boolean:
- Return
Trueif the error was handled (this stops the error from propagating to other handlers). - Return
Falseif the error should be passed to the next handler in the hierarchy.
- Return
async def example_error_handler(exc: lightbulb.exceptions.ExecutionPipelineFailedException) -> bool: # Return True if the error was handled, otherwise False. return True- Handlers only apply to errors raised during command execution. For errors occurring during listener invocation, you must create a listener for Hikari's
Declare commands in Lightbulb
masterCommands are defined by subclassing one of the provided command base classes. Each command is represented by a single class.
Available command bases:
lightbulb.SlashCommand: For Discord slash commands.lightbulb.MessageCommand: For message context menu commands.lightbulb.UserCommand: For user context menu commands.
When declaring a
SlashCommand, you must provide anameand adescriptionas named parameters in the class definition.class YourCommand(lightbulb.SlashCommand, name="test-command", description="a test slash command"): ...Use Union and Optional types for dependency fallbacks
masterLightbulb supports Python type unions and
typing.Optionalto provide fallback mechanisms when a dependency is missing.- Unions (
TypeA | TypeB): Lightbulb checks dependencies in the order specified. IfTypeAis not registered, it attempts to provideTypeB. - Optional (
TypeA | NoneorOptional[TypeA]): IfTypeAis not registered, the parameter is set toNoneinstead of raising an error.
Advanced Resolution with
Try[]: Uselightbulb.di.Tryto change the behavior so that if a dependency is registered but its creation fails, Lightbulb will fall back to the next type in the union sequence.Note: Using
TypeAwithout a meta-annotation is functionally equivalent toIf[TypeA].from lightbulb.di import Try # If Bar fails to initialize, it falls back to Baz async def dependency_factory(foo: Try[Bar] | Baz) -> Bork: ... # If Bar is not registered at all, foo is None async def optional_factory(foo: Bar | None) -> Baz: ...- Unions (
Override dependencies in child containers
masterDependency overriding occurs when a child container (e.g., a
COMMANDcontext) defines a dependency of the same type as one in its parent container (e.g.,DEFAULT).Resolution Rules:
- When resolving a dependency, the container first looks in its own scope.
- It can look into its child containers if needed.
- It never looks into its parent containers for dependencies (it only looks up the hierarchy if the current container doesn't have it, but a child's override prevents the parent's version from being seen by the child).
- Scope of Override: An overridden dependency is only accessible to the child container and any of its own children. Parent containers cannot access the overridden value.
Handle components and modals
masterLightbulb provides a specialized handler for processing component and modal interactions. This abstraction simplifies handling these interactions compared to using raw Hikari code. For detailed implementation guides and API references, refer to thelightbulb.componentssubpackage documentation.