NoneBot Plugin Alconna

repository·master·Indexed 40 days ago

https://github.com/nonebot/plugin-alconna

A bridge between the Alconna command parsing library and the NoneBot2 framework. It provides an adapter and tools for advanced command handling, unified cross-platform messaging via UniMessage and Target, and support for 22 different adapters including OneBot, Telegram, Discord, and Feishu. Features include automatic command help, internationalization (i18n), and a powerful session completion mechanism.

Tokens
18.1K
Snippets
46
Records
68
Agent score
49%

What's inside NoneBot Plugin Alconna

  1. Overview of NoneBot Plugin Alconna

    master

    NoneBot Plugin Alconna provides a NoneBot2 adapter and tools for Alconna. It enables advanced command parsing and cross-platform message handling within the NoneBot2 ecosystem.

    Key features include:

    • Full support for Alconna features.
    • Automatic command help information replies.
    • Cross-platform message receiving and sending (both passive and active).
    • Support for message sending and receiving across 22 different adapters.
    • Enhanced session completion mechanism (more powerful than got-reject).
    • Built-in plugins such as echo, help, and lang.
    • Internationalization (i18n) support.
  2. Use MessageSegment annotations for Alconna matching

    master

    The plugin provides specialized annotations in nonebot_plugin_alconna.adapters to match non-text MessageSegment elements (like images or mentions) within an Alconna command.

    • Universal annotations: Located in nonebot_plugin_alconna.adapters (e.g., At for mentions).
    • Adapter-specific annotations: Located in adapter-specific submodules (e.g., Image in nonebot_plugin_alconna.adapters.onebot12).
  3. Cross-platform messaging with UniMessage and uniseg

    master

    The uniseg module provides a set of universal message components for building cross-platform messages that work across different adapters.

    Universal Segments

    You can use these segments in Alconna definitions or UniMessage construction:

    • Text(text: str)
    • At(type: Literal["user", "role", "channel"], target: str)
    • AtAll()
    • Emoji(id: str, name: Optional[str])
    • Image(url, id, path, raw)
    • Audio, Voice, Video, File, Reply, Reference, Card, Other

    Using UniMessage

    UniMessage is a sequence of universal segments. It can be used to:

    • Receive messages: Use UniMsg dependency injection to get a UniMessage from an event.
    • Send messages: Use UniMessage.send() or UniMessage.export() to convert to adapter-specific segments.
    • Chain messages: Use .text(), .at(), .image() for fluent construction, or use + to concatenate strings, segments, and other UniMessage objects.
    • Use Templates: UniMessage.template supports standard formatting and special {:XXX} syntax for segments like {:At(user, target)}.

    Special Template Syntax in AlconnaMatcher

    In AlconnaMatcher, the {:XXX} syntax can access $event and $bot attributes (e.g., {:At(user, $event.get_user_id())}). It also supports $message_id and $target.

    Checking and Iterating

    • Check existence: At(...) in message or message.has(At).
    • Check exclusivity: message.only(Text).
    • Filter: message.include(Text, At) or message.exclude(Reply).
    • Indexing/Slicing: Supports type-based indexing like message[At, 0] or message[Text, 0:2].
    • Extraction: message.extract_plain_text() returns the plain text string.
    from nonebot_plugin_alconna.uniseg import UniMsg, At, Reply
    
    @matcher.handle()
    async def _(msg: UniMsg):
        reply = msg[Reply, 0]
        if msg.has(At):
            ats = msg.get(At)
            print(ats)
    
    # Sending cross-platform
    await test.send(await UniMessage(Image(path="path/to/img")).export())
    
    # Fluent construction
    msg = UniMessage.text("Hello").at("124").image(path="/path/to/img")
  4. How Command Headers work

    master

    A Command Header is the combination of a Prefix and a Command name. Alconna supports various header types:

    PrefixCommandMatch ContentDescription
    -"foo""foo"Pure text header without prefix
    123-123Element header without prefix
    "re:\d{2}"-"32"Regex header without prefix
    int-123 or "456"Type header without prefix
    [int, bool]-True or 123Unnamed element class header
    ["foo", "bar"]-"foo" or "bar"Unnamed pure text header
    ["foo", "bar"]"baz""foobaz" or "barbaz"Pure text header
    [int, bool]"foo"[123, "foo"] or [False, "foo"]Type header
    [123, 4567]"foo"[123, "foo"] or [4567, "foo"]Element header
    [nepattern.NUMBER]"bar"[123, "bar"] or [123.456, "bar"]Expression header
    [123, "foo"]"bar"[123, "foobar"] or [123, "bar"] or ["foo", "bar"]Mixed header
    [(int, "foo"), (456, "bar")]"baz"[123, "foobaz"] or [456, "foobaz"] or [456, "barbaz"]Pair header

    Note: Regex only works on the command name; regex in the prefix is escaped. For a more concise way to use regex, use Bracket Headers.

  5. Use Bracket Headers for concise regex matching

    master

    Bracket Headers use a syntax similar to Python f-strings ({name:type}) to declare matching patterns within the command name.

    • {} or {:}: Matches any content (placeholder).
    • {foo}: Named group (?P<foo>.+).
    • {foo:int}: Named group (?P<foo>\d+) where int is converted to a BasePattern.

    This allows you to define dynamic command names directly in the Alconna constructor.

    from alconna import Alconna
    
    alc = Alconna(".rd{roll:int}")
    assert alc.parse(".rd123").header["roll"] == 123
  6. Install nonebot-plugin-alconna

    master

    You can install the plugin using pip or the nb-cli.

    pip install nonebot-plugin-alconna
    # or
    nb plugin install nonebot-plugin-alconna
  7. Use cross-platform messaging with UniMessage and Target

    master

    The plugin provides UniMessage for constructing cross-platform messages and Target for sending messages to specific entities (like groups or users) regardless of the underlying adapter. You can use SupportScope to specify the platform scope for active messaging.

    To send an image to a specific group actively:

    1. Use Target.group(group_id, scope) to define the destination.
    2. Use .send() on the target with a UniMessage object.

    To handle message recall:

    • Use message_recall() within a handler to trigger recall logic.
    • When sending a message via UniMessage.send(), the returned object may have a .recallable attribute and a .recall(delay=..., index=...) method.
    from nonebot import get_driver
    from nonebot_plugin_alconna import Target, UniMessage, SupportScope, on_alconna, message_recall
    
    driver = get_driver()
    test = on_alconna("test")
    
    @test.handle()
    async def handle_test():
        # Sending an image and handling recall
        r = await UniMessage.image(path="path/to/img").send()
        if r.recallable:
            await r.reply("图片已发送,10秒后撤回")
            await r.recall(delay=10, index=0)
    
    @test.got("foo", prompt=UniMessage.template("{:Reply($message_id)}请输入图片"))
    async def handle_foo():
        # Using message_recall to trigger recall in a handler
        await test.send("图片已收到, 即将撤回")
        await message_recall()
    
    @driver.on_startup
    async def _():
        # Active sending to a group using Target
        await Target.group("123456789", SupportScope.qq_client).send(UniMessage.image(path="test.png"))
  8. Compare and verify Targets

    master

    Two Target objects can be compared for equality using the == operator. The comparison checks if they represent the same recipient by verifying:

    • id matches
    • channel status matches
    • private status matches
    • parent_id matches (if provided)
    • self_id matches (if provided)
    • adapter matches (if provided in extra)

    You can also use the verify(other) method to perform this check explicitly.

  9. Customize Alconna behavior with Extension

    master

    The Extension class is the base for customizing how Alconna interacts with NoneBot. By subclassing Extension, you can override specific lifecycle hooks to modify message handling, permission checks, context provision, or dependency injection.

    Extensions are executed in order of their priority property. When multiple extensions override the same method, they are processed according to the internal ExtensionExecutor logic (e.g., some methods like receive_wrapper or send_wrapper act as a chain, while others like message_provider or output_converter return the first non-None/non-empty result).

    from nonebot_plugin_alconna.extension import Extension
    
    class MyCustomExtension(Extension):
        @property
        def priority(self) -> int:
            return 10
    
        @property
        def id(self) -> str:
            return "my_custom_ext"
    
        async def permission_check(self, bot, event, medium) -> bool:
            # Custom logic to allow/deny commands
            return True
    
        async def send_wrapper(self, bot, event, send) -> str | Message | UniMessage:
            # Modify the message before it is sent
            return send
  10. Configure NoneBot Plugin Alconna via environment variables

    master

    You can configure the global behavior of the plugin using the following environment variables:

    VariableDescription
    ALCONNA_AUTO_SEND_OUTPUTWhether to globally enable automatic sending of output information.
    ALCONNA_USE_COMMAND_STARTWhether to use COMMAND_START as the global command prefix.
    ALCONNA_GLOBAL_COMPLETIONGlobal completion session configuration (does not imply global enablement).
    ALCONNA_USE_ORIGINWhether to globally use the original message (unprocessed by to_me, etc.).
    ALCONNA_USE_PARAMWhether to use specialized Param for better dependency injection.
    ALCONNA_USE_CMD_SEPWhether to use COMMAND_SEP as the global command separator.
    ALCONNA_GLOBAL_EXTENSIONSGlobal extensions to load, paths separated by dots (e.g., foo.bar.baz:DemoExtension).
    ALCONNA_CONTEXT_STYLEGlobal command context interpolation style: None to disable, bracket for {...}, or parentheses for $(...).
    ALCONNA_ENABLE_SAA_PATCHWhether to enable the SAA patch.
    ALCONNA_APPLY_FILEHOSTWhether to enable file hosting.
    ALCONNA_APPLY_FETCH_TARGETSWhether to pull the list of send targets once at startup.
    ALCONNA_BUILTIN_PLUGINSA collection of built-in Alconna plugins to load.
    ALCONNA_CONFLICT_RESOLVERStrategy for resolving command conflicts. Options: default (keep both), raise (throw exception), ignore (ignore new command), or replace (replace old command).
    ALCONNA_RESPONSE_SELFWhether to allow responding to your own messages.
    ALCONNA_CACHE_MESSAGEWhether to cache messages.
  11. Configure Namespaces

    master

    Namespaces allow you to group Alconna settings (like prefixes, separators, and formatters). By default, Alconna uses the "Alconna" namespace.

    Creating and using a Namespace

    You can create a custom Namespace and assign it to an Alconna instance, or use a context manager to apply settings to all commands created within that block.

    from arclet.alconna import Alconna, namespace, Namespace, config
    
    # Create a namespace that requires a '/' prefix
    ns = Namespace("foo", prefixes=["/"])
    
    # Use the namespace in Alconna
    alc = Alconna("pip", namespace=ns)
    
    # Or use a context manager to inject settings into all commands in the block
    with namespace("bar") as np1:
        np1.prefixes = ["!"]
        # Commands created here will use the 'bar' namespace

    Managing Namespaces

    • Global Config: Use config.namespaces["name"] = ns to register a namespace.
    • Default Namespace: Modify config.default_namespace to change settings for all commands without an explicit namespace.
    from arclet.alconna import Alconna, namespace, Namespace, config
    
    ns = Namespace("foo", prefixes=["/"])
    
    alc = Alconna("pip", Subcommand("install", Args["package", str]), namespace=ns)
    
    with namespace("bar") as np1:
        np1.prefixes = ["!"]
        np1.formatter_type = ShellTextFormatter
        np1.builtin_option_name["help"] = {"帮助", "-h"}
    
    config.namespaces["foo"] = ns
  12. Implement a basic echo command with @Command

    master

    You can create simple commands using the @Command decorator. By using .build(auto_send_output=True), the command will automatically send its output. You can use UniMessage as a type hint in your handler to receive the command content.

    # echo 插件
    from nonebot_plugin_alconna import UniMessage, Command
    
    @Command("echo <...content>").build(auto_send_output=True).handle()
    async def _(content: UniMessage):
        await content.finish()