From dd9392036978575172faa1f9352d2548076bcff8 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 15:25:41 +0000 Subject: [PATCH 01/17] Add English documentation Co-Authored-By: Junyan Qin --- docs/.vitepress/config.mts | 401 +++++++------- docs/en/deploy/command.md | 163 ++++++ docs/en/deploy/models/readme.md | 113 ++++ docs/en/deploy/pipelines/readme.md | 148 ++++++ docs/en/deploy/platforms/readme.md | 20 + docs/en/deploy/update.md | 93 ++++ docs/en/develop/comp-arch.md | 96 ++++ docs/en/develop/dev-config.md | 160 ++++++ docs/en/index.md | 3 + docs/en/insight/community.md | 24 + docs/en/insight/guide.md | 49 ++ docs/en/plugin/dev/api-ref.md | 612 ++++++++++++++++++++++ docs/en/plugin/dev/messages.md | 338 ++++++++++++ docs/en/plugin/dev/tutor.md | 298 +++++++++++ docs/en/plugin/plugin-intro.md | 84 +++ docs/en/workshop/dify-service-api.md | 176 +++++++ docs/en/workshop/impl-platform-adapter.md | 389 ++++++++++++++ 17 files changed, 2952 insertions(+), 215 deletions(-) create mode 100644 docs/en/deploy/command.md create mode 100644 docs/en/deploy/models/readme.md create mode 100644 docs/en/deploy/pipelines/readme.md create mode 100644 docs/en/deploy/platforms/readme.md create mode 100644 docs/en/deploy/update.md create mode 100644 docs/en/develop/comp-arch.md create mode 100644 docs/en/develop/dev-config.md create mode 100644 docs/en/index.md create mode 100644 docs/en/insight/community.md create mode 100644 docs/en/insight/guide.md create mode 100644 docs/en/plugin/dev/api-ref.md create mode 100644 docs/en/plugin/dev/messages.md create mode 100644 docs/en/plugin/dev/tutor.md create mode 100644 docs/en/plugin/plugin-intro.md create mode 100644 docs/en/workshop/dify-service-api.md create mode 100644 docs/en/workshop/impl-platform-adapter.md diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index bf6695eeb..8a2088264 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -210,225 +210,196 @@ export default defineConfig({ ], }, }, - // en: { - // label: "English", - // lang: "en", - // link: "/en/", - // themeConfig: { - // // https://vitepress.dev/reference/default-theme-config + en: { + label: "English", + lang: "en", + link: "/en/", + themeConfig: { + // https://vitepress.dev/reference/default-theme-config - // nav: [ - // { text: "Home", link: "/en/" }, - // { text: "Insight", link: "/en/insight/guide" }, - // { text: "Deploy", link: "/en/deploy/langbot/manual" }, - // { text: "Plugin", link: "/en/plugin/plugin-intro" }, - // ], + nav: [ + { text: "Home", link: "https://langbot.app" }, + { text: "Roadmap", link: "https://langbot.featurebase.app/roadmap" }, + ], - // sidebar: [ - // { - // text: "Insight", - // items: [ - // { text: "Getting Started", link: "/en/insight/guide" }, - // { text: "Introduction", link: "/en/insight/intro" }, - // { text: "Community Resources", link: "/en/insight/community" }, - // ], - // }, - // { - // text: "Deploy", - // items: [ - // { - // text: "Deploy LangBot", - // collapsed: true, - // items: [ - // { - // text: "Deploy with Baota Panel", - // link: "/en/deploy/langbot/one-click/bt", - // }, - // { - // text: "Deploy with Docker", - // link: "/en/deploy/langbot/docker", - // }, - // { - // text: "Manual Deployment", - // link: "/en/deploy/langbot/manual", - // }, - // ], - // }, - // { - // text: "Deploy Message Platforms", - // collapsed: true, - // items: [ - // { - // text: "aiocqhttp", - // collapsed: true, - // items: [ - // { - // text: "NapCat", - // link: "/en/deploy/platforms/aiocqhttp/napcat", - // }, - // { - // text: "Lagrange", - // link: "/en/deploy/platforms/aiocqhttp/lagrange", - // }, - // { - // text: "llonebot", - // link: "/en/deploy/platforms/aiocqhttp/llonebot", - // }, - // { - // text: "shamrock", - // link: "/en/deploy/platforms/aiocqhttp/shamrock", - // }, - // ], - // }, - // { - // text: "go-cqhttp", - // link: "/en/deploy/platforms/gocq", - // }, - // { - // text: "QQ Official", - // link: "/en/deploy/platforms/official", - // }, - // ], - // }, - // { - // text: "Fill in the Configuration", - // link: "/en/deploy/quick-config/config", - // }, - // { - // text: "Network Configuration", - // link: "/en/deploy/network-details", - // }, - // { - // text: "Update LangBot", - // link: "/en/deploy/update", - // }, - // ], - // }, + sidebar: [ + { + text: "Overview", + items: [ + { text: "Getting Started (Must Read)", link: "/en/insight/guide" }, + { text: "Community Resources", link: "/en/insight/community" }, + ], + }, + { + text: "Deployment and Usage", + items: [ + { + text: "Deploy LangBot", + collapsed: true, + items: [ + { text: "Docker Deployment", link: "/en/deploy/langbot/docker" }, + { + text: "Baota Panel Deployment", + link: "/en/deploy/langbot/one-click/bt", + }, + { text: "Manual Deployment", link: "/en/deploy/langbot/manual" }, + ], + }, + { + text: "Configure Bots", + collapsed: true, + link: "/en/deploy/platforms/readme", + items: [ + { + text: "OneBot v11", + collapsed: true, + items: [ + { + text: "NapCat", + link: "/en/deploy/platforms/qq/aiocqhttp/napcat", + }, + { + text: "Lagrange", + link: "/en/deploy/platforms/qq/aiocqhttp/lagrange", + }, + { + text: "llonebot", + link: "/en/deploy/platforms/qq/aiocqhttp/llonebot", + }, + ], + }, + { + text: "QQ Official Bot", + collapsed: true, + items: [ + { + text: "Webhook Method", + link: "/en/deploy/platforms/qq/official_webhook", + }, + { + text: "WebSocket Method", + link: "/en/deploy/platforms/qq/official", + }, + ], + }, + { + text: "Personal WeChat", + collapsed: true, + items: [ + { + text: "Gewechat", + link: "/en/deploy/platforms/wechat/gewechat", + }, + ], + }, + { + text: "WeCom (Enterprise WeChat)", + collapsed: true, + items: [ + { + text: "Internal Application", + link: "/en/deploy/platforms/wecom/wecom", + }, + { + text: "External Customer Service", + link: "/en/deploy/platforms/wecom/wecomcs", + }, + ], + }, + { text: "WeChat Official Account", link: "/en/deploy/platforms/wxoa" }, + { text: "Feishu (Lark)", link: "/en/deploy/platforms/lark" }, + { text: "DingTalk", link: "/en/deploy/platforms/dingtalk" }, + { text: "Discord", link: "/en/deploy/platforms/discord" }, + { text: "Telegram", link: "/en/deploy/platforms/telegram" }, + { text: "Slack", link: "/en/deploy/platforms/slack" }, + ], + }, + { + text: "Configure Dialogue Models", + link: "/en/deploy/models/readme", + }, + { + text: "Modify Dialogue Pipeline", + link: "/en/deploy/pipelines/readme", + }, + { + text: "System Environment Settings", + link: "/en/deploy/settings", + }, + { + text: "Dialogue Commands", + link: "/en/deploy/command", + }, + { + text: "Update LangBot", + link: "/en/deploy/update", + }, + ], + }, + { + text: "Plugins", + items: [ + { text: "Plugin Introduction", link: "/en/plugin/plugin-intro" }, + { + text: "Plugin Development", + collapsed: true, + items: [ + { text: "Basic Tutorial", link: "/en/plugin/dev/tutor" }, + { text: "Message Platform Entities", link: "/en/plugin/dev/messages" }, + { text: "API Reference", link: "/en/plugin/dev/api-ref" }, + ], + }, + ], + }, + { + text: "Workshops", + items: [ + { + text: "How to Integrate with Dify?", + link: "/en/workshop/dify-service-api", + }, + { + text: "How to Implement a Message Platform Adapter?", + link: "/en/workshop/impl-platform-adapter", + }, + { + text: "Integrating Complete MCP Ecosystem in LangBot", + link: "/en/workshop/mcp-details" + }, + { + text: "Container Network Configuration Details", + link: "/en/workshop/network-details", + }, + { + text: "Integrating PPIO API", + link: "/en/workshop/ppio-integration" + } + ], + }, + { + text: "Development", + items: [ + { text: "Development Configuration", link: "/en/develop/dev-config" }, + { text: "Component Architecture", link: "/en/develop/comp-arch" }, + ], + }, + ], - // { - // text: "Usage", - // items: [ - // { text: "Command Usage", link: "/en/usage/command" }, - // { text: "FAQ", link: "/en/usage/faq" }, - // ], - // }, - // { - // text: "Config", - // items: [ - // { - // text: "Function Configuration", - // collapsed: true, - // items: [ - // { - // text: "platform.json", - // link: "/en/config/function/platform", - // }, - // { - // text: "pipeline.json", - // link: "/en/config/function/pipeline", - // }, - // { - // text: "provider.json", - // link: "/en/config/function/provider", - // }, - // { text: "command.json", link: "/en/config/function/command" }, - // { text: "system.json", link: "/en/config/function/system" }, - // ], - // }, - // { - // text: "Metadata Configuration", - // collapsed: true, - // items: [ - // { - // text: "sensitive-words.json", - // link: "/en/config/metadata/sensitive-words", - // }, - // { - // text: "llm-models.json", - // link: "/en/config/metadata/llm-models", - // }, - // { - // text: "adapter-qq-botpy.json", - // link: "/en/config/metadata/adapter-qq-botpy", - // }, - // ], - // }, - // ], - // }, - // { - // text: "Plugin", - // items: [ - // { text: "Plugin Introduction", link: "/en/plugin/plugin-intro" }, - // { - // text: "Plugin Development", - // collapsed: true, - // items: [ - // { text: "Basic Tutorial", link: "/en/plugin/dev/tutor" }, - // { - // text: "Message Platform Entity", - // link: "/en/plugin/dev/messages", - // }, - // { text: "API Reference", link: "/en/plugin/dev/api-ref" }, - // { - // text: "Component Extension", - // link: "/en/plugin/dev/extension", - // }, - // ], - // }, - // ], - // }, - // { - // text: "WebUI (Beta)", - // items: [ - // { text: "Introduction & Usage", link: "/en/webui/intro" }, - // { text: "System Operation", link: "/en/webui/system" }, - // { text: "Settings Management", link: "/en/webui/settings" }, - // ], - // }, - // { - // text: "Workshop", - // items: [ - // { - // text: "How to Access Dify Service API?", - // link: "/en/workshop/dify-service-api", - // }, - // { - // text: "How to Access Dify Service API? - NewAPI Intermediate Solution", - // link: "/en/workshop/dify-integration", - // }, - // { - // text: "How to Access Third-party OpenAI Format APIs like OneAPI, LinkAI?", - // link: "/en/workshop/one-api", - // }, - // { - // text: "How to Implement a Message Platform Adapter?", - // link: "/en/workshop/impl-platform-adapter", - // }, - // ], - // }, - // { - // text: "Develop", - // items: [ - // { text: "Component Architecture", link: "/en/develop/comp-arch" }, - // ], - // }, - // ], + // edit link + editLink: { + pattern: + "https://github.com/the-lazy-me/Langbot-Wiki/edit/main/docs/:path", + }, - // // edit link - // editLink: { - // pattern: - // "https://github.com/the-lazy-me/Langbot-Wiki/edit/main/docs/:path", - // }, - - // // social links in the navbar - // socialLinks: [ - // { - // icon: "github", - // link: "https://github.com/the-lazy-me/QChatGPT-Wiki", - // }, - // ], - // }, - // }, + // social links in the navbar + socialLinks: [ + { + icon: "github", + link: "https://github.com/RockChinQ/LangBot", + }, + ], + }, + }, }, // 最后更新时间 diff --git a/docs/en/deploy/command.md b/docs/en/deploy/command.md new file mode 100644 index 000000000..8497ede29 --- /dev/null +++ b/docs/en/deploy/command.md @@ -0,0 +1,163 @@ +# Dialogue Commands + +LangBot provides various commands that can be used during conversations to control the bot's behavior. + +## Command Prefix + +Commands start with a prefix, which by default is `!` or `!`. You can configure the command prefixes in the `data/config.yaml` file: + +```yaml +trigger: + command_prefixes: + - "!" + - "!" +``` + +## Available Commands + +### Help Command + +``` +!help +``` + +Displays a list of available commands. + +### Model Commands + +``` +!model list +``` + +Lists all available models. + +``` +!model use +``` + +Switches to the specified model. + +``` +!model info +``` + +Displays information about the current model. + +### Context Commands + +``` +!context clear +``` + +Clears the current conversation context. + +``` +!context show +``` + +Displays the current conversation context. + +``` +!context length +``` + +Shows the length of the current context in tokens. + +### Pipeline Commands + +``` +!pipeline list +``` + +Lists all available pipelines. + +``` +!pipeline use +``` + +Switches to the specified pipeline. + +``` +!pipeline info +``` + +Displays information about the current pipeline. + +### System Commands + +``` +!system info +``` + +Displays system information, including version, uptime, and memory usage. + +``` +!system reload +``` + +Reloads the system configuration. + +### Plugin Commands + +``` +!plugin list +``` + +Lists all installed plugins. + +``` +!plugin enable +``` + +Enables the specified plugin. + +``` +!plugin disable +``` + +Disables the specified plugin. + +``` +!plugin info +``` + +Displays information about the specified plugin. + +### Preset Commands + +``` +!preset list +``` + +Lists all available presets. + +``` +!preset use +``` + +Switches to the specified preset. + +``` +!preset info +``` + +Displays information about the current preset. + +## Command Permissions + +You can configure command permissions in the `data/config.yaml` file: + +```yaml +command: + permissions: + - command: "system reload" + roles: + - admin + - command: "plugin *" + roles: + - admin +``` + +## Custom Commands + +Plugins can register custom commands. Check the plugin documentation for available commands. diff --git a/docs/en/deploy/models/readme.md b/docs/en/deploy/models/readme.md new file mode 100644 index 000000000..9f77b28a3 --- /dev/null +++ b/docs/en/deploy/models/readme.md @@ -0,0 +1,113 @@ +# Configure Dialogue Models + +LangBot supports multiple model providers, including OpenAI, Anthropic, Moonshot, Baichuan, and more. + +## Configuration Methods + +### Method 1: Configure via WebUI + +After deploying LangBot, access the WebUI at `http://your-server-ip:5300` and configure the models in the "Models" section. + +### Method 2: Configure via Configuration File + +Edit the `data/config.yaml` file and add your model configuration under the `providers` section: + +```yaml +providers: + - id: openai + type: openai + config: + api_key: "your-api-key" + base_url: "https://api.openai.com/v1" + models: + - gpt-3.5-turbo + - gpt-4 + - gpt-4-turbo +``` + +## Supported Providers + +### OpenAI + +```yaml +providers: + - id: openai + type: openai + config: + api_key: "your-api-key" + base_url: "https://api.openai.com/v1" + models: + - gpt-3.5-turbo + - gpt-4 + - gpt-4-turbo +``` + +### Anthropic + +```yaml +providers: + - id: anthropic + type: anthropic + config: + api_key: "your-api-key" + models: + - claude-3-opus-20240229 + - claude-3-sonnet-20240229 + - claude-3-haiku-20240307 +``` + +### Moonshot + +```yaml +providers: + - id: moonshot + type: openai + config: + api_key: "your-api-key" + base_url: "https://api.moonshot.cn/v1" + models: + - moonshot-v1-8k + - moonshot-v1-32k + - moonshot-v1-128k +``` + +### OneAPI + +```yaml +providers: + - id: oneapi + type: openai + config: + api_key: "your-api-key" + base_url: "https://your-oneapi-url/v1" + models: + - gpt-3.5-turbo + - gpt-4 + - claude-3-opus-20240229 +``` + +## Default Model Configuration + +You can set the default model in the `data/config.yaml` file: + +```yaml +system: + default_provider: openai + default_model: gpt-3.5-turbo +``` + +## Testing Your Configuration + +After configuring your models, you can test them by sending a message to your bot: + +``` +!model list +``` + +This command will list all available models. + +To switch to a specific model: + +``` +!model use gpt-4 +``` diff --git a/docs/en/deploy/pipelines/readme.md b/docs/en/deploy/pipelines/readme.md new file mode 100644 index 000000000..05b0803cb --- /dev/null +++ b/docs/en/deploy/pipelines/readme.md @@ -0,0 +1,148 @@ +# Modify Dialogue Pipeline + +The dialogue pipeline is the core processing flow of LangBot, which handles user messages and generates responses. + +## Pipeline Structure + +![pipeline](/assets/image/zh/deploy/bots/arch.png) + +The pipeline consists of the following components: + +1. **Trigger**: Determines whether to process a message +2. **Moderation**: Filters and processes messages before sending to the model +3. **AI**: Sends the message to the LLM and receives a response +4. **Output**: Processes the model's response before sending it back to the user + +## Configuration Methods + +### Method 1: Configure via WebUI + +After deploying LangBot, access the WebUI at `http://your-server-ip:5300` and configure the pipeline in the "Pipeline" section. + +### Method 2: Configure via Configuration File + +Edit the `data/config.yaml` file and modify the `pipelines` section: + +```yaml +pipelines: + - id: default + components: + - id: trigger + type: trigger + config: + # Trigger configuration + - id: moderation + type: moderation + config: + # Moderation configuration + - id: ai + type: ai + config: + # AI configuration + - id: output + type: output + config: + # Output configuration +``` + +## Component Configuration + +### Trigger Component + +The trigger component determines whether a message should be processed by the pipeline. + +```yaml +- id: trigger + type: trigger + config: + # Whether to respond to messages that start with a command prefix + command_prefix: true + # Command prefixes (characters that trigger the bot) + command_prefixes: + - "!" + - "!" + # Whether to respond to messages that mention the bot + at: true + # Whether to respond to private messages + private: true + # Whether to respond to group messages without being triggered by command prefix or mention + group_chat: false +``` + +### Moderation Component + +The moderation component filters and processes messages before sending them to the model. + +```yaml +- id: moderation + type: moderation + config: + # Whether to enable sensitive word filtering + sensitive_word_filter: true + # Whether to enable rate limiting + rate_limit: true + # Rate limit configuration + rate_limit_config: + # Number of messages allowed per time period + count: 10 + # Time period in seconds + period: 60 + # Whether to enable context management + context: true + # Context configuration + context_config: + # Maximum number of messages to include in the context + max_count: 10 + # Maximum context length in tokens + max_tokens: 4000 +``` + +### AI Component + +The AI component sends the message to the LLM and receives a response. + +```yaml +- id: ai + type: ai + config: + # System prompt + system_prompt: "You are LangBot, a helpful assistant." + # Whether to enable function calling + function_call: true + # Whether to enable streaming responses + stream: true +``` + +### Output Component + +The output component processes the model's response before sending it back to the user. + +```yaml +- id: output + type: output + config: + # Whether to enable message splitting for long responses + split: true + # Maximum length of a single message + max_length: 1000 +``` + +## Multiple Pipelines + +You can configure multiple pipelines for different scenarios: + +```yaml +pipelines: + - id: default + # Default pipeline configuration + - id: image-generation + # Pipeline for image generation + - id: code-assistant + # Pipeline for code assistance +``` + +To switch between pipelines, use the following command: + +``` +!pipeline use image-generation +``` diff --git a/docs/en/deploy/platforms/readme.md b/docs/en/deploy/platforms/readme.md new file mode 100644 index 000000000..972d7fbdf --- /dev/null +++ b/docs/en/deploy/platforms/readme.md @@ -0,0 +1,20 @@ +# Configure Bots + +Bots are used to receive message events from messaging platforms and call pipelines to process messages. + +![arch](/assets/image/zh/deploy/bots/arch.png) + + +| Platform | Status | Notes | Tutorial | +| ------ | ---- | ---- | ---- | +| OneBot v11 | ✅ | Uses a regular QQ account as a bot, requires an independent protocol client, supports OneBot v11 | [NapCat](/en/deploy/platforms/qq/aiocqhttp/napcat)
[Lagrange](/en/deploy/platforms/qq/aiocqhttp/lagrange)
[LiteLoaderQQNT](/en/deploy/platforms/qq/aiocqhttp/llonebot) | +| QQ Official Bot | ✅ | Connects to the QQ Open Platform bot API | [Tutorial](/en/deploy/platforms/qq/official_webhook) | +| WeCom (Enterprise WeChat) | ✅ | Enterprise internal bot | [Tutorial](/en/deploy/platforms/wecom/wecom) | +| WeCom Customer Service | ✅ | Enterprise external bot | [Tutorial](/en/deploy/platforms/wecom/wecomcs) | +| WeChat Official Account | ✅ | WeChat public platform bot | [Tutorial](/en/deploy/platforms/wxoa) | +| Personal WeChat | ✅ | Personal WeChat bot | [Gewechat](/en/deploy/platforms/wechat/gewechat) | +| Feishu (Lark) | ✅ | | [Tutorial](/en/deploy/platforms/lark) | +| DingTalk | ✅ | | [Tutorial](/en/deploy/platforms/dingtalk) | +| Discord | ✅ | | [Tutorial](/en/deploy/platforms/discord) | +| Telegram | ✅ | | [Tutorial](/en/deploy/platforms/telegram) | +| Slack | ✅ | | [Tutorial](/en/deploy/platforms/slack) | diff --git a/docs/en/deploy/update.md b/docs/en/deploy/update.md new file mode 100644 index 000000000..a930fc649 --- /dev/null +++ b/docs/en/deploy/update.md @@ -0,0 +1,93 @@ +# Update LangBot + +This guide explains how to update your LangBot installation to the latest version. + +## Docker Deployment + +If you deployed LangBot using Docker, follow these steps to update: + +1. Pull the latest image: + +```bash +docker pull rockchin/langbot:latest +``` + +2. Stop and remove the current container: + +```bash +docker stop langbot +docker rm langbot +``` + +3. Start a new container with the latest image: + +```bash +docker run -d --name langbot \ + -p 5300:5300 \ + -v /path/to/data:/app/data \ + rockchin/langbot:latest +``` + +Replace `/path/to/data` with the path to your data directory. + +## Manual Deployment + +If you deployed LangBot manually, follow these steps to update: + +1. Navigate to your LangBot directory: + +```bash +cd /path/to/langbot +``` + +2. Pull the latest changes: + +```bash +git pull +``` + +3. Update dependencies: + +```bash +pip install -r requirements.txt +``` + +4. Restart LangBot: + +```bash +python main.py +``` + +## Baota Panel Deployment + +If you deployed LangBot using the Baota Panel, follow these steps to update: + +1. Log in to your Baota Panel +2. Navigate to the LangBot website +3. Click on "Site Settings" +4. Click on "Update from Git" +5. Click "Update" to pull the latest changes +6. Restart the website + +## Configuration Changes + +After updating, check the [release notes](https://github.com/RockChinQ/LangBot/releases) for any configuration changes. You may need to update your `data/config.yaml` file to accommodate new features or changes. + +## Database Migration + +LangBot automatically handles database migrations during updates. However, it's always a good idea to back up your data directory before updating. + +## Troubleshooting + +If you encounter issues after updating, check the following: + +1. Make sure your configuration file is compatible with the new version +2. Check the logs for any error messages +3. Ensure all dependencies are installed correctly +4. Verify that your model providers are still configured correctly + +If problems persist, you can: + +1. Join the [community chat](https://qm.qq.com/q/Nnz7Vbj8OU) for help +2. Open an issue on [GitHub](https://github.com/RockChinQ/LangBot/issues) +3. Revert to a previous version if necessary diff --git a/docs/en/develop/comp-arch.md b/docs/en/develop/comp-arch.md new file mode 100644 index 000000000..cf2042776 --- /dev/null +++ b/docs/en/develop/comp-arch.md @@ -0,0 +1,96 @@ +# Component Architecture + +This document describes the component architecture of LangBot, which is designed to be modular and extensible. + +## Overview + +LangBot is built around a component-based architecture that allows for easy extension and customization. The main components are: + +1. **Bots**: Interface with messaging platforms +2. **Pipelines**: Process messages and generate responses +3. **Models**: Connect to LLM providers +4. **Plugins**: Extend functionality + +![architecture](/assets/image/zh/deploy/bots/arch.png) + +## Bots + +Bots are responsible for connecting to messaging platforms and handling message events. Each bot implements a specific platform adapter that translates between the platform's API and LangBot's internal event system. + +Key classes: +- `MessageSourceAdapter`: Base class for all platform adapters +- `EventConverter`: Converts platform-specific events to LangBot events +- `MessageConverter`: Converts platform-specific messages to LangBot messages + +## Pipelines + +Pipelines process messages through a series of components: + +1. **Trigger**: Determines whether to process a message +2. **Moderation**: Filters and processes messages before sending to the model +3. **AI**: Sends the message to the LLM and receives a response +4. **Output**: Processes the model's response before sending it back to the user + +Each component in the pipeline can be configured and customized. + +## Models + +The Models component manages connections to various LLM providers, such as OpenAI, Anthropic, and others. It provides a unified interface for sending requests to these providers and handling their responses. + +Key classes: +- `ModelProvider`: Base class for all model providers +- `ModelRequest`: Represents a request to a model +- `ModelResponse`: Represents a response from a model + +## Plugins + +The plugin system allows for extending LangBot's functionality without modifying the core code. Plugins can: + +- Register event handlers +- Add new commands +- Provide functions that can be called by the LLM +- Extend core components + +Key concepts: +- `@handler`: Decorator for registering event handlers +- `@llm_func`: Decorator for registering functions that can be called by the LLM +- `APIHost`: Provides access to LangBot's API from plugins +- `manifest.yaml`: Configuration file for plugins + +## Event System + +LangBot uses an event-driven architecture where different components communicate through events. Events include: + +- Message events (received, sent) +- Group events (joined, left, invited) +- System events (startup, shutdown) +- Custom events defined by plugins + +## Configuration System + +The configuration system uses a YAML-based approach with the main configuration file at `data/config.yaml`. The configuration is structured around the component architecture: + +```yaml +bots: + # Bot configurations +pipelines: + # Pipeline configurations +providers: + # Model provider configurations +plugins: + # Plugin configurations +system: + # System configurations +``` + +## Development Workflow + +When developing for LangBot, follow these steps: + +1. Understand the component you want to modify or extend +2. Create a plugin if you're adding new functionality +3. Implement the necessary classes and methods +4. Test your changes +5. Submit a pull request + +For more detailed information on development, see the [Development Configuration](/en/develop/dev-config) page. diff --git a/docs/en/develop/dev-config.md b/docs/en/develop/dev-config.md new file mode 100644 index 000000000..417dafddd --- /dev/null +++ b/docs/en/develop/dev-config.md @@ -0,0 +1,160 @@ +# Development Configuration + +This guide explains how to set up a development environment for LangBot and contribute to the project. + +## Prerequisites + +Before you start, make sure you have the following installed: + +- Python 3.10 or higher +- Git +- Node.js 16 or higher (for WebUI development) +- A code editor (VS Code recommended) + +## Setting Up the Development Environment + +1. Fork the LangBot repository on GitHub +2. Clone your fork: + +```bash +git clone https://github.com/your-username/LangBot.git +cd LangBot +``` + +3. Create a virtual environment: + +```bash +python -m venv venv +source venv/bin/activate # On Windows: venv\Scripts\activate +``` + +4. Install dependencies: + +```bash +pip install -r requirements.txt +pip install -r requirements-dev.txt +``` + +5. Set up pre-commit hooks: + +```bash +pre-commit install +``` + +## Project Structure + +The LangBot project is organized as follows: + +``` +LangBot/ +├── data/ # Data directory (configs, plugins, etc.) +├── langbot/ # Core package +│ ├── bot/ # Bot implementations +│ ├── pipeline/ # Pipeline components +│ ├── model/ # Model providers +│ ├── plugin/ # Plugin system +│ ├── webui/ # WebUI backend +│ └── utils/ # Utility functions +├── tests/ # Test cases +├── web/ # WebUI frontend +├── main.py # Entry point +└── requirements.txt # Dependencies +``` + +## Running LangBot in Development Mode + +To run LangBot in development mode: + +```bash +python main.py --debug +``` + +This will start LangBot with debug logging enabled. + +## WebUI Development + +To develop the WebUI: + +1. Navigate to the web directory: + +```bash +cd web +``` + +2. Install dependencies: + +```bash +npm install +``` + +3. Start the development server: + +```bash +npm run dev +``` + +4. In another terminal, start LangBot with the WebUI enabled: + +```bash +python main.py --webui +``` + +## Testing + +LangBot uses pytest for testing. To run the tests: + +```bash +pytest +``` + +To run a specific test: + +```bash +pytest tests/test_file.py::test_function +``` + +## Code Style + +LangBot follows the PEP 8 style guide with some modifications. We use black for code formatting and isort for import sorting. + +To format your code: + +```bash +black langbot tests +isort langbot tests +``` + +## Creating a Pull Request + +1. Create a new branch for your feature or bugfix: + +```bash +git checkout -b feature/your-feature-name +``` + +2. Make your changes and commit them: + +```bash +git add . +git commit -m "Add your feature or fix" +``` + +3. Push your branch to GitHub: + +```bash +git push origin feature/your-feature-name +``` + +4. Create a pull request on GitHub + +## Documentation + +When adding new features, make sure to update the documentation. The documentation is written in Markdown and is located in the [QChatGPT-Wiki](https://github.com/RockChinQ/QChatGPT-Wiki) repository. + +## Getting Help + +If you need help with development, you can: + +- Join the [community chat](https://qm.qq.com/q/Nnz7Vbj8OU) +- Open an issue on [GitHub](https://github.com/RockChinQ/LangBot/issues) +- Check the existing documentation and code for examples diff --git a/docs/en/index.md b/docs/en/index.md new file mode 100644 index 000000000..0613a5da9 --- /dev/null +++ b/docs/en/index.md @@ -0,0 +1,3 @@ + diff --git a/docs/en/insight/community.md b/docs/en/insight/community.md new file mode 100644 index 000000000..87129b60b --- /dev/null +++ b/docs/en/insight/community.md @@ -0,0 +1,24 @@ +# Community Resources + +## Official Resources + +- [GitHub Repository](https://github.com/RockChinQ/LangBot) +- [Documentation](https://docs.langbot.app) +- [Plugin Market](https://plugins.langbot.app) + +## Community Resources + +### Deployment Tutorials + +- [Simplified Deployment Tutorial](https://www.bilibili.com/video/BV1Uj421y7Gu) +- [Simplified Deployment Tutorial (Text Version)](https://www.bilibili.com/read/cv31018000) + +### Community Discussions + +- [QQ Group: 1141946368](https://qm.qq.com/q/Nnz7Vbj8OU) +- [Telegram Group](https://t.me/+Ij8xM9pPToxmNDQ1) +- [Discord Server](https://discord.gg/Nnz7Vbj8OU) + +## Contribution + +If you want to contribute to LangBot, please read the [Development Configuration](/en/develop/dev-config) and [Component Architecture](/en/develop/comp-arch) sections. diff --git a/docs/en/insight/guide.md b/docs/en/insight/guide.md new file mode 100644 index 000000000..8b80c6ad7 --- /dev/null +++ b/docs/en/insight/guide.md @@ -0,0 +1,49 @@ +# Welcome to LangBot Documentation Center + +LangBot is an open-source Large Language Model (LLM) native instant messaging robot platform that supports multiple messaging platforms and model providers. It provides support for LLMOps platforms such as Dify and Alibaba Cloud Bailian. It also offers a WebUI for users to quickly deploy and use. + +:::info +This is the documentation for LangBot 4.0. For 3.0 documentation, please [visit here](https://v3.docs.langbot.app). +::: + +## Documentation Structure + +The documentation is divided into the following sections: + +- Deployment and Usage (Must Read): Detailed steps for deploying and using LangBot +- Plugins: Information related to plugin usage and development +- Workshops: Many excellent use cases for LangBot +- Development: Information related to participating in LangBot development + +## Usage Examples + +![private_chat](/assets/image/insight/private_chat.png) + +![group_chat](/assets/image/insight/group_chat.png) + +## Quick Start Guide + +> Two ways to get started quickly: +> +> - Use the simplified tutorial from community resources (see left sidebar) +> - Read the documentation to deploy it yourself (method described below) + +### 1. Deploy LangBot + +Read the **Deploy LangBot** section on the left and choose a method to deploy LangBot + +After deployment, open `http://127.0.0.1:5300` or `http://your-server-public-ip:5300` + +Complete the initialization by entering an email and password, and make sure to save them + +### 2. Configure Bots + +Check [Configure Bots](/en/deploy/platforms/readme) in the left sidebar + +### 3. Configure Models + +Check [Configure Dialogue Models](/en/deploy/models/readme) in the left sidebar + +
+ +After completing the above two steps, you can start using LangBot. If you need to configure more features, you can set them in the [Pipeline](/en/deploy/pipelines/readme) section diff --git a/docs/en/plugin/dev/api-ref.md b/docs/en/plugin/dev/api-ref.md new file mode 100644 index 000000000..54b1efb41 --- /dev/null +++ b/docs/en/plugin/dev/api-ref.md @@ -0,0 +1,612 @@ +# API Reference + +This document provides a reference for the LangBot API that is available to plugins. + +## APIHost + +The `APIHost` class provides access to LangBot's API from plugins. It is passed to the plugin's constructor. + +```python +from langbot.api import APIHost + +class MyPlugin(Plugin): + def __init__(self, api_host: APIHost): + super().__init__(api_host) + self.api = api_host +``` + +## Message API + +### send_message + +Sends a message to a specific destination. + +```python +def send_message(self, source, content, **kwargs) -> str: + """ + Send a message to a specific destination. + + Args: + source: The destination to send the message to + content: The content of the message + **kwargs: Additional arguments + + Returns: + The message ID + """ +``` + +Example: + +```python +message_id = self.api.send_message( + ctx.event.message.source, + "Hello, World!" +) +``` + +### reply_message + +Replies to a specific message. + +```python +def reply_message(self, message, content, **kwargs) -> str: + """ + Reply to a specific message. + + Args: + message: The message to reply to + content: The content of the reply + **kwargs: Additional arguments + + Returns: + The message ID + """ +``` + +Example: + +```python +message_id = self.api.reply_message( + ctx.event.message, + "This is a reply" +) +``` + +### delete_message + +Deletes a specific message. + +```python +def delete_message(self, message_id, **kwargs) -> bool: + """ + Delete a specific message. + + Args: + message_id: The ID of the message to delete + **kwargs: Additional arguments + + Returns: + True if successful, False otherwise + """ +``` + +Example: + +```python +success = self.api.delete_message(message_id) +``` + +## User API + +### get_user_info + +Gets information about a user. + +```python +def get_user_info(self, user_id, **kwargs) -> dict: + """ + Get information about a user. + + Args: + user_id: The ID of the user + **kwargs: Additional arguments + + Returns: + A dictionary containing user information + """ +``` + +Example: + +```python +user_info = self.api.get_user_info("12345678") +print(user_info["nickname"]) +``` + +### get_friend_list + +Gets a list of the bot's friends. + +```python +def get_friend_list(self, **kwargs) -> list: + """ + Get a list of the bot's friends. + + Args: + **kwargs: Additional arguments + + Returns: + A list of friend information + """ +``` + +Example: + +```python +friends = self.api.get_friend_list() +for friend in friends: + print(friend["nickname"]) +``` + +## Group API + +### get_group_info + +Gets information about a group. + +```python +def get_group_info(self, group_id, **kwargs) -> dict: + """ + Get information about a group. + + Args: + group_id: The ID of the group + **kwargs: Additional arguments + + Returns: + A dictionary containing group information + """ +``` + +Example: + +```python +group_info = self.api.get_group_info("12345678") +print(group_info["name"]) +``` + +### get_group_member_list + +Gets a list of members in a group. + +```python +def get_group_member_list(self, group_id, **kwargs) -> list: + """ + Get a list of members in a group. + + Args: + group_id: The ID of the group + **kwargs: Additional arguments + + Returns: + A list of member information + """ +``` + +Example: + +```python +members = self.api.get_group_member_list("12345678") +for member in members: + print(member["nickname"]) +``` + +### get_group_list + +Gets a list of groups the bot is in. + +```python +def get_group_list(self, **kwargs) -> list: + """ + Get a list of groups the bot is in. + + Args: + **kwargs: Additional arguments + + Returns: + A list of group information + """ +``` + +Example: + +```python +groups = self.api.get_group_list() +for group in groups: + print(group["name"]) +``` + +## LLM API + +### call_llm + +Calls the LLM with a specific prompt. + +```python +def call_llm(self, prompt, model=None, **kwargs) -> str: + """ + Call the LLM with a specific prompt. + + Args: + prompt: The prompt to send to the LLM + model: The model to use (defaults to the system default) + **kwargs: Additional arguments + + Returns: + The LLM's response + """ +``` + +Example: + +```python +response = self.api.call_llm( + "What is the capital of France?", + model="gpt-3.5-turbo" +) +print(response) +``` + +### call_llm_with_functions + +Calls the LLM with a specific prompt and available functions. + +```python +def call_llm_with_functions(self, prompt, functions, model=None, **kwargs) -> dict: + """ + Call the LLM with a specific prompt and available functions. + + Args: + prompt: The prompt to send to the LLM + functions: A list of function definitions + model: The model to use (defaults to the system default) + **kwargs: Additional arguments + + Returns: + A dictionary containing the LLM's response and any function calls + """ +``` + +Example: + +```python +functions = [ + { + "name": "get_weather", + "description": "Get the weather for a location", + "parameters": { + "type": "object", + "properties": { + "location": { + "type": "string", + "description": "The location to get weather for" + } + }, + "required": ["location"] + } + } +] + +response = self.api.call_llm_with_functions( + "What's the weather in Paris?", + functions, + model="gpt-3.5-turbo" +) + +if "function_call" in response: + function_name = response["function_call"]["name"] + function_args = response["function_call"]["arguments"] + # Handle the function call +``` + +## Storage API + +### storage.set + +Stores a value with a specific key. + +```python +def set(self, key, value) -> None: + """ + Store a value with a specific key. + + Args: + key: The key to store the value under + value: The value to store + """ +``` + +Example: + +```python +self.api.storage.set("my_plugin:counter", 42) +``` + +### storage.get + +Gets a value with a specific key. + +```python +def get(self, key, default=None): + """ + Get a value with a specific key. + + Args: + key: The key to get the value for + default: The default value to return if the key doesn't exist + + Returns: + The stored value or the default value + """ +``` + +Example: + +```python +counter = self.api.storage.get("my_plugin:counter", 0) +``` + +### storage.has + +Checks if a key exists. + +```python +def has(self, key) -> bool: + """ + Check if a key exists. + + Args: + key: The key to check + + Returns: + True if the key exists, False otherwise + """ +``` + +Example: + +```python +if self.api.storage.has("my_plugin:counter"): + # Key exists +``` + +### storage.delete + +Deletes a key. + +```python +def delete(self, key) -> None: + """ + Delete a key. + + Args: + key: The key to delete + """ +``` + +Example: + +```python +self.api.storage.delete("my_plugin:counter") +``` + +## Config API + +### config.get + +Gets a configuration value. + +```python +def get(self, key, default=None): + """ + Get a configuration value. + + Args: + key: The key to get the value for + default: The default value to return if the key doesn't exist + + Returns: + The configuration value or the default value + """ +``` + +Example: + +```python +api_key = self.api.config.get("my_plugin:api_key") +``` + +### config.set + +Sets a configuration value. + +```python +def set(self, key, value) -> None: + """ + Set a configuration value. + + Args: + key: The key to set the value for + value: The value to set + """ +``` + +Example: + +```python +self.api.config.set("my_plugin:api_key", "my-api-key") +``` + +## Event API + +### register_event_handler + +Registers a handler for a specific event. + +```python +def register_event_handler(self, event_type, handler) -> None: + """ + Register a handler for a specific event. + + Args: + event_type: The type of event to handle + handler: The handler function + """ +``` + +Example: + +```python +def my_handler(ctx): + # Handle the event + pass + +self.api.register_event_handler(PersonNormalMessageReceived, my_handler) +``` + +### unregister_event_handler + +Unregisters a handler for a specific event. + +```python +def unregister_event_handler(self, event_type, handler) -> None: + """ + Unregister a handler for a specific event. + + Args: + event_type: The type of event + handler: The handler function + """ +``` + +Example: + +```python +self.api.unregister_event_handler(PersonNormalMessageReceived, my_handler) +``` + +## Command API + +### register_command + +Registers a command. + +```python +def register_command(self, name, handler, description=None) -> None: + """ + Register a command. + + Args: + name: The name of the command + handler: The handler function + description: The description of the command + """ +``` + +Example: + +```python +def echo_handler(args): + return args + +self.api.register_command("echo", echo_handler, "Echo a message") +``` + +### unregister_command + +Unregisters a command. + +```python +def unregister_command(self, name) -> None: + """ + Unregister a command. + + Args: + name: The name of the command + """ +``` + +Example: + +```python +self.api.unregister_command("echo") +``` + +## Utility API + +### logger + +Provides access to the logger. + +```python +self.api.logger.debug("Debug message") +self.api.logger.info("Info message") +self.api.logger.warning("Warning message") +self.api.logger.error("Error message") +``` + +### schedule + +Schedules a function to be called at a specific time. + +```python +def schedule(self, func, when, *args, **kwargs) -> str: + """ + Schedule a function to be called at a specific time. + + Args: + func: The function to call + when: When to call the function (datetime or timedelta) + *args: Arguments to pass to the function + **kwargs: Keyword arguments to pass to the function + + Returns: + The schedule ID + """ +``` + +Example: + +```python +from datetime import timedelta + +def my_function(): + # Do something + pass + +schedule_id = self.api.schedule(my_function, timedelta(hours=1)) +``` + +### cancel_schedule + +Cancels a scheduled function. + +```python +def cancel_schedule(self, schedule_id) -> bool: + """ + Cancel a scheduled function. + + Args: + schedule_id: The ID of the schedule + + Returns: + True if successful, False otherwise + """ +``` + +Example: + +```python +success = self.api.cancel_schedule(schedule_id) +``` diff --git a/docs/en/plugin/dev/messages.md b/docs/en/plugin/dev/messages.md new file mode 100644 index 000000000..340a1a5be --- /dev/null +++ b/docs/en/plugin/dev/messages.md @@ -0,0 +1,338 @@ +# Message Platform Entities + +This document describes the message platform entities used in LangBot, which are important for plugin development. + +## Message + +The `Message` class represents a message in LangBot. + +```python +class Message: + """ + Represents a message in LangBot. + """ + + def __init__(self, source, content, message_id=None, sender=None, time=None, **kwargs): + """ + Initialize a message. + + Args: + source: The source of the message + content: The content of the message + message_id: The ID of the message + sender: The sender of the message + time: The time the message was sent + **kwargs: Additional arguments + """ + self.source = source + self.content = content + self.message_id = message_id + self.sender = sender + self.time = time + self.kwargs = kwargs +``` + +### Properties + +- `source`: The source of the message (where it was sent from) +- `content`: The content of the message +- `message_id`: The ID of the message +- `sender`: The sender of the message +- `time`: The time the message was sent +- `kwargs`: Additional arguments + +## Source + +The `Source` class represents the source of a message. + +```python +class Source: + """ + Represents the source of a message. + """ + + def __init__(self, platform, identity, **kwargs): + """ + Initialize a source. + + Args: + platform: The platform of the source + identity: The identity of the source + **kwargs: Additional arguments + """ + self.platform = platform + self.identity = identity + self.kwargs = kwargs +``` + +### Properties + +- `platform`: The platform of the source (e.g., "qq", "wechat", "discord") +- `identity`: The identity of the source (e.g., user ID, group ID) +- `kwargs`: Additional arguments + +### Types of Sources + +#### Person Source + +Represents a direct message from a person. + +```python +class PersonSource(Source): + """ + Represents a direct message from a person. + """ + + def __init__(self, platform, identity, **kwargs): + """ + Initialize a person source. + + Args: + platform: The platform of the source + identity: The identity of the person + **kwargs: Additional arguments + """ + super().__init__(platform, identity, **kwargs) +``` + +#### Group Source + +Represents a message from a group. + +```python +class GroupSource(Source): + """ + Represents a message from a group. + """ + + def __init__(self, platform, identity, **kwargs): + """ + Initialize a group source. + + Args: + platform: The platform of the source + identity: The identity of the group + **kwargs: Additional arguments + """ + super().__init__(platform, identity, **kwargs) +``` + +## Sender + +The `Sender` class represents the sender of a message. + +```python +class Sender: + """ + Represents the sender of a message. + """ + + def __init__(self, identity, name=None, **kwargs): + """ + Initialize a sender. + + Args: + identity: The identity of the sender + name: The name of the sender + **kwargs: Additional arguments + """ + self.identity = identity + self.name = name + self.kwargs = kwargs +``` + +### Properties + +- `identity`: The identity of the sender (e.g., user ID) +- `name`: The name of the sender +- `kwargs`: Additional arguments + +## Events + +LangBot uses events to represent various actions in the system. Here are some common events related to messages: + +### PersonNormalMessageReceived + +Triggered when a normal message is received from a person. + +```python +class PersonNormalMessageReceived(Event): + """ + Triggered when a normal message is received from a person. + """ + + def __init__(self, message): + """ + Initialize the event. + + Args: + message: The received message + """ + super().__init__() + self.message = message +``` + +### GroupNormalMessageReceived + +Triggered when a normal message is received in a group. + +```python +class GroupNormalMessageReceived(Event): + """ + Triggered when a normal message is received in a group. + """ + + def __init__(self, message): + """ + Initialize the event. + + Args: + message: The received message + """ + super().__init__() + self.message = message +``` + +### MessageSent + +Triggered when a message is sent. + +```python +class MessageSent(Event): + """ + Triggered when a message is sent. + """ + + def __init__(self, message): + """ + Initialize the event. + + Args: + message: The sent message + """ + super().__init__() + self.message = message +``` + +## Working with Messages in Plugins + +### Handling Messages + +```python +from langbot.plugin import Plugin, handler +from langbot.event import PersonNormalMessageReceived, GroupNormalMessageReceived +from langbot.api import APIHost + +class MessageHandlerPlugin(Plugin): + def __init__(self, api_host: APIHost): + super().__init__(api_host) + self.api = api_host + + @handler(PersonNormalMessageReceived) + def on_person_message(self, ctx): + # Get the message + message = ctx.event.message + + # Get the content + content = message.content + + # Get the sender + sender = message.sender + + # Get the source + source = message.source + + # Do something with the message + if content == "!hello": + self.api.send_message(source, f"Hello, {sender.name}!") + + @handler(GroupNormalMessageReceived) + def on_group_message(self, ctx): + # Get the message + message = ctx.event.message + + # Get the content + content = message.content + + # Get the sender + sender = message.sender + + # Get the source + source = message.source + + # Do something with the message + if content == "!hello": + self.api.send_message(source, f"Hello, {sender.name}!") +``` + +### Sending Messages + +```python +# Send a message to a person +person_source = PersonSource("qq", "12345678") +self.api.send_message(person_source, "Hello!") + +# Send a message to a group +group_source = GroupSource("qq", "87654321") +self.api.send_message(group_source, "Hello, everyone!") + +# Reply to a message +self.api.reply_message(message, "This is a reply") +``` + +### Creating Messages + +```python +from langbot.message import Message, PersonSource, Sender + +# Create a person source +source = PersonSource("qq", "12345678") + +# Create a sender +sender = Sender("87654321", "John Doe") + +# Create a message +message = Message( + source=source, + content="Hello, World!", + message_id="msg123", + sender=sender, + time=1234567890 +) +``` + +## Platform-Specific Considerations + +Different messaging platforms may have different capabilities and limitations. Here are some platform-specific considerations: + +### QQ + +- Supports text, images, and rich media +- Group messages can mention specific users +- Message IDs are platform-specific + +### WeChat + +- Supports text, images, and rich media +- Limited API for personal accounts +- More features available for official accounts + +### Discord + +- Supports text, images, embeds, and rich formatting +- Has a robust API with many features +- Supports slash commands + +### Telegram + +- Supports text, images, and rich formatting +- Has a comprehensive API +- Supports inline bots and commands + +## Best Practices + +- Always check the platform before using platform-specific features +- Handle message content carefully, as it may contain special formatting +- Use the appropriate source type (PersonSource or GroupSource) when sending messages +- Consider rate limits and message size limits when sending messages +- Handle errors gracefully, as message sending may fail for various reasons diff --git a/docs/en/plugin/dev/tutor.md b/docs/en/plugin/dev/tutor.md new file mode 100644 index 000000000..d207b303f --- /dev/null +++ b/docs/en/plugin/dev/tutor.md @@ -0,0 +1,298 @@ +# Plugin Development Tutorial + +This tutorial will guide you through the process of developing a plugin for LangBot. + +## Plugin Structure + +A LangBot plugin consists of the following components: + +1. A main Python file that contains the plugin logic +2. A `manifest.yaml` file that describes the plugin +3. Optional additional files for resources, templates, etc. + +## Creating a Basic Plugin + +Let's create a simple "Hello World" plugin that responds to a specific command. + +### Step 1: Create the Plugin Directory + +Create a directory for your plugin in the `data/plugins` directory: + +```bash +mkdir -p data/plugins/hello-plugin +``` + +### Step 2: Create the Manifest File + +Create a `manifest.yaml` file in your plugin directory: + +```yaml +name: HelloPlugin +version: 1.0.0 +description: A simple hello world plugin +author: Your Name +entry: hello.py +requirements: + - requests>=2.28.0 +``` + +### Step 3: Create the Plugin Code + +Create a `hello.py` file in your plugin directory: + +```python +from langbot.plugin import Plugin, handler +from langbot.event import PersonNormalMessageReceived +from langbot.api import APIHost + +class HelloPlugin(Plugin): + def __init__(self, api_host: APIHost): + super().__init__(api_host) + self.api = api_host + + @handler(PersonNormalMessageReceived) + def on_person_message(self, ctx): + # Get the message content + message = ctx.event.message.content + + # Check if the message is the command we want to handle + if message == "!hello": + # Prevent the default handling (don't send to LLM) + ctx.prevent_default() + + # Send a response + self.api.send_message( + ctx.event.message.source, + "Hello, World! This is my first LangBot plugin." + ) +``` + +### Step 4: Install and Test the Plugin + +1. Restart LangBot or use the `!plugin reload` command +2. Send the `!hello` command to your bot +3. The bot should respond with "Hello, World! This is my first LangBot plugin." + +## Event Handling + +LangBot uses an event-driven architecture. Plugins can register handlers for various events: + +```python +@handler(PersonNormalMessageReceived) +def on_person_message(self, ctx): + # Handle the event + pass +``` + +Common events include: + +- `PersonNormalMessageReceived`: Received a message from a person +- `GroupNormalMessageReceived`: Received a message in a group +- `GroupInvitationReceived`: Received an invitation to join a group +- `FriendRequestReceived`: Received a friend request + +The `ctx` parameter provides access to: + +- `ctx.event`: The event that triggered the handler +- `ctx.prevent_default()`: Prevents the default handling of the event +- `ctx.add_return(key, value)`: Adds a return value to the event + +## API Access + +The `APIHost` provides access to LangBot's API: + +```python +# Send a message +self.api.send_message(source, content) + +# Get user information +user_info = self.api.get_user_info(user_id) + +# Get group information +group_info = self.api.get_group_info(group_id) + +# Call the LLM +response = self.api.call_llm(prompt, model="gpt-3.5-turbo") +``` + +## LLM Function Registration + +Plugins can register functions that can be called by the LLM during conversations: + +```python +import requests +from langbot.plugin import Plugin, llm_func +from langbot.api import APIHost + +class WebSearchPlugin(Plugin): + def __init__(self, api_host: APIHost): + super().__init__(api_host) + self.api = api_host + + @llm_func(name="search_the_web") + def search_web(self, query: str) -> str: + """ + Search the web for information. + + Args: + query: The search query + + Returns: + The search results + """ + # Implement web search logic + response = requests.get( + "https://api.search.com/search", + params={"q": query} + ) + return response.json()["results"] +``` + +The LLM can then call this function during conversations: + +``` +User: What's the weather in New York? +Bot: Let me check that for you. +[Function call: search_the_web("weather in New York")] +The current weather in New York is 72°F and sunny. +``` + +## Configuration + +Plugins can have configuration options that users can set: + +```python +from langbot.plugin import Plugin, config_field +from langbot.api import APIHost + +class WeatherPlugin(Plugin): + def __init__(self, api_host: APIHost): + super().__init__(api_host) + self.api = api_host + + @config_field(name="api_key", description="API key for the weather service") + def set_api_key(self, api_key: str): + self.api_key = api_key +``` + +Users can set these options using the WebUI or the `!plugin config` command: + +``` +!plugin config WeatherPlugin api_key=your-api-key +``` + +## Persistent Storage + +Plugins can store data persistently: + +```python +# Store data +self.api.storage.set("key", "value") + +# Retrieve data +value = self.api.storage.get("key") + +# Check if a key exists +exists = self.api.storage.has("key") + +# Delete a key +self.api.storage.delete("key") +``` + +## Advanced Features + +### Command Registration + +Plugins can register commands that users can use: + +```python +from langbot.plugin import Plugin, command +from langbot.api import APIHost + +class EchoPlugin(Plugin): + def __init__(self, api_host: APIHost): + super().__init__(api_host) + self.api = api_host + + @command(name="echo", description="Echo a message") + def echo_command(self, args: str) -> str: + """ + Echo the provided message. + + Usage: !echo + """ + return args +``` + +Users can then use the command: + +``` +!echo Hello, World! +``` + +### Event Return Values + +Handlers can add return values to events: + +```python +@handler(PersonNormalMessageReceived) +def on_person_message(self, ctx): + # Add a return value + ctx.add_return("reply", ["This is a reply"]) +``` + +### Component Extension + +Plugins can extend LangBot's core components: + +```python +from langbot.plugin import Plugin, component_extension +from langbot.api import APIHost + +class CustomTriggerPlugin(Plugin): + def __init__(self, api_host: APIHost): + super().__init__(api_host) + self.api = api_host + + @component_extension("trigger") + def custom_trigger(self, component, message): + # Implement custom trigger logic + if message.content.startswith("custom:"): + return True + return None # Let the default trigger handle it +``` + +## Publishing Your Plugin + +To publish your plugin: + +1. Create a GitHub repository for your plugin +2. Add a README.md file with installation and usage instructions +3. Add your plugin to the [LangBot Plugin Market](https://plugins.langbot.app) + +## Best Practices + +- Keep your plugin focused on a specific functionality +- Handle errors gracefully +- Provide clear documentation +- Follow Python best practices +- Test your plugin thoroughly +- Keep dependencies minimal + +## Example Plugins + +Check out these example plugins for inspiration: + +- [WebSearch](https://github.com/RockChinQ/WebSearch): Search the web for information +- [ImageGen](https://github.com/RockChinQ/ImageGen): Generate images using AI +- [Schedule](https://github.com/RockChinQ/Schedule): Schedule messages to be sent at specific times + +## Troubleshooting + +If you encounter issues with your plugin: + +1. Check the logs for error messages +2. Verify that your plugin is correctly installed +3. Make sure your plugin's dependencies are installed +4. Test your plugin with simple functionality first +5. Ask for help in the [community chat](https://qm.qq.com/q/Nnz7Vbj8OU) diff --git a/docs/en/plugin/plugin-intro.md b/docs/en/plugin/plugin-intro.md new file mode 100644 index 000000000..6d96cf368 --- /dev/null +++ b/docs/en/plugin/plugin-intro.md @@ -0,0 +1,84 @@ +# Plugin Introduction + +LangBot supports a powerful plugin system that allows you to extend its functionality. Plugins can add new commands, event handlers, and even integrate with external services. + +## What Can Plugins Do? + +Plugins can: + +- Add new commands to the bot +- Handle and respond to various events (messages, group invitations, etc.) +- Integrate with external services and APIs +- Modify the behavior of the bot +- Add new functions that can be called by the LLM during conversations +- Extend the core components of LangBot + +## Plugin Market + +You can find and install plugins from the [LangBot Plugin Market](https://plugins.langbot.app). + +## Installing Plugins + +### Method 1: Install via WebUI + +1. Access the WebUI at `http://your-server-ip:5300` +2. Navigate to the "Plugins" section +3. Click "Install Plugin" +4. Enter the plugin's GitHub repository URL or select from the featured plugins +5. Click "Install" + +### Method 2: Manual Installation + +1. Download or clone the plugin repository +2. Place the plugin files in the `data/plugins` directory +3. Restart LangBot or use the `!plugin reload` command + +## Managing Plugins + +You can manage plugins using the following commands: + +``` +!plugin list +``` + +Lists all installed plugins. + +``` +!plugin enable +``` + +Enables the specified plugin. + +``` +!plugin disable +``` + +Disables the specified plugin. + +``` +!plugin info +``` + +Displays information about the specified plugin. + +``` +!plugin reload +``` + +Reloads all plugins. + +## Popular Plugins + +Here are some popular plugins for LangBot: + +- **WebSearch**: Allows the bot to search the web and provide information +- **ImageGen**: Generates images using AI models like DALL-E or Stable Diffusion +- **Weather**: Provides weather information for specified locations +- **Translator**: Translates text between languages +- **Calculator**: Performs mathematical calculations +- **Schedule**: Schedules messages to be sent at specific times +- **GroupManager**: Provides group management features like welcome messages and anti-spam + +## Developing Plugins + +If you want to develop your own plugins, check out the [Plugin Development Tutorial](/en/plugin/dev/tutor). diff --git a/docs/en/workshop/dify-service-api.md b/docs/en/workshop/dify-service-api.md new file mode 100644 index 000000000..c6ab0315c --- /dev/null +++ b/docs/en/workshop/dify-service-api.md @@ -0,0 +1,176 @@ +# How to Integrate with Dify? + +This guide explains how to integrate LangBot with Dify, a powerful LLMOps platform that allows you to create and deploy AI applications. + +## What is Dify? + +[Dify](https://dify.ai) is an open-source LLMOps platform that helps you build, deploy, and monitor AI applications. It provides a user-friendly interface for creating AI applications without writing code, as well as APIs for integrating with other systems. + +## Integration Methods + +There are two ways to integrate LangBot with Dify: + +1. **Service API**: Use Dify's Service API to access applications created in Dify +2. **Completion API**: Use Dify's Completion API as a model provider in LangBot + +This guide focuses on the Service API integration, which is the recommended approach for most use cases. + +## Prerequisites + +Before you begin, you need: + +1. A Dify account and a deployed application +2. The API key for your Dify application +3. LangBot version 4.0 or higher + +## Step 1: Create a Dify Application + +1. Sign up or log in to [Dify](https://dify.ai) +2. Create a new application or use an existing one +3. Configure your application with the desired model, prompt, and other settings +4. Deploy your application +5. Go to the API Reference section and copy your API key + +## Step 2: Configure LangBot + +### Method 1: Configure via WebUI + +1. Access the LangBot WebUI at `http://your-server-ip:5300` +2. Navigate to the "Models" section +3. Click "Add Provider" +4. Select "Dify" as the provider type +5. Enter your Dify API key and application ID +6. Save the configuration + +### Method 2: Configure via Configuration File + +Edit the `data/config.yaml` file and add the Dify provider: + +```yaml +providers: + - id: dify + type: dify + config: + api_key: "your-dify-api-key" + application_id: "your-dify-application-id" + base_url: "https://api.dify.ai/v1" # Optional, defaults to Dify's official API endpoint +``` + +## Step 3: Test the Integration + +Send a test message to your bot: + +``` +!model use dify +Hello, how are you? +``` + +The bot should respond using your Dify application. + +## Advanced Configuration + +### Using Multiple Dify Applications + +You can configure multiple Dify applications in LangBot: + +```yaml +providers: + - id: dify-general + type: dify + config: + api_key: "your-dify-api-key-1" + application_id: "your-dify-application-id-1" + + - id: dify-specialized + type: dify + config: + api_key: "your-dify-api-key-2" + application_id: "your-dify-application-id-2" +``` + +### Configuring Input Variables + +If your Dify application uses input variables, you can configure them in LangBot: + +```yaml +providers: + - id: dify + type: dify + config: + api_key: "your-dify-api-key" + application_id: "your-dify-application-id" + input_variables: + - name: "user_name" + value: "{sender.name}" + - name: "group_name" + value: "{group.name}" +``` + +### Using Dify's File Capabilities + +If your Dify application supports file inputs, you can configure LangBot to handle them: + +```yaml +providers: + - id: dify + type: dify + config: + api_key: "your-dify-api-key" + application_id: "your-dify-application-id" + file_enabled: true +``` + +When file support is enabled, users can send files to the bot, and they will be forwarded to Dify: + +``` +!model use dify +[Attach a file] +Can you analyze this document? +``` + +## Troubleshooting + +### Connection Issues + +If you're having trouble connecting to Dify, check: + +1. Your API key is correct +2. Your application ID is correct +3. Your Dify application is deployed and active +4. Your server can access the Dify API endpoint + +### Response Format Issues + +If the responses from Dify don't look right: + +1. Check your Dify application's prompt configuration +2. Make sure your application is using the correct model +3. Verify that your application is configured for chat completion (not text completion) + +### Rate Limiting + +If you're hitting rate limits: + +1. Check your Dify plan's rate limits +2. Consider implementing rate limiting in LangBot +3. Upgrade your Dify plan if necessary + +## Example Use Cases + +### Customer Support Bot + +Create a Dify application for customer support, with knowledge of your products and services, and integrate it with LangBot to provide automated customer support in your messaging channels. + +### Content Creation Assistant + +Create a Dify application for content creation, with templates and guidelines for your brand, and integrate it with LangBot to help your team create content more efficiently. + +### Data Analysis Bot + +Create a Dify application for data analysis, with the ability to interpret and visualize data, and integrate it with LangBot to provide data insights on demand. + +## Conclusion + +Integrating LangBot with Dify allows you to leverage Dify's powerful LLMOps capabilities while using LangBot's messaging platform integrations. This combination provides a flexible and powerful solution for deploying AI applications across multiple messaging platforms. + +For more information, check out the [Dify documentation](https://docs.dify.ai) and the [LangBot documentation](https://docs.langbot.app). diff --git a/docs/en/workshop/impl-platform-adapter.md b/docs/en/workshop/impl-platform-adapter.md new file mode 100644 index 000000000..a91573c9f --- /dev/null +++ b/docs/en/workshop/impl-platform-adapter.md @@ -0,0 +1,389 @@ +# How to Implement a Message Platform Adapter? + +This guide explains how to implement a custom message platform adapter for LangBot, allowing you to connect the bot to new messaging platforms. + +## Overview + +LangBot uses a modular architecture that allows for easy integration with various messaging platforms. The core component for platform integration is the `MessageSourceAdapter`, which handles the communication between LangBot and the messaging platform. + +![architecture](/assets/image/zh/deploy/bots/arch.png) + +## Prerequisites + +Before implementing a platform adapter, you should: + +1. Understand the API of the messaging platform you want to integrate with +2. Have basic knowledge of Python and asynchronous programming +3. Be familiar with LangBot's architecture and event system + +## Implementation Steps + +### Step 1: Create a New Adapter Class + +Create a new Python file in the `langbot/bot/adapters` directory: + +```python +from langbot.bot import MessageSourceAdapter +from langbot.message import Message, PersonSource, GroupSource, Sender +from langbot.event import PersonNormalMessageReceived, GroupNormalMessageReceived + +class MyPlatformAdapter(MessageSourceAdapter): + """ + Adapter for MyPlatform. + """ + + def __init__(self, config): + """ + Initialize the adapter. + + Args: + config: The adapter configuration + """ + super().__init__(config) + self.config = config + # Initialize platform-specific client + self.client = None +``` + +### Step 2: Implement the Required Methods + +Implement the required methods for the adapter: + +```python +async def start(self): + """ + Start the adapter. + """ + # Initialize and start the platform client + self.client = MyPlatformClient( + api_key=self.config.get("api_key"), + # Other platform-specific configuration + ) + + # Register event handlers + self.client.on_message(self.on_message) + + # Start the client + await self.client.start() + +async def stop(self): + """ + Stop the adapter. + """ + # Stop the platform client + if self.client: + await self.client.stop() + +async def send_message(self, source, content, **kwargs): + """ + Send a message to a specific destination. + + Args: + source: The destination to send the message to + content: The content of the message + **kwargs: Additional arguments + + Returns: + The message ID + """ + # Send the message using the platform client + if isinstance(source, PersonSource): + # Send a direct message + message_id = await self.client.send_direct_message( + source.identity, + content, + **kwargs + ) + elif isinstance(source, GroupSource): + # Send a group message + message_id = await self.client.send_group_message( + source.identity, + content, + **kwargs + ) + else: + raise ValueError(f"Unsupported source type: {type(source)}") + + return message_id +``` + +### Step 3: Implement Event Handlers + +Implement event handlers to convert platform-specific events to LangBot events: + +```python +async def on_message(self, platform_message): + """ + Handle a message from the platform. + + Args: + platform_message: The platform-specific message + """ + # Convert the platform message to a LangBot message + message = self.convert_message(platform_message) + + # Determine the type of message and emit the appropriate event + if isinstance(message.source, PersonSource): + # Emit a person message event + await self.emit(PersonNormalMessageReceived(message)) + elif isinstance(message.source, GroupSource): + # Emit a group message event + await self.emit(GroupNormalMessageReceived(message)) + +def convert_message(self, platform_message): + """ + Convert a platform-specific message to a LangBot message. + + Args: + platform_message: The platform-specific message + + Returns: + A LangBot message + """ + # Extract message information + message_id = platform_message.get("id") + content = platform_message.get("content") + time = platform_message.get("timestamp") + + # Extract sender information + sender_id = platform_message.get("sender_id") + sender_name = platform_message.get("sender_name") + sender = Sender(sender_id, sender_name) + + # Determine the source type + if platform_message.get("is_group"): + # Group message + group_id = platform_message.get("group_id") + source = GroupSource("my_platform", group_id) + else: + # Direct message + source = PersonSource("my_platform", sender_id) + + # Create and return the message + return Message( + source=source, + content=content, + message_id=message_id, + sender=sender, + time=time + ) +``` + +### Step 4: Register the Adapter + +Register the adapter in the `langbot/bot/adapters/__init__.py` file: + +```python +from .my_platform import MyPlatformAdapter + +__all__ = [ + # Other adapters + "MyPlatformAdapter", +] +``` + +### Step 5: Add Configuration Schema + +Add the configuration schema for your adapter in the `langbot/schema/bot.py` file: + +```python +MY_PLATFORM_SCHEMA = { + "type": "object", + "properties": { + "type": {"type": "string", "enum": ["my_platform"]}, + "api_key": {"type": "string"}, + # Other platform-specific configuration + }, + "required": ["type", "api_key"], +} + +# Add to the BOT_SCHEMA +BOT_SCHEMA = { + "type": "object", + "properties": { + "bots": { + "type": "array", + "items": { + "oneOf": [ + # Other adapter schemas + MY_PLATFORM_SCHEMA, + ], + }, + }, + }, +} +``` + +## Example: Implementing a Discord Adapter + +Here's a simplified example of implementing a Discord adapter: + +```python +import discord +from langbot.bot import MessageSourceAdapter +from langbot.message import Message, PersonSource, GroupSource, Sender +from langbot.event import PersonNormalMessageReceived, GroupNormalMessageReceived + +class DiscordAdapter(MessageSourceAdapter): + """ + Adapter for Discord. + """ + + def __init__(self, config): + """ + Initialize the adapter. + + Args: + config: The adapter configuration + """ + super().__init__(config) + self.config = config + self.client = discord.Client(intents=discord.Intents.all()) + + # Register event handlers + @self.client.event + async def on_ready(): + print(f"Logged in as {self.client.user}") + + @self.client.event + async def on_message(message): + # Ignore messages from the bot itself + if message.author == self.client.user: + return + + # Convert and emit the message + await self.on_message(message) + + async def start(self): + """ + Start the adapter. + """ + # Start the Discord client + await self.client.start(self.config.get("token")) + + async def stop(self): + """ + Stop the adapter. + """ + # Stop the Discord client + if self.client: + await self.client.close() + + async def send_message(self, source, content, **kwargs): + """ + Send a message to a specific destination. + + Args: + source: The destination to send the message to + content: The content of the message + **kwargs: Additional arguments + + Returns: + The message ID + """ + # Send the message using the Discord client + if isinstance(source, PersonSource): + # Send a direct message + user = await self.client.fetch_user(int(source.identity)) + message = await user.send(content) + return str(message.id) + elif isinstance(source, GroupSource): + # Send a group message + channel = self.client.get_channel(int(source.identity)) + message = await channel.send(content) + return str(message.id) + else: + raise ValueError(f"Unsupported source type: {type(source)}") + + async def on_message(self, discord_message): + """ + Handle a message from Discord. + + Args: + discord_message: The Discord message + """ + # Convert the Discord message to a LangBot message + message = self.convert_message(discord_message) + + # Determine the type of message and emit the appropriate event + if isinstance(message.source, PersonSource): + # Emit a person message event + await self.emit(PersonNormalMessageReceived(message)) + elif isinstance(message.source, GroupSource): + # Emit a group message event + await self.emit(GroupNormalMessageReceived(message)) + + def convert_message(self, discord_message): + """ + Convert a Discord message to a LangBot message. + + Args: + discord_message: The Discord message + + Returns: + A LangBot message + """ + # Extract message information + message_id = str(discord_message.id) + content = discord_message.content + time = discord_message.created_at.timestamp() + + # Extract sender information + sender_id = str(discord_message.author.id) + sender_name = discord_message.author.name + sender = Sender(sender_id, sender_name) + + # Determine the source type + if discord_message.guild: + # Group message + group_id = str(discord_message.channel.id) + source = GroupSource("discord", group_id) + else: + # Direct message + source = PersonSource("discord", sender_id) + + # Create and return the message + return Message( + source=source, + content=content, + message_id=message_id, + sender=sender, + time=time + ) +``` + +## Testing Your Adapter + +To test your adapter: + +1. Add your adapter configuration to the `data/config.yaml` file: + +```yaml +bots: + - id: my_platform + type: my_platform + config: + api_key: your-api-key + # Other platform-specific configuration +``` + +2. Start LangBot and check the logs for any errors +3. Send a test message on your platform and verify that LangBot receives and processes it +4. Send a test message from LangBot to your platform and verify that it's delivered correctly + +## Best Practices + +- Handle errors gracefully and provide meaningful error messages +- Implement proper rate limiting to avoid being blocked by the platform +- Use asynchronous programming to avoid blocking the event loop +- Document your adapter thoroughly, including configuration options and limitations +- Follow the platform's terms of service and API guidelines +- Implement proper logging to help with debugging +- Consider implementing reconnection logic for platforms with unstable connections +- Test your adapter thoroughly with different types of messages and scenarios + +## Conclusion + +Implementing a custom message platform adapter allows you to extend LangBot to work with new messaging platforms. By following the steps and best practices outlined in this guide, you can create a robust and reliable adapter that integrates seamlessly with LangBot's architecture. + +If you encounter any issues or have questions, feel free to ask in the [community chat](https://qm.qq.com/q/Nnz7Vbj8OU) or open an issue on [GitHub](https://github.com/RockChinQ/LangBot/issues). From c50e58142cac7f0a12c9500a5dec2b49e2abfa5c Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 15:30:11 +0000 Subject: [PATCH 02/17] Update English model documentation to match Chinese structure Co-Authored-By: Junyan Qin --- docs/en/deploy/models/readme.md | 114 +++++--------------------------- 1 file changed, 16 insertions(+), 98 deletions(-) diff --git a/docs/en/deploy/models/readme.md b/docs/en/deploy/models/readme.md index 9f77b28a3..bc8e5455b 100644 --- a/docs/en/deploy/models/readme.md +++ b/docs/en/deploy/models/readme.md @@ -1,113 +1,31 @@ -# Configure Dialogue Models +# Configure Models (Models) -LangBot supports multiple model providers, including OpenAI, Anthropic, Moonshot, Baichuan, and more. +Models will be used by pipelines to process messages. The first model you configure will be set as the model for the default pipeline. -## Configuration Methods +![arch](/assets/image/zh/deploy/models/arch.png) -### Method 1: Configure via WebUI +You can add multiple models, and then select which model to use in the pipeline. -After deploying LangBot, access the WebUI at `http://your-server-ip:5300` and configure the models in the "Models" section. +![Model Configuration](/assets/image/zh/deploy/models/model_config.png) -### Method 2: Configure via Configuration File +Enter these four parameters: `Model Name`, `Model Provider`, `Request URL`, and `API Key`, then submit. -Edit the `data/config.yaml` file and add your model configuration under the `providers` section: +For model capabilities, please choose according to the specific model characteristics: -```yaml -providers: - - id: openai - type: openai - config: - api_key: "your-api-key" - base_url: "https://api.openai.com/v1" - models: - - gpt-3.5-turbo - - gpt-4 - - gpt-4-turbo -``` +- Visual Capability: Needs to be enabled to recognize images -## Supported Providers +- Function Calling: Needs to be enabled to use Agent tools in conversations -### OpenAI +:::info -```yaml -providers: - - id: openai - type: openai - config: - api_key: "your-api-key" - base_url: "https://api.openai.com/v1" - models: - - gpt-3.5-turbo - - gpt-4 - - gpt-4-turbo -``` +*If you don't have an API Key, you can [get one from this relay station](https://api.qhaigc.net/)* -### Anthropic +If you need to use third-party API relays or other model providers, -```yaml -providers: - - id: anthropic - type: anthropic - config: - api_key: "your-api-key" - models: - - claude-3-opus-20240229 - - claude-3-sonnet-20240229 - - claude-3-haiku-20240307 -``` +Select `OpenAI` -### Moonshot +Fill in the relay's Base URL for the Request URL, for example: -```yaml -providers: - - id: moonshot - type: openai - config: - api_key: "your-api-key" - base_url: "https://api.moonshot.cn/v1" - models: - - moonshot-v1-8k - - moonshot-v1-32k - - moonshot-v1-128k -``` +![Model Configuration](/assets/image/zh/deploy/models/other_provider.png) -### OneAPI - -```yaml -providers: - - id: oneapi - type: openai - config: - api_key: "your-api-key" - base_url: "https://your-oneapi-url/v1" - models: - - gpt-3.5-turbo - - gpt-4 - - claude-3-opus-20240229 -``` - -## Default Model Configuration - -You can set the default model in the `data/config.yaml` file: - -```yaml -system: - default_provider: openai - default_model: gpt-3.5-turbo -``` - -## Testing Your Configuration - -After configuring your models, you can test them by sending a message to your bot: - -``` -!model list -``` - -This command will list all available models. - -To switch to a specific model: - -``` -!model use gpt-4 -``` +::: From dbeb04eb38f04d264db7621e66241c6862f32c92 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 15:32:21 +0000 Subject: [PATCH 03/17] Update English documentation to match Chinese structure Co-Authored-By: Junyan Qin --- docs/en/deploy/command.md | 175 ++++------------------------- docs/en/deploy/pipelines/readme.md | 150 +++++-------------------- docs/en/deploy/platforms/readme.md | 2 +- 3 files changed, 46 insertions(+), 281 deletions(-) diff --git a/docs/en/deploy/command.md b/docs/en/deploy/command.md index 8497ede29..c850d9f8a 100644 --- a/docs/en/deploy/command.md +++ b/docs/en/deploy/command.md @@ -1,163 +1,28 @@ -# Dialogue Commands +# Command Details -LangBot provides various commands that can be used during conversations to control the bot's behavior. +> Parameters in `<>` are required, do not include `<>` when using +> Parameters in `[]` are optional, do not include `[]` when using +> Here we only list the commands briefly, for detailed usage please use the `!cmd` command -## Command Prefix +## Command List -Commands start with a prefix, which by default is `!` or `!`. You can configure the command prefixes in the `data/config.yaml` file: - -```yaml -trigger: - command_prefixes: - - "!" - - "!" -``` - -## Available Commands - -### Help Command +Available to any object ``` -!help +!help Display custom help information (can be changed in the configuration file) +!cmd [command name] Display command list or detailed information for a specific command +!list [page number] List the history sessions for this object +!del Delete the specified history record, you can view the number with !list +!del all Delete all history records for this session object +!last Switch to the previous session +!next Switch to the next session +!reset Reset the current session +!prompt View all records of the current session +!version Check the current version and check for updates +!resend Rollback the previous request +!plugin For usage, please see the `Management` section of the plugin introduction page ``` -Displays a list of available commands. +## Command Permission Control -### Model Commands - -``` -!model list -``` - -Lists all available models. - -``` -!model use -``` - -Switches to the specified model. - -``` -!model info -``` - -Displays information about the current model. - -### Context Commands - -``` -!context clear -``` - -Clears the current conversation context. - -``` -!context show -``` - -Displays the current conversation context. - -``` -!context length -``` - -Shows the length of the current context in tokens. - -### Pipeline Commands - -``` -!pipeline list -``` - -Lists all available pipelines. - -``` -!pipeline use -``` - -Switches to the specified pipeline. - -``` -!pipeline info -``` - -Displays information about the current pipeline. - -### System Commands - -``` -!system info -``` - -Displays system information, including version, uptime, and memory usage. - -``` -!system reload -``` - -Reloads the system configuration. - -### Plugin Commands - -``` -!plugin list -``` - -Lists all installed plugins. - -``` -!plugin enable -``` - -Enables the specified plugin. - -``` -!plugin disable -``` - -Disables the specified plugin. - -``` -!plugin info -``` - -Displays information about the specified plugin. - -### Preset Commands - -``` -!preset list -``` - -Lists all available presets. - -``` -!preset use -``` - -Switches to the specified preset. - -``` -!preset info -``` - -Displays information about the current preset. - -## Command Permissions - -You can configure command permissions in the `data/config.yaml` file: - -```yaml -command: - permissions: - - command: "system reload" - roles: - - admin - - command: "plugin *" - roles: - - admin -``` - -## Custom Commands - -Plugins can register custom commands. Check the plugin documentation for available commands. +You can [edit the system environment settings](/en/deploy/settings.md) to set permissions for command nodes. When a command is initiated, if the user's permission level (administrator is `2`, regular user is `1`) is greater than or equal to the command node's permission level, the command can be successfully executed. diff --git a/docs/en/deploy/pipelines/readme.md b/docs/en/deploy/pipelines/readme.md index 05b0803cb..a5c226a39 100644 --- a/docs/en/deploy/pipelines/readme.md +++ b/docs/en/deploy/pipelines/readme.md @@ -1,148 +1,48 @@ -# Modify Dialogue Pipeline +# Modify Dialogue Pipeline Configuration (Pipelines) -The dialogue pipeline is the core processing flow of LangBot, which handles user messages and generates responses. +The pipeline controls the processing flow after receiving a message, as well as the information exchange with the large language model. Each bot can be bound to one pipeline, and the same pipeline can be bound to multiple bots. -## Pipeline Structure +LangBot automatically creates a default pipeline when first started. When creating a bot, it is automatically bound to the default pipeline. When creating the first model, it is automatically set as the model for the default pipeline. -![pipeline](/assets/image/zh/deploy/bots/arch.png) +![arch](/assets/image/zh/deploy/pipelines/arch.png) -The pipeline consists of the following components: +You can create multiple pipelines for different bots to adapt to different scenarios. -1. **Trigger**: Determines whether to process a message -2. **Moderation**: Filters and processes messages before sending to the model -3. **AI**: Sends the message to the LLM and receives a response -4. **Output**: Processes the model's response before sending it back to the user -## Configuration Methods -### Method 1: Configure via WebUI +The current pipeline can be configured with the following features: -After deploying LangBot, access the WebUI at `http://your-server-ip:5300` and configure the pipeline in the "Pipeline" section. +## AI Capabilities -### Method 2: Configure via Configuration File +You can choose to use: regular large language model `Built-in Agent`, [`Dify`](https://dify.ai/), [`Alibaba Cloud Bailian`](https://www.aliyun.com/product/bailian?source=5176.29345612&userCode=ys4ad8gs) -Edit the `data/config.yaml` file and modify the `pipelines` section: +AI capabilities are mainly divided into two parts: selecting the runner and configuring runner parameters. -```yaml -pipelines: - - id: default - components: - - id: trigger - type: trigger - config: - # Trigger configuration - - id: moderation - type: moderation - config: - # Moderation configuration - - id: ai - type: ai - config: - # AI configuration - - id: output - type: output - config: - # Output configuration -``` +The runner defines how to schedule the large model to process messages. The default is `Built-in Agent`, which is a multi-round Agent strategy implemented by LangBot. Only when this runner is selected will the models and tools configured internally in LangBot be used. -## Component Configuration +** -### Trigger Component +edit_pipeline -The trigger component determines whether a message should be processed by the pipeline. +You can also choose to use external LLMOps platforms such as `Dify`, [`Alibaba Cloud Bailian`](https://www.aliyun.com/product/bailian?source=5176.29345612&userCode=ys4ad8gs). In this case, the models, prompts, tools, and other resources used will be provided by the LLMOps platform. -```yaml -- id: trigger - type: trigger - config: - # Whether to respond to messages that start with a command prefix - command_prefix: true - # Command prefixes (characters that trigger the bot) - command_prefixes: - - "!" - - "!" - # Whether to respond to messages that mention the bot - at: true - # Whether to respond to private messages - private: true - # Whether to respond to group messages without being triggered by command prefix or mention - group_chat: false -``` +more_runner -### Moderation Component +- For detailed steps on connecting to Dify, you can [refer to the tutorial](/en/workshop/dify-service-api.html). -The moderation component filters and processes messages before sending them to the model. +### Conversation Variables -```yaml -- id: moderation - type: moderation - config: - # Whether to enable sensitive word filtering - sensitive_word_filter: true - # Whether to enable rate limiting - rate_limit: true - # Rate limit configuration - rate_limit_config: - # Number of messages allowed per time period - count: 10 - # Time period in seconds - period: 60 - # Whether to enable context management - context: true - # Context configuration - context_config: - # Maximum number of messages to include in the context - max_count: 10 - # Maximum context length in tokens - max_tokens: 4000 -``` +When using Dify or Alibaba Cloud Bailian, LangBot explicitly passes the following parameters, which you can add to the start node of the Dify workflow: -### AI Component +- `user_message_text`: The plain text of the user message +- `session_id`: User session ID, `person_` for private chat, `group_` for group chat +- `conversation_id`: String, user session ID, generated by LangBot. It will be regenerated after the user resets the session +- `msg_create_time`: Numeric type, timestamp (seconds) when this message was received -The AI component sends the message to the LLM and receives a response. +You can [customize any variable through plugins](/en/plugin/dev/api-ref.html#%E8%AE%BE%E7%BD%AE%E8%AF%B7%E6%B1%82%E5%8F%98%E9%87%8F). -```yaml -- id: ai - type: ai - config: - # System prompt - system_prompt: "You are LangBot, a helpful assistant." - # Whether to enable function calling - function_call: true - # Whether to enable streaming responses - stream: true -``` +![Dify Workflow Start Node Configuration](/assets/image/zh/workshop/dify-service-api/dify_workflow_var.png) -### Output Component +## Other Configurations -The output component processes the model's response before sending it back to the user. - -```yaml -- id: output - type: output - config: - # Whether to enable message splitting for long responses - split: true - # Maximum length of a single message - max_length: 1000 -``` - -## Multiple Pipelines - -You can configure multiple pipelines for different scenarios: - -```yaml -pipelines: - - id: default - # Default pipeline configuration - - id: image-generation - # Pipeline for image generation - - id: code-assistant - # Pipeline for code assistance -``` - -To switch between pipelines, use the following command: - -``` -!pipeline use image-generation -``` +Please refer to the descriptions in the configuration form. diff --git a/docs/en/deploy/platforms/readme.md b/docs/en/deploy/platforms/readme.md index 972d7fbdf..ab981fbbd 100644 --- a/docs/en/deploy/platforms/readme.md +++ b/docs/en/deploy/platforms/readme.md @@ -1,4 +1,4 @@ -# Configure Bots +# Configure Bots (Bots) Bots are used to receive message events from messaging platforms and call pipelines to process messages. From af72d002dd2f6620bc72ed5c7824f8b63a0da1a0 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 15:34:30 +0000 Subject: [PATCH 04/17] Update update.md and plugin-intro.md to match Chinese structure Co-Authored-By: Junyan Qin --- docs/en/deploy/update.md | 88 ++----------------------- docs/en/plugin/plugin-intro.md | 116 ++++++++++++++------------------- 2 files changed, 54 insertions(+), 150 deletions(-) diff --git a/docs/en/deploy/update.md b/docs/en/deploy/update.md index a930fc649..47d1e6795 100644 --- a/docs/en/deploy/update.md +++ b/docs/en/deploy/update.md @@ -1,93 +1,15 @@ # Update LangBot -This guide explains how to update your LangBot installation to the latest version. +## Using Docker (or one-click deployment based on Docker) -## Docker Deployment - -If you deployed LangBot using Docker, follow these steps to update: - -1. Pull the latest image: +Please execute in the `docker-compose.yaml` directory: ```bash -docker pull rockchin/langbot:latest +docker compose up --force-recreate --pull always -d ``` -2. Stop and remove the current container: - -```bash -docker stop langbot -docker rm langbot -``` - -3. Start a new container with the latest image: - -```bash -docker run -d --name langbot \ - -p 5300:5300 \ - -v /path/to/data:/app/data \ - rockchin/langbot:latest -``` - -Replace `/path/to/data` with the path to your data directory. +This command will pull the latest LangBot image and rebuild the container. ## Manual Deployment -If you deployed LangBot manually, follow these steps to update: - -1. Navigate to your LangBot directory: - -```bash -cd /path/to/langbot -``` - -2. Pull the latest changes: - -```bash -git pull -``` - -3. Update dependencies: - -```bash -pip install -r requirements.txt -``` - -4. Restart LangBot: - -```bash -python main.py -``` - -## Baota Panel Deployment - -If you deployed LangBot using the Baota Panel, follow these steps to update: - -1. Log in to your Baota Panel -2. Navigate to the LangBot website -3. Click on "Site Settings" -4. Click on "Update from Git" -5. Click "Update" to pull the latest changes -6. Restart the website - -## Configuration Changes - -After updating, check the [release notes](https://github.com/RockChinQ/LangBot/releases) for any configuration changes. You may need to update your `data/config.yaml` file to accommodate new features or changes. - -## Database Migration - -LangBot automatically handles database migrations during updates. However, it's always a good idea to back up your data directory before updating. - -## Troubleshooting - -If you encounter issues after updating, check the following: - -1. Make sure your configuration file is compatible with the new version -2. Check the logs for any error messages -3. Ensure all dependencies are installed correctly -4. Verify that your model providers are still configured correctly - -If problems persist, you can: - -1. Join the [community chat](https://qm.qq.com/q/Nnz7Vbj8OU) for help -2. Open an issue on [GitHub](https://github.com/RockChinQ/LangBot/issues) -3. Revert to a previous version if necessary +Download the latest release's `langbot--all.zip` from the Assets section on the [Releases](https://github.com/RockChinQ/LangBot/releases) page, extract it to the original deployment directory, and restart. diff --git a/docs/en/plugin/plugin-intro.md b/docs/en/plugin/plugin-intro.md index 6d96cf368..1b2eda605 100644 --- a/docs/en/plugin/plugin-intro.md +++ b/docs/en/plugin/plugin-intro.md @@ -1,84 +1,66 @@ # Plugin Introduction -LangBot supports a powerful plugin system that allows you to extend its functionality. Plugins can add new commands, event handlers, and even integrate with external services. +:::info Table of Contents +[[toc]] +::: -## What Can Plugins Do? +## Plugin Categories -Plugins can: +**Plugins** are extension modules for LangBot, containing two types of executable content: **event listeners** and **content functions**. Event listeners are driven by events in the main program, while content functions are driven by content generated by GPT. Both driving methods are uniformly wrapped in the plugin class. -- Add new commands to the bot -- Handle and respond to various events (messages, group invitations, etc.) -- Integrate with external services and APIs -- Modify the behavior of the bot -- Add new functions that can be called by the LLM during conversations -- Extend the core components of LangBot +### Event Listeners -## Plugin Market +Plugins listen to various events during LangBot's operation. When an event occurs, LangBot calls the listener registered by the plugin with event-related information. Plugins can register listeners to be triggered in specific situations. -You can find and install plugins from the [LangBot Plugin Market](https://plugins.langbot.app). +### Content Functions -## Installing Plugins +Content functions differ from event-triggered plugins in that during a conversation, when the conversation content matches the description of a content function, GPT specifies which function to call, and LangBot makes the call. +This is implemented based on [GPT's Function Calling capability](https://platform.openai.com/docs/guides/gpt/function-calling), which is a function embedded in the conversation and automatically called by GPT. -### Method 1: Install via WebUI +> It's like the official ChatGPT plugins -1. Access the WebUI at `http://your-server-ip:5300` -2. Navigate to the "Plugins" section -3. Click "Install Plugin" -4. Enter the plugin's GitHub repository URL or select from the featured plugins -5. Click "Install" - -### Method 2: Manual Installation - -1. Download or clone the plugin repository -2. Place the plugin files in the `data/plugins` directory -3. Restart LangBot or use the `!plugin reload` command - -## Managing Plugins - -You can manage plugins using the following commands: +For example, if we provide GPT with a function `access_the_web` and provide detailed descriptions of it and its parameters, then when we talk to GPT about content similar to the following: ``` -!plugin list +Q: Please search for QQ bot projects on GitHub? +Q: Please search for some good cloud service provider websites for me? +Q: Read and summarize this article: https://zhuanlan.zhihu.com/p/607570830 +Q: Search for the weather in Qingyuan today ``` -Lists all installed plugins. +GPT will respond with a function call request for `access_the_web`, LangBot will call the content function provided by the plugin, and return the call result to GPT for it to generate a new response. +Of course, the function calling feature is not limited to web access, it can also implement image processing, scientific calculations, trip planning, and other functions that require function calls. Theoretically, we can implement the same functionality as `ChatGPT Plugins` through content functions. + +## Plugin Usage + +### Installation + +> This only provides the basic installation method for plugins, for details please check each plugin's documentation or consult its developer + +#### Install from Plugin GitHub Repository + +In the WebUI's plugin management page, click the `Install` button, enter the plugin's GitHub repository address, and click the `Install` button. + +#### From Plugin Market + +Install from the plugin market in the WebUI's plugin management page. + +#### Manual Installation + +Place the obtained plugin program in the `plugins` directory. For specific usage, please check each plugin's documentation or consult its developer. + +### Management + +#### !plugin Commands ``` -!plugin enable +!plugin List all installed plugins +!plugin get Install a plugin from a GitHub repository address (requires administrator privileges) +!plugin update all Update all plugins (requires administrator privileges, only supports plugins installed from repositories) +!plugin update Update the specified plugin +!plugin del Delete a plugin (requires administrator privileges) +!plugin on Enable a plugin (requires administrator privileges) +!plugin off Disable a plugin (requires administrator privileges) + +!func List all content functions ``` - -Enables the specified plugin. - -``` -!plugin disable -``` - -Disables the specified plugin. - -``` -!plugin info -``` - -Displays information about the specified plugin. - -``` -!plugin reload -``` - -Reloads all plugins. - -## Popular Plugins - -Here are some popular plugins for LangBot: - -- **WebSearch**: Allows the bot to search the web and provide information -- **ImageGen**: Generates images using AI models like DALL-E or Stable Diffusion -- **Weather**: Provides weather information for specified locations -- **Translator**: Translates text between languages -- **Calculator**: Performs mathematical calculations -- **Schedule**: Schedules messages to be sent at specific times -- **GroupManager**: Provides group management features like welcome messages and anti-spam - -## Developing Plugins - -If you want to develop your own plugins, check out the [Plugin Development Tutorial](/en/plugin/dev/tutor). From 87a90ec4e74d2f4c535456fd31f18c8ea7537c87 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 15:35:27 +0000 Subject: [PATCH 05/17] Update community.md to match Chinese structure Co-Authored-By: Junyan Qin --- docs/en/insight/community.md | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/docs/en/insight/community.md b/docs/en/insight/community.md index 87129b60b..5e4e1e33b 100644 --- a/docs/en/insight/community.md +++ b/docs/en/insight/community.md @@ -1,24 +1,25 @@ # Community Resources -## Official Resources -- [GitHub Repository](https://github.com/RockChinQ/LangBot) -- [Documentation](https://docs.langbot.app) -- [Plugin Market](https://plugins.langbot.app) +## Simplified Tutorials +> Simplified tutorials: such as video tutorials, simpler text tutorials, suitable for beginners -## Community Resources +Collected at: https://blog.thelazy.top/tags/langbot -### Deployment Tutorials +## Project Discussion QQ Groups -- [Simplified Deployment Tutorial](https://www.bilibili.com/video/BV1Uj421y7Gu) -- [Simplified Deployment Tutorial (Text Version)](https://www.bilibili.com/read/cv31018000) +> Please read the documentation carefully before joining the group to ask questions, if you cannot solve the problem then join the group to ask -### Community Discussions +### 🥰 Community Main Groups -- [QQ Group: 1141946368](https://qm.qq.com/q/Nnz7Vbj8OU) -- [Telegram Group](https://t.me/+Ij8xM9pPToxmNDQ1) -- [Discord Server](https://discord.gg/Nnz7Vbj8OU) +Answering various questions -## Contribution +- LangBot Community (3): 966235608 ([Link](https://qm.qq.com/q/JLi38whHum)) +- [Full] LangBot Community (2): 1030838208 ([Link](https://qm.qq.com/q/PF9OuQCCcM)) +- [Full] LangBot Community (1): 619154800 ([Link](https://qm.qq.com/q/1K9GjQuza)) -If you want to contribute to LangBot, please read the [Development Configuration](/en/develop/dev-config) and [Component Architecture](/en/develop/comp-arch) sections. +### 🧩 Plugin Development Group + +Plugin development, publishing, and discussion + +- LangBot Plugin Development: 1032327138 ([Link](https://qm.qq.com/q/G7ENGTjeou)) From 313071b3b4b2d7b638582a7cbb80a15cb06a3c04 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 15:36:21 +0000 Subject: [PATCH 06/17] Update guide.md to match Chinese structure Co-Authored-By: Junyan Qin --- docs/en/insight/guide.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/en/insight/guide.md b/docs/en/insight/guide.md index 8b80c6ad7..13a28f602 100644 --- a/docs/en/insight/guide.md +++ b/docs/en/insight/guide.md @@ -12,7 +12,7 @@ The documentation is divided into the following sections: - Deployment and Usage (Must Read): Detailed steps for deploying and using LangBot - Plugins: Information related to plugin usage and development -- Workshops: Many excellent use cases for LangBot +- Practice: Many excellent use cases for LangBot - Development: Information related to participating in LangBot development ## Usage Examples @@ -21,6 +21,7 @@ The documentation is divided into the following sections: ![group_chat](/assets/image/insight/group_chat.png) + ## Quick Start Guide > Two ways to get started quickly: From cbc6d37fb8e2396707400816e6e89e84ef825999 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 15:37:04 +0000 Subject: [PATCH 07/17] Update comp-arch.md to match Chinese structure Co-Authored-By: Junyan Qin --- docs/en/develop/comp-arch.md | 95 +----------------------------------- 1 file changed, 1 insertion(+), 94 deletions(-) diff --git a/docs/en/develop/comp-arch.md b/docs/en/develop/comp-arch.md index cf2042776..f1606d73d 100644 --- a/docs/en/develop/comp-arch.md +++ b/docs/en/develop/comp-arch.md @@ -1,96 +1,3 @@ # Component Architecture -This document describes the component architecture of LangBot, which is designed to be modular and extensible. - -## Overview - -LangBot is built around a component-based architecture that allows for easy extension and customization. The main components are: - -1. **Bots**: Interface with messaging platforms -2. **Pipelines**: Process messages and generate responses -3. **Models**: Connect to LLM providers -4. **Plugins**: Extend functionality - -![architecture](/assets/image/zh/deploy/bots/arch.png) - -## Bots - -Bots are responsible for connecting to messaging platforms and handling message events. Each bot implements a specific platform adapter that translates between the platform's API and LangBot's internal event system. - -Key classes: -- `MessageSourceAdapter`: Base class for all platform adapters -- `EventConverter`: Converts platform-specific events to LangBot events -- `MessageConverter`: Converts platform-specific messages to LangBot messages - -## Pipelines - -Pipelines process messages through a series of components: - -1. **Trigger**: Determines whether to process a message -2. **Moderation**: Filters and processes messages before sending to the model -3. **AI**: Sends the message to the LLM and receives a response -4. **Output**: Processes the model's response before sending it back to the user - -Each component in the pipeline can be configured and customized. - -## Models - -The Models component manages connections to various LLM providers, such as OpenAI, Anthropic, and others. It provides a unified interface for sending requests to these providers and handling their responses. - -Key classes: -- `ModelProvider`: Base class for all model providers -- `ModelRequest`: Represents a request to a model -- `ModelResponse`: Represents a response from a model - -## Plugins - -The plugin system allows for extending LangBot's functionality without modifying the core code. Plugins can: - -- Register event handlers -- Add new commands -- Provide functions that can be called by the LLM -- Extend core components - -Key concepts: -- `@handler`: Decorator for registering event handlers -- `@llm_func`: Decorator for registering functions that can be called by the LLM -- `APIHost`: Provides access to LangBot's API from plugins -- `manifest.yaml`: Configuration file for plugins - -## Event System - -LangBot uses an event-driven architecture where different components communicate through events. Events include: - -- Message events (received, sent) -- Group events (joined, left, invited) -- System events (startup, shutdown) -- Custom events defined by plugins - -## Configuration System - -The configuration system uses a YAML-based approach with the main configuration file at `data/config.yaml`. The configuration is structured around the component architecture: - -```yaml -bots: - # Bot configurations -pipelines: - # Pipeline configurations -providers: - # Model provider configurations -plugins: - # Plugin configurations -system: - # System configurations -``` - -## Development Workflow - -When developing for LangBot, follow these steps: - -1. Understand the component you want to modify or extend -2. Create a plugin if you're adding new functionality -3. Implement the necessary classes and methods -4. Test your changes -5. Submit a pull request - -For more detailed information on development, see the [Development Configuration](/en/develop/dev-config) page. +![Component Architecture Diagram](/assets/image/zh/develop/comp_arch/develop_comp_arch_1.png) From a8800cd873121ce0163ac529319b427f31c7e19e Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 15:37:56 +0000 Subject: [PATCH 08/17] Update dev-config.md to match Chinese structure Co-Authored-By: Junyan Qin --- docs/en/develop/dev-config.md | 147 ++++------------------------------ 1 file changed, 14 insertions(+), 133 deletions(-) diff --git a/docs/en/develop/dev-config.md b/docs/en/develop/dev-config.md index 417dafddd..1da578098 100644 --- a/docs/en/develop/dev-config.md +++ b/docs/en/develop/dev-config.md @@ -1,160 +1,41 @@ # Development Configuration -This guide explains how to set up a development environment for LangBot and contribute to the project. +> This document is based on LangBot version 4.0 -## Prerequisites +LangBot is divided into frontend and backend. The frontend is developed using Next.js + shadcn, and the backend is developed using Quart (an asynchronous version of Flask). -Before you start, make sure you have the following installed: +## Backend -- Python 3.10 or higher -- Git -- Node.js 16 or higher (for WebUI development) -- A code editor (VS Code recommended) +The code is located in the `pkg` directory and is started by the `main.py` file in the root directory. -## Setting Up the Development Environment - -1. Fork the LangBot repository on GitHub -2. Clone your fork: - -```bash -git clone https://github.com/your-username/LangBot.git -cd LangBot -``` - -3. Create a virtual environment: - -```bash -python -m venv venv -source venv/bin/activate # On Windows: venv\Scripts\activate -``` - -4. Install dependencies: +Install dependencies ```bash pip install -r requirements.txt -pip install -r requirements-dev.txt ``` -5. Set up pre-commit hooks: +Start the backend ```bash -pre-commit install +python main.py ``` -## Project Structure +At this point, the configuration file will be automatically generated in the `data/config.yaml` file. -The LangBot project is organized as follows: +## Frontend -``` -LangBot/ -├── data/ # Data directory (configs, plugins, etc.) -├── langbot/ # Core package -│ ├── bot/ # Bot implementations -│ ├── pipeline/ # Pipeline components -│ ├── model/ # Model providers -│ ├── plugin/ # Plugin system -│ ├── webui/ # WebUI backend -│ └── utils/ # Utility functions -├── tests/ # Test cases -├── web/ # WebUI frontend -├── main.py # Entry point -└── requirements.txt # Dependencies -``` +The code is located in the `web` directory and requires Node.js. -## Running LangBot in Development Mode - -To run LangBot in development mode: - -```bash -python main.py --debug -``` - -This will start LangBot with debug logging enabled. - -## WebUI Development - -To develop the WebUI: - -1. Navigate to the web directory: - -```bash -cd web -``` - -2. Install dependencies: +Install dependencies ```bash npm install ``` -3. Start the development server: +Go to the `web/src/app/infra/http/HttpClient.ts` file and change `export const httpClient = new HttpClient('/');` at the bottom to `export const httpClient = new HttpClient('http://localhost:5300');` to ensure the frontend can access the standalone backend. + +Start debugging ```bash npm run dev ``` - -4. In another terminal, start LangBot with the WebUI enabled: - -```bash -python main.py --webui -``` - -## Testing - -LangBot uses pytest for testing. To run the tests: - -```bash -pytest -``` - -To run a specific test: - -```bash -pytest tests/test_file.py::test_function -``` - -## Code Style - -LangBot follows the PEP 8 style guide with some modifications. We use black for code formatting and isort for import sorting. - -To format your code: - -```bash -black langbot tests -isort langbot tests -``` - -## Creating a Pull Request - -1. Create a new branch for your feature or bugfix: - -```bash -git checkout -b feature/your-feature-name -``` - -2. Make your changes and commit them: - -```bash -git add . -git commit -m "Add your feature or fix" -``` - -3. Push your branch to GitHub: - -```bash -git push origin feature/your-feature-name -``` - -4. Create a pull request on GitHub - -## Documentation - -When adding new features, make sure to update the documentation. The documentation is written in Markdown and is located in the [QChatGPT-Wiki](https://github.com/RockChinQ/QChatGPT-Wiki) repository. - -## Getting Help - -If you need help with development, you can: - -- Join the [community chat](https://qm.qq.com/q/Nnz7Vbj8OU) -- Open an issue on [GitHub](https://github.com/RockChinQ/LangBot/issues) -- Check the existing documentation and code for examples From b29355b814ec56e3407fd271d67c3ec3cfcbcb04 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 15:41:04 +0000 Subject: [PATCH 09/17] Update plugin/dev/tutor.md to match Chinese structure Co-Authored-By: Junyan Qin --- docs/en/plugin/dev/tutor.md | 490 ++++++++++++++++++------------------ 1 file changed, 250 insertions(+), 240 deletions(-) diff --git a/docs/en/plugin/dev/tutor.md b/docs/en/plugin/dev/tutor.md index d207b303f..bcc86cc0a 100644 --- a/docs/en/plugin/dev/tutor.md +++ b/docs/en/plugin/dev/tutor.md @@ -1,298 +1,308 @@ # Plugin Development Tutorial -This tutorial will guide you through the process of developing a plugin for LangBot. +> Please read the [Plugin Introduction page](/en/plugin/plugin-intro) first +> It is recommended to read the source code of this project first to understand the project architecture -## Plugin Structure +> For questions and requirements, please open an issue in the repository +> **Try to solve problems on your own before asking** -A LangBot plugin consists of the following components: +:::info Table of Contents +[[toc]] +::: -1. A main Python file that contains the plugin logic -2. A `manifest.yaml` file that describes the plugin -3. Optional additional files for resources, templates, etc. +## 💬 Introduction -## Creating a Basic Plugin +This page explains the steps for developing a regular plugin. Plugins have two methods: event listeners and content functions. +Event listeners can respond to certain events when triggered and change behavior. Content functions can be called by large language models, please check the plugin introduction page for details. -Let's create a simple "Hello World" plugin that responds to a specific command. +## 💻 Quick Start -### Step 1: Create the Plugin Directory +Use [HelloPlugin](https://github.com/langbot-app/HelloPlugin) as a template to generate a plugin code repository, then clone the repository code to the `plugins` directory. -Create a directory for your plugin in the `data/plugins` directory: +Modify the parts that need to be changed in the plugin's README.md file. + +Edit `main.py` and enter the following content: + +```Python +from pkg.plugin.context import register, handler, llm_func, BasePlugin, APIHost, EventContext +from pkg.plugin.events import * # Import event classes + + +""" +When receiving a private chat or group chat message "hello", reply with "hello, !" or "hello, everyone!" +""" + + +class HelloPlugin(BasePlugin): + + # Triggered when the plugin is loaded + def __init__(self, host: APIHost): + pass + + # Asynchronous initialization + async def initialize(self): + pass + + # Triggered when a personal message is received + @handler(PersonNormalMessageReceived) + async def person_normal_message_received(self, ctx: EventContext): + msg = ctx.event.text_message # Here event is the PersonNormalMessageReceived object + if msg == "hello": # If the message is hello + + # Output debug information + self.ap.logger.debug("hello, {}".format(ctx.event.sender_id)) + + # Reply with message "hello, !" + ctx.add_return("reply", ["hello, {}!".format(ctx.event.sender_id)]) + + # Prevent the default behavior of this event (getting a reply from the API) + ctx.prevent_default() + + # Triggered when a group message is received + @handler(GroupNormalMessageReceived) + async def group_normal_message_received(self, ctx: EventContext): + msg = ctx.event.text_message # Here event is the GroupNormalMessageReceived object + if msg == "hello": # If the message is hello + + # Output debug information + self.ap.logger.debug("hello, {}".format(ctx.event.sender_id)) + + # Reply with message "hello, everyone!" + ctx.add_return("reply", ["hello, everyone!"]) + + # Prevent the default behavior of this event (getting a reply from the API) + ctx.prevent_default() + + # Triggered when the plugin is unloaded + def __del__(self): + pass -```bash -mkdir -p data/plugins/hello-plugin ``` -### Step 2: Create the Manifest File +:::info -Create a `manifest.yaml` file in your plugin directory: +Interpreting this plugin program + +- `import pkg.plugin.context` imports `register (used to register plugin classes)`, `handler (used to register event listeners)`, `llm_func (used to register content functions)`, `BasePlugin (plugin base class)`, `APIHost (API host)`, `EventContext (event context class)` and other content +- `import pkg.plugin.events` imports all supported event classes +- Declare class `HelloPlugin` inheriting from `BasePlugin`, this class can be named arbitrarily, the plugin name is only related to the parameter when calling `register` +- Declare the `__init__` method of this class, this method is optional, the code in it will be executed when the main program loads the plugin at startup +- The plugin class also supports adding an asynchronous method `async def initialize(self)` for asynchronous initialization +- `@handler` marks the method `person_normal_message_received` as an event listener, handling the `PersonNormalMessageReceived` (triggered when a private chat message is received and before getting an OpenAI reply) event, this method can be named arbitrarily, the bound event is only related to the parameter in `handler`, more supported events can be found in the `pkg.plugin.events` file or check the `API` section below +- Output debug information through the `self.ap.logger` logger, the `ap` object in the plugin class parent class is the context object of the entire program, you can access all objects in the program through this object +- The event listener method gets a second parameter `ctx` which is the context of this event, the `event` in it is the event object of this event, you can extract related parameters from it, for specific parameters that can be obtained from each event, please check the comments of each event class in the `pkg.plugin.events` file +- The event listener method extracts the `text_message` parameter from the parameters, checks if it is `hello`, if so, sets the return value `reply` to `["hello, {}!".format(ctx.event.sender_id)]`, then calls the `ctx.prevent_default()` method to prevent the original program's default behavior + - For return values supported by each event, please check the comments of each event in `pkg.plugin.events` +- Register the `GroupNormalMessageReceived` event with similar code to handle group messages + +After writing and saving, restart the main program, go to the WebUI plugin page to see the plugin +::: + +This plugin will implement: replying with `hello, !` when receiving a `hello` message in private chat, and replying with `hello, everyone!` when receiving a `hello` message in group chat. + +### Writing the Manifest File + +Starting from LangBot 4.0, manifest files will be used to register plugins. +Please create a new file `manifest.yaml` in the same directory as main.py. If it already exists, please modify it according to the format below. ```yaml -name: HelloPlugin -version: 1.0.0 -description: A simple hello world plugin -author: Your Name -entry: hello.py -requirements: - - requests>=2.28.0 +apiVersion: v1 # Do not change +kind: Plugin # Do not change +metadata: + # author and name uniquely identify a plugin + author: langbot # Plugin author, change to your name + name: Hello # Plugin name, change to your plugin name + repository: 'https://github.com/langbot-app/HelloPlugin' # Plugin repository address, change to your plugin GitHub repository address + version: 0.1.0 # Plugin version, change to your plugin version + description: # Plugin description, change to your plugin description, supports multiple languages. Language codes follow the RFC 1766 standard. + en_US: Plugin for sending hello + zh_CN: 示例插件 + label: # Plugin display name, supports multiple languages. The WebUI will display the label in the corresponding language. Language codes follow the RFC 1766 standard. + en_US: Hello + zh_CN: Hello +spec: + # Plugin configuration (optional), can configure multiple items + config: + - name: github_token # Configuration item name + label: # Configuration item display name, supports multiple languages. Language codes follow the RFC 1766 standard. + en_US: Github Token + zh_CN: Github Token + description: # Configuration item description, supports multiple languages. Optional. + en_US: Image downloading requires a Github token + zh_CN: If not filled in, image download may fail + type: string # Configuration item type, supports string, integer, float, boolean, etc. + default: '' # Configuration item default value + required: false # Whether the configuration item is required +execution: + python: + path: main.py # Plugin main program path, must be the same as the file name of the plugin entry code above + attr: HelloPlugin # Plugin class name, must be the same as the class name declared in the code above ``` -### Step 3: Create the Plugin Code +:::info +Configuration item supported types: -Create a `hello.py` file in your plugin directory: +- string: String +- integer: Integer +- float: Floating point number +- boolean: Boolean value +- array[string]: String array +- select: Dropdown box, can select one option from multiple options + - Need to configure the options option, indicating the options in the dropdown box + + ```yaml + - name: mode + label: + en_US: Mode + zh_CN: Mode + type: select + options: + - name: mode1 + label: + en_US: Mode 1 + zh_CN: Mode 1 + - name: mode2 + label: + en_US: Mode 2 + zh_CN: Mode 2 + ``` +- prompt-editor: Prompt editor. +- llm-model-selector: LLM model selector. + +After the user modifies the configuration items through the WebUI, the plugin can get the values of the configuration items in self.config. +::: + + +## ❗ Standards (Important) + +- Please keep each plugin in a separate directory for management, it is recommended to create a repository on Github to store a single plugin for easy retrieval and updates +- Naming conventions (important): + - Plugin author: Use English, case insensitive, such as `RockChinQ` + - Plugin name: Use `PascalCase`, such as `Hello`, `ExamplePlugin`, `ChineseCommands`, etc. + - Plugin repository: Please use PascalCase, it is recommended to be the same as the plugin name, such as `HelloPlugin` + - Plugin description: It is recommended to include the phrase `LangBot Plugin` +- Multiple Python program files can be stored in one directory to separate the various functions of the plugin for easy management by developers, but it is not recommended to register multiple plugins in one directory +- Please specify the dependencies required by the plugin in the `requirements.txt` file in the plugin directory, the program will automatically install the dependencies when obtaining this plugin from the repository + +## 🪝 Content Functions + +`Content functions` implemented through [GPT's Function Calling capability](https://platform.openai.com/docs/guides/gpt/function-calling), which is a function embedded in the conversation and automatically called by GPT. + +> Your plugin does not necessarily have to include content functions, please check the content function page to understand this feature first + +### Content Function Writing Steps + +1️⃣ Please first follow the steps above to write your plugin's basic structure, now please delete (of course you can also keep them, just for simplicity) the various class functions decorated by `@handler` in the above plugin content + +
+Class structure after deletion ```python -from langbot.plugin import Plugin, handler -from langbot.event import PersonNormalMessageReceived -from langbot.api import APIHost class HelloPlugin(Plugin): - def __init__(self, api_host: APIHost): - super().__init__(api_host) - self.api = api_host - - @handler(PersonNormalMessageReceived) - def on_person_message(self, ctx): - # Get the message content - message = ctx.event.message.content - - # Check if the message is the command we want to handle - if message == "!hello": - # Prevent the default handling (don't send to LLM) - ctx.prevent_default() - - # Send a response - self.api.send_message( - ctx.event.message.source, - "Hello, World! This is my first LangBot plugin." - ) + + # Triggered when the plugin is loaded + def __init__(self, plugin_host: APIHost): + pass + + # Triggered when the plugin is unloaded + def __del__(self): + pass ``` -### Step 4: Install and Test the Plugin +
-1. Restart LangBot or use the `!plugin reload` command -2. Send the `!hello` command to your bot -3. The bot should respond with "Hello, World! This is my first LangBot plugin." +2️⃣ Now we add the following function to the position of the functions we just deleted (as a class method) -## Event Handling +```Python -LangBot uses an event-driven architecture. Plugins can register handlers for various events: +# Function to add +@llm_func(name="access_the_web") # Set function name +async def access_web(self, query, url: str): + """Call this function to search about the question before you answer any questions. + - Do not search through baidu.com at any time. + - If you need to search somthing, visit https://www.google.com/search?q=xxx. + - If user ask you to open a url (start with http:// or https://), visit it directly. + - Summary the plain content result by yourself, DO NOT directly output anything in the result you got. -```python -@handler(PersonNormalMessageReceived) -def on_person_message(self, ctx): - # Handle the event - pass -``` + Args: + url(str): url to visit -Common events include: + Returns: + str: plain text content of the web page + """ + import requests + from bs4 import BeautifulSoup + # You need to first use + # pip install beautifulsoup4 + # to install dependencies -- `PersonNormalMessageReceived`: Received a message from a person -- `GroupNormalMessageReceived`: Received a message in a group -- `GroupInvitationReceived`: Received an invitation to join a group -- `FriendRequestReceived`: Received a friend request + r = requests.get( + url, + timeout=10, + headers={ + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183" + } + ) + soup = BeautifulSoup(r.text, 'html.parser') -The `ctx` parameter provides access to: + s = soup.get_text() -- `ctx.event`: The event that triggered the handler -- `ctx.prevent_default()`: Prevents the default handling of the event -- `ctx.add_return(key, value)`: Adds a return value to the event + # Remove extra blank lines or lines with only \t and spaces + s = re.sub(r'\n\s*\n', '\n', s) -## API Access + if len(s) >= 512: # Truncate the first 512 characters of the plain text content of the web page + return s[:512] -The `APIHost` provides access to LangBot's API: - -```python -# Send a message -self.api.send_message(source, content) - -# Get user information -user_info = self.api.get_user_info(user_id) - -# Get group information -group_info = self.api.get_group_info(group_id) - -# Call the LLM -response = self.api.call_llm(prompt, model="gpt-3.5-turbo") -``` - -## LLM Function Registration - -Plugins can register functions that can be called by the LLM during conversations: - -```python -import requests -from langbot.plugin import Plugin, llm_func -from langbot.api import APIHost - -class WebSearchPlugin(Plugin): - def __init__(self, api_host: APIHost): - super().__init__(api_host) - self.api = api_host - - @llm_func(name="search_the_web") - def search_web(self, query: str) -> str: - """ - Search the web for information. - - Args: - query: The search query - - Returns: - The search results - """ - # Implement web search logic - response = requests.get( - "https://api.search.com/search", - params={"q": query} - ) - return response.json()["results"] -``` - -The LLM can then call this function during conversations: + return s -``` -User: What's the weather in New York? -Bot: Let me check that for you. -[Function call: search_the_web("weather in New York")] -The current weather in New York is 72°F and sunny. ``` -## Configuration +#### Please note: -Plugins can have configuration options that users can set: +- The function's comments must be strictly written according to the required format, please check [this document](https://github.com/RockChinQ/CallingGPT/wiki/1.-Function-Format#function-format) for the specific format +- Content functions and `event listeners decorated with @handler` can coexist in the same plugin and are both controlled by the plugin switch +- Make sure that the model you are using supports the function calling feature -```python -from langbot.plugin import Plugin, config_field -from langbot.api import APIHost +3️⃣ Now your program has web access capability, restart the program, ask the bot about online content or directly send article links requesting summaries. -class WeatherPlugin(Plugin): - def __init__(self, api_host: APIHost): - super().__init__(api_host) - self.api = api_host - - @config_field(name="api_key", description="API key for the weather service") - def set_api_key(self, api_key: str): - self.api_key = api_key -``` +- This is just an example, for more efficient web access capability support plugin, please check [WebwlkrPlugin](https://github.com/RockChinQ/WebwlkrPlugin) -Users can set these options using the WebUI or the `!plugin config` command: +## 📦 Publishing to the Plugin Market -``` -!plugin config WeatherPlugin api_key=your-api-key -``` +When your plugin development is complete, please upload it to GitHub and [open an issue in the LangBot repository](https://github.com/RockChinQ/LangBot/issues/new?assignees=&labels=%E7%8B%AC%E7%AB%8B%E6%8F%92%E4%BB%B6&projects=&template=submit-plugin.yml&title=%5BPlugin%5D%3A+%E8%AF%B7%E6%B1%82%E7%99%BB%E8%AE%B0%E6%96%B0%E6%8F%92%E4%BB%B6). After review, it will be added to the plugin market and will be visible on the LangBot WebUI plugin page's plugin market in about half an hour. -## Persistent Storage +![](/assets/image/zh/plugin/dev/plugin_dev_tutor_01.png) -Plugins can store data persistently: +## 📄 Glossary -```python -# Store data -self.api.storage.set("key", "value") +### Description -# Retrieve data -value = self.api.storage.get("key") +The `ctx: EventContext` object obtained by the event listener method can be viewed in the `pkg.plugin.context` module for its structure. The `event` attribute of the `ctx` object is the event object of this event, from which you can extract related parameters. For specific parameters that can be obtained from each event, please check the comments of each event class in the `pkg.plugin.events` file. +Event return values are all **optional**, you can submit return values by calling `ctx.add_return(key: str, ret)` -# Check if a key exists -exists = self.api.storage.has("key") +### Events -# Delete a key -self.api.storage.delete("key") -``` +If an event is an event in a request (user sending a message), its event object will contain a `query` object, which includes the context data in the processing of this request (i.e., the process of handling a user sending a message, a request process may trigger multiple events). +For all supported events, please check the comments of each event class in the `pkg.plugin.events` file. +For `message chain components`, please check [Message Platform Entities](./messages) -## Advanced Features +### BasePlugin Structure -### Command Registration +#### self.ap -Plugins can register commands that users can use: +Accessing `self.ap` in the plugin's methods is an object of the `pkg.core.app.Application` class, which contains the context object of the entire program, you can access all objects in the program through this object. -```python -from langbot.plugin import Plugin, command -from langbot.api import APIHost +#### self.host -class EchoPlugin(Plugin): - def __init__(self, api_host: APIHost): - super().__init__(api_host) - self.api = api_host - - @command(name="echo", description="Echo a message") - def echo_command(self, args: str) -> str: - """ - Echo the provided message. - - Usage: !echo - """ - return args -``` +Accessing `self.host` in the plugin's methods is an object of the `pkg.plugin.context.APIHost` class, which provides some APIs of the main program, please check its source code for details. -Users can then use the command: +### API Reference -``` -!echo Hello, World! -``` +For APIs that plugins can call, please check [API Reference](./api-ref) -### Event Return Values +### Plugin Development Group -Handlers can add return values to events: +Plugin development, publishing, and discussion -```python -@handler(PersonNormalMessageReceived) -def on_person_message(self, ctx): - # Add a return value - ctx.add_return("reply", ["This is a reply"]) -``` - -### Component Extension - -Plugins can extend LangBot's core components: - -```python -from langbot.plugin import Plugin, component_extension -from langbot.api import APIHost - -class CustomTriggerPlugin(Plugin): - def __init__(self, api_host: APIHost): - super().__init__(api_host) - self.api = api_host - - @component_extension("trigger") - def custom_trigger(self, component, message): - # Implement custom trigger logic - if message.content.startswith("custom:"): - return True - return None # Let the default trigger handle it -``` - -## Publishing Your Plugin - -To publish your plugin: - -1. Create a GitHub repository for your plugin -2. Add a README.md file with installation and usage instructions -3. Add your plugin to the [LangBot Plugin Market](https://plugins.langbot.app) - -## Best Practices - -- Keep your plugin focused on a specific functionality -- Handle errors gracefully -- Provide clear documentation -- Follow Python best practices -- Test your plugin thoroughly -- Keep dependencies minimal - -## Example Plugins - -Check out these example plugins for inspiration: - -- [WebSearch](https://github.com/RockChinQ/WebSearch): Search the web for information -- [ImageGen](https://github.com/RockChinQ/ImageGen): Generate images using AI -- [Schedule](https://github.com/RockChinQ/Schedule): Schedule messages to be sent at specific times - -## Troubleshooting - -If you encounter issues with your plugin: - -1. Check the logs for error messages -2. Verify that your plugin is correctly installed -3. Make sure your plugin's dependencies are installed -4. Test your plugin with simple functionality first -5. Ask for help in the [community chat](https://qm.qq.com/q/Nnz7Vbj8OU) +- LangBot Plugin Development: 1032327138 ([Link](https://qm.qq.com/q/G7ENGTjeou)) From 0e48d2541202f8ff388c54b74a0ddca9b2616c2c Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 15:42:56 +0000 Subject: [PATCH 10/17] Update plugin/dev/api-ref.md to match Chinese structure Co-Authored-By: Junyan Qin --- docs/en/plugin/dev/api-ref.md | 742 +++++++--------------------------- 1 file changed, 147 insertions(+), 595 deletions(-) diff --git a/docs/en/plugin/dev/api-ref.md b/docs/en/plugin/dev/api-ref.md index 54b1efb41..d6c9f11b4 100644 --- a/docs/en/plugin/dev/api-ref.md +++ b/docs/en/plugin/dev/api-ref.md @@ -1,612 +1,164 @@ # API Reference -This document provides a reference for the LangBot API that is available to plugins. - -## APIHost - -The `APIHost` class provides access to LangBot's API from plugins. It is passed to the plugin's constructor. - -```python -from langbot.api import APIHost - -class MyPlugin(Plugin): - def __init__(self, api_host: APIHost): - super().__init__(api_host) - self.api = api_host -``` - -## Message API - -### send_message - -Sends a message to a specific destination. - -```python -def send_message(self, source, content, **kwargs) -> str: - """ - Send a message to a specific destination. - - Args: - source: The destination to send the message to - content: The content of the message - **kwargs: Additional arguments - - Returns: - The message ID - """ -``` - -Example: - -```python -message_id = self.api.send_message( - ctx.event.message.source, - "Hello, World!" -) -``` - -### reply_message - -Replies to a specific message. - -```python -def reply_message(self, message, content, **kwargs) -> str: - """ - Reply to a specific message. - - Args: - message: The message to reply to - content: The content of the reply - **kwargs: Additional arguments - - Returns: - The message ID - """ -``` - -Example: - -```python -message_id = self.api.reply_message( - ctx.event.message, - "This is a reply" -) -``` - -### delete_message - -Deletes a specific message. - -```python -def delete_message(self, message_id, **kwargs) -> bool: - """ - Delete a specific message. - - Args: - message_id: The ID of the message to delete - **kwargs: Additional arguments - - Returns: - True if successful, False otherwise - """ -``` - -Example: - -```python -success = self.api.delete_message(message_id) -``` - -## User API - -### get_user_info - -Gets information about a user. - -```python -def get_user_info(self, user_id, **kwargs) -> dict: - """ - Get information about a user. - - Args: - user_id: The ID of the user - **kwargs: Additional arguments - - Returns: - A dictionary containing user information - """ -``` - -Example: - -```python -user_info = self.api.get_user_info("12345678") -print(user_info["nickname"]) -``` - -### get_friend_list - -Gets a list of the bot's friends. - -```python -def get_friend_list(self, **kwargs) -> list: - """ - Get a list of the bot's friends. - - Args: - **kwargs: Additional arguments - - Returns: - A list of friend information - """ -``` - -Example: - -```python -friends = self.api.get_friend_list() -for friend in friends: - print(friend["nickname"]) -``` - -## Group API - -### get_group_info - -Gets information about a group. - -```python -def get_group_info(self, group_id, **kwargs) -> dict: - """ - Get information about a group. - - Args: - group_id: The ID of the group - **kwargs: Additional arguments - - Returns: - A dictionary containing group information - """ -``` - -Example: - -```python -group_info = self.api.get_group_info("12345678") -print(group_info["name"]) -``` - -### get_group_member_list - -Gets a list of members in a group. - -```python -def get_group_member_list(self, group_id, **kwargs) -> list: - """ - Get a list of members in a group. - - Args: - group_id: The ID of the group - **kwargs: Additional arguments - - Returns: - A list of member information - """ -``` - -Example: - -```python -members = self.api.get_group_member_list("12345678") -for member in members: - print(member["nickname"]) -``` - -### get_group_list - -Gets a list of groups the bot is in. - -```python -def get_group_list(self, **kwargs) -> list: - """ - Get a list of groups the bot is in. - - Args: - **kwargs: Additional arguments - - Returns: - A list of group information - """ -``` - -Example: - -```python -groups = self.api.get_group_list() -for group in groups: - print(group["name"]) -``` - -## LLM API - -### call_llm - -Calls the LLM with a specific prompt. - -```python -def call_llm(self, prompt, model=None, **kwargs) -> str: - """ - Call the LLM with a specific prompt. - - Args: - prompt: The prompt to send to the LLM - model: The model to use (defaults to the system default) - **kwargs: Additional arguments - - Returns: - The LLM's response - """ -``` - -Example: - -```python -response = self.api.call_llm( - "What is the capital of France?", - model="gpt-3.5-turbo" -) -print(response) -``` - -### call_llm_with_functions - -Calls the LLM with a specific prompt and available functions. - -```python -def call_llm_with_functions(self, prompt, functions, model=None, **kwargs) -> dict: - """ - Call the LLM with a specific prompt and available functions. - - Args: - prompt: The prompt to send to the LLM - functions: A list of function definitions - model: The model to use (defaults to the system default) - **kwargs: Additional arguments - - Returns: - A dictionary containing the LLM's response and any function calls - """ -``` - -Example: - -```python -functions = [ - { - "name": "get_weather", - "description": "Get the weather for a location", - "parameters": { - "type": "object", - "properties": { - "location": { - "type": "string", - "description": "The location to get weather for" - } - }, - "required": ["location"] - } - } -] - -response = self.api.call_llm_with_functions( - "What's the weather in Paris?", - functions, - model="gpt-3.5-turbo" -) - -if "function_call" in response: - function_name = response["function_call"]["name"] - function_args = response["function_call"]["arguments"] - # Handle the function call -``` - -## Storage API - -### storage.set - -Stores a value with a specific key. - -```python -def set(self, key, value) -> None: - """ - Store a value with a specific key. - - Args: - key: The key to store the value under - value: The value to store - """ -``` - -Example: - -```python -self.api.storage.set("my_plugin:counter", 42) -``` - -### storage.get - -Gets a value with a specific key. - -```python -def get(self, key, default=None): - """ - Get a value with a specific key. - - Args: - key: The key to get the value for - default: The default value to return if the key doesn't exist - - Returns: - The stored value or the default value - """ -``` - -Example: - -```python -counter = self.api.storage.get("my_plugin:counter", 0) -``` - -### storage.has - -Checks if a key exists. - -```python -def has(self, key) -> bool: - """ - Check if a key exists. - - Args: - key: The key to check - - Returns: - True if the key exists, False otherwise - """ -``` - -Example: - -```python -if self.api.storage.has("my_plugin:counter"): - # Key exists -``` - -### storage.delete - -Deletes a key. - -```python -def delete(self, key) -> None: - """ - Delete a key. - - Args: - key: The key to delete - """ -``` - -Example: - -```python -self.api.storage.delete("my_plugin:counter") -``` - -## Config API - -### config.get - -Gets a configuration value. - -```python -def get(self, key, default=None): - """ - Get a configuration value. - - Args: - key: The key to get the value for - default: The default value to return if the key doesn't exist - - Returns: - The configuration value or the default value - """ -``` - -Example: - -```python -api_key = self.api.config.get("my_plugin:api_key") -``` - -### config.set - -Sets a configuration value. - -```python -def set(self, key, value) -> None: - """ - Set a configuration value. - - Args: - key: The key to set the value for - value: The value to set - """ -``` - -Example: - -```python -self.api.config.set("my_plugin:api_key", "my-api-key") -``` +:::info Table of Contents +[[toc]] +::: ## Event API -### register_event_handler +The following APIs are only available in event handler functions. -Registers a handler for a specific event. +### Reply to Message ```python -def register_event_handler(self, event_type, handler) -> None: - """ - Register a handler for a specific event. - - Args: - event_type: The type of event to handle - handler: The handler function - """ +await ctx.reply(message_chain: MessageChain) ``` +Reply to the conversation that initiated this event. + +- `message_chain`: [Message Chain object](./messages.md), the program can automatically convert it to the target message platform's message chain + +### Send Active Message + +> Due to poor support for active messages in QQ Official API, if users are using QQ Official API, sending active messages may fail + +```python +await ctx.send_message(target_type: str, target_id: str, message_chain: MessageChain) +``` + +Send an active message to the target. + +- `target_type`: Target type, possible values: `"person"`, `"group"` +- `target_id`: Target ID (QQ number or group number) +- `message_chain`: [Message Chain object](./messages.md), the program can automatically convert it to the target message platform's message chain + +### Prevent Default Event Behavior + +```python +ctx.prevent_default() +``` + +Prevent the default behavior of this event, i.e., stop the subsequent behaviors in the processing flow (such as getting a reply from the interface when receiving a private chat message, getting a reply from the interface when receiving a group message, etc.). + +### Prevent Subsequent Plugin Execution + +```python +ctx.prevent_postorder() +``` + +Prevent the execution of subsequent plugins for this event. To modify the execution order of plugins, please change the priority through the methods described in the "Plugin Management" section of the "Plugin Introduction". + +### Add Return Value + +```python +ctx.add_return(name: str, value: Any) +``` + +Add a return value. Event return values are all **optional**. For return values supported by each event, please check the comments of each event in `pkg.plugin.events`. + +## Request API + +A `Request (Query)` refers to the program context when a user sends a question to LangBot and LangBot processes that question. The APIs in this section are used to interact with these contexts. + +- In event handler functions, the Query object is usually located at `ctx.event.query` + +### Set Request Variable + +```python +ctx.event.query.set_variable(key: str, value: typing.Any) +``` + +Request variables are a `dict` bound to this Query, which contains some program context information. If you are using Dify or Alibaba Cloud Bailian or other LLMOps runners, [these variables will be passed to the corresponding platform's API as variables](/en/deploy/pipelines/readme.html#conversation-variables). +If you need to set variables using plugins, it is recommended to handle them during the `PromptPreProcessing` event. + +- `key`: The name of the variable +- `value`: The value of the variable + +### Get Request Variable + +```python +ctx.event.query.get_variable(key: str) +``` + +Get a request variable. + +- `key`: Variable name +- Return value: `typing.Any` + +### Get All Request Variables + +```python +ctx.event.query.get_variables() +``` + +Get all variable values set for this Query object. + +- Return value: `dict[str, typing.Any]` + + +## LangBot API + +The following APIs are provided directly by LangBot for plugins to call and can be called outside of event handler functions. + +- `host` represents an object of the `pkg.plugin.context.APIHost` class, which is included in each plugin class. + +### Get Enabled Message Platform Adapters List + +```python +host.get_platform_adapters() +``` + +Get a list of enabled message platform adapters. + +- Return value: `list[pkg.platform.adapter.MessageSourceAdapter]` + +### Send Active Message + +```python +await host.send_active_message(adapter: pkg.platform.adapter.MessageSourceAdapter, target_type: str, target_id: str, message_chain: MessageChain) +``` + +Send an active message to the target. + +- `adapter`: Message platform adapter, obtained through `host.get_platform_adapters()` and selecting one of them +- `target_type`: Target type, possible values: `"person"`, `"group"` +- `target_id`: Target ID (QQ number or group number) +- `message_chain`: [Message Chain object](./messages.md), the program can automatically convert it to the target message platform's message chain + +:::info +- Some message platforms may not support active messages or have strict limitations +- Some message platform adapters (such as `aiocqhttp`) act as servers waiting for message platforms to connect and push messages. Before a successful connection, sending active messages will fail +::: + Example: ```python -def my_handler(ctx): - # Handle the event - pass +import asyncio +import pkg.platform.types as platform_types -self.api.register_event_handler(PersonNormalMessageReceived, my_handler) -``` +... -### unregister_event_handler - -Unregisters a handler for a specific event. - -```python -def unregister_event_handler(self, event_type, handler) -> None: - """ - Unregister a handler for a specific event. - - Args: - event_type: The type of event - handler: The handler function - """ -``` - -Example: - -```python -self.api.unregister_event_handler(PersonNormalMessageReceived, my_handler) -``` - -## Command API - -### register_command - -Registers a command. - -```python -def register_command(self, name, handler, description=None) -> None: - """ - Register a command. - - Args: - name: The name of the command - handler: The handler function - description: The description of the command - """ -``` - -Example: - -```python -def echo_handler(args): - return args - -self.api.register_command("echo", echo_handler, "Echo a message") -``` - -### unregister_command - -Unregisters a command. - -```python -def unregister_command(self, name) -> None: - """ - Unregister a command. - - Args: - name: The name of the command - """ -``` - -Example: - -```python -self.api.unregister_command("echo") -``` - -## Utility API - -### logger - -Provides access to the logger. - -```python -self.api.logger.debug("Debug message") -self.api.logger.info("Info message") -self.api.logger.warning("Warning message") -self.api.logger.error("Error message") -``` - -### schedule - -Schedules a function to be called at a specific time. - -```python -def schedule(self, func, when, *args, **kwargs) -> str: - """ - Schedule a function to be called at a specific time. - - Args: - func: The function to call - when: When to call the function (datetime or timedelta) - *args: Arguments to pass to the function - **kwargs: Keyword arguments to pass to the function + # initialize function of a plugin + # This code will attempt to send a message "hello, world!" to QQ number 1010553892 10 seconds after LangBot starts + async def initialize(self): + print(self.host.get_platform_adapters()) - Returns: - The schedule ID - """ -``` - -Example: - -```python -from datetime import timedelta - -def my_function(): - # Do something - pass - -schedule_id = self.api.schedule(my_function, timedelta(hours=1)) -``` - -### cancel_schedule - -Cancels a scheduled function. - -```python -def cancel_schedule(self, schedule_id) -> bool: - """ - Cancel a scheduled function. - - Args: - schedule_id: The ID of the schedule - - Returns: - True if successful, False otherwise - """ -``` - -Example: - -```python -success = self.api.cancel_schedule(schedule_id) + async def send_message(): + print("send message start waiting") + await asyncio.sleep(10) + + try: + await self.host.send_active_message( + adapter=self.host.get_platform_adapters()[0], + target_type="person", + target_id="1010553892", + message=platform_types.MessageChain([ + platform_types.Plain("hello, world!") + ]) + ) + except Exception as e: + traceback.print_exc() + print("send message end") + + asyncio.get_running_loop().create_task(send_message()) ``` From 9ca8e6092fe5076fda784cbabc8e9ffd65cd187d Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 15:44:50 +0000 Subject: [PATCH 11/17] Update plugin/dev/messages.md to match Chinese structure Co-Authored-By: Junyan Qin --- docs/en/plugin/dev/messages.md | 403 ++++++++------------------------- 1 file changed, 89 insertions(+), 314 deletions(-) diff --git a/docs/en/plugin/dev/messages.md b/docs/en/plugin/dev/messages.md index 340a1a5be..09553f7e8 100644 --- a/docs/en/plugin/dev/messages.md +++ b/docs/en/plugin/dev/messages.md @@ -1,338 +1,113 @@ # Message Platform Entities -This document describes the message platform entities used in LangBot, which are important for plugin development. +:::info Table of Contents +[[toc]] +::: -## Message +LangBot supports multiple messaging platforms, but the `message entity` format of each platform is different. To shield these differences, LangBot has a unified standard. Plugin developers only need to master and use the message entities described on this page in their plugins, and LangBot's internal message processing logic can automatically complete the parsing and conversion of messages. -The `Message` class represents a message in LangBot. +:::info +LangBot's message platform entities are modified based on the implementation of [YiriMirai](https://github.com/YiriMiraiProject/YiriMirai). +::: + +## Message Chain + +The `message` of a message platform is different from the message used for AI interaction. The message of a message platform is described in the form of a `message chain`. Each independent message displayed on QQ is a message chain, which can contain multiple `message chain components` such as `text`, `image`, `@ component`, for example: + +![](/assets/image/zh/plugin/dev/plugin_dev_messages_01.png) + +This is a message chain, containing one Plain component (Hello World) and one Image component (Usagi) + +The definitions of message chain and message chain components are located in `pkg/platform/types/messages.py`. + +### Constructing Message Chain + +Please import the `pkg.platform.types` package first to use the message components. ```python -class Message: - """ - Represents a message in LangBot. - """ - - def __init__(self, source, content, message_id=None, sender=None, time=None, **kwargs): - """ - Initialize a message. - - Args: - source: The source of the message - content: The content of the message - message_id: The ID of the message - sender: The sender of the message - time: The time the message was sent - **kwargs: Additional arguments - """ - self.source = source - self.content = content - self.message_id = message_id - self.sender = sender - self.time = time - self.kwargs = kwargs +from pkg.platform.types import * + +# Build a message containing the text Hello LangBot and an image (from URL) +msg_chain = MessageChain([ + Plain("Hello LangBot"), + Image(url='https://qchatgpt.rockchin.top/langbot-logo.png') +]) + +# Build a message containing @all and the text Hello LangBot +msg_chain = MessageChain([ + AtAll(), + Plain("Hello LangBot") +]) + +# Build a message containing @specific member and the text Hello LangBot +msg_chain = MessageChain([ + At(123456), + Plain("Hello LangBot") +]) ``` -### Properties +Currently supported message chain components: -- `source`: The source of the message (where it was sent from) -- `content`: The content of the message -- `message_id`: The ID of the message -- `sender`: The sender of the message -- `time`: The time the message was sent -- `kwargs`: Additional arguments +- `Source` Source message chain information, if it is a message received from a message platform, this component will be included at the beginning of the message chain, recording message information +- `Plain` Plain text message +- `Quote` Quote message +- `Image` Image message +- `AtAll` @all members message +- `At` @specific member message +- `Voice` Voice message + - Need to check message platform support +- `Forward` Forward message + - Not supported on many platforms, not recommended to use +- `File` File message -## Source +For specific usage, please check the definitions in the source code `pkg/platform/types/messages.py`. -The `Source` class represents the source of a message. +## Message Platform Events + +These events are different from LangBot plugin events, they are an independent set of event systems for the message platform system. These events are the source events of LangBot messages, which can be obtained from each Query.message_event. ```python -class Source: - """ - Represents the source of a message. - """ - - def __init__(self, platform, identity, **kwargs): - """ - Initialize a source. - - Args: - platform: The platform of the source - identity: The identity of the source - **kwargs: Additional arguments - """ - self.platform = platform - self.identity = identity - self.kwargs = kwargs + # For example in a plugin + @handler(GroupMessageReceived) + async def _(self, ctx: EventContext): + # Get the message platform event + message_event = ctx.query.message_event ``` -### Properties +Message platform events are defined in `pkg/platform/types/events.py`. -- `platform`: The platform of the source (e.g., "qq", "wechat", "discord") -- `identity`: The identity of the source (e.g., user ID, group ID) -- `kwargs`: Additional arguments +## Other Entities -### Types of Sources +In addition to the above entities, there are also entities such as `Friend` and `Group`, which are defined in `pkg/platform/types/entities.py`. +They are generally included in the above event objects, from which you can get some information not included in the plugin events. -#### Person Source +## Accessing Message Platform Underlying API -Represents a direct message from a person. +In order to smooth out the differences between various platforms, LangBot provides an abstraction layer on top of the message platform APIs, namely the content described above and the platform adapters in the `pkg/platform/sources` directory. +However, due to the large differences between platforms, the entities and API encapsulation in the abstraction layer are not complete. If your plugin needs to access specific platform functions, you can access the underlying API according to the following instructions. + +:::info +The `adapter` here is the instantiated object of each adapter, corresponding to the `xxxAdapter(adapter.MessagePlatformAdapter)` class in each file under `pkg/platform/sources/`. It can be obtained from the plugin event's `query.adapter`, for example: ```python -class PersonSource(Source): - """ - Represents a direct message from a person. - """ - - def __init__(self, platform, identity, **kwargs): - """ - Initialize a person source. - - Args: - platform: The platform of the source - identity: The identity of the person - **kwargs: Additional arguments - """ - super().__init__(platform, identity, **kwargs) + # For example in a plugin + @handler(GroupMessageReceived) + async def _(self, ctx: EventContext): + # Get the adapter object + adapter = ctx.query.adapter ``` +::: -#### Group Source -Represents a message from a group. - -```python -class GroupSource(Source): - """ - Represents a message from a group. - """ - - def __init__(self, platform, identity, **kwargs): - """ - Initialize a group source. - - Args: - platform: The platform of the source - identity: The identity of the group - **kwargs: Additional arguments - """ - super().__init__(platform, identity, **kwargs) -``` - -## Sender - -The `Sender` class represents the sender of a message. - -```python -class Sender: - """ - Represents the sender of a message. - """ - - def __init__(self, identity, name=None, **kwargs): - """ - Initialize a sender. - - Args: - identity: The identity of the sender - name: The name of the sender - **kwargs: Additional arguments - """ - self.identity = identity - self.name = name - self.kwargs = kwargs -``` - -### Properties - -- `identity`: The identity of the sender (e.g., user ID) -- `name`: The name of the sender -- `kwargs`: Additional arguments - -## Events - -LangBot uses events to represent various actions in the system. Here are some common events related to messages: - -### PersonNormalMessageReceived - -Triggered when a normal message is received from a person. - -```python -class PersonNormalMessageReceived(Event): - """ - Triggered when a normal message is received from a person. - """ - - def __init__(self, message): - """ - Initialize the event. - - Args: - message: The received message - """ - super().__init__() - self.message = message -``` - -### GroupNormalMessageReceived - -Triggered when a normal message is received in a group. - -```python -class GroupNormalMessageReceived(Event): - """ - Triggered when a normal message is received in a group. - """ - - def __init__(self, message): - """ - Initialize the event. - - Args: - message: The received message - """ - super().__init__() - self.message = message -``` - -### MessageSent - -Triggered when a message is sent. - -```python -class MessageSent(Event): - """ - Triggered when a message is sent. - """ - - def __init__(self, message): - """ - Initialize the event. - - Args: - message: The sent message - """ - super().__init__() - self.message = message -``` - -## Working with Messages in Plugins - -### Handling Messages - -```python -from langbot.plugin import Plugin, handler -from langbot.event import PersonNormalMessageReceived, GroupNormalMessageReceived -from langbot.api import APIHost - -class MessageHandlerPlugin(Plugin): - def __init__(self, api_host: APIHost): - super().__init__(api_host) - self.api = api_host - - @handler(PersonNormalMessageReceived) - def on_person_message(self, ctx): - # Get the message - message = ctx.event.message - - # Get the content - content = message.content - - # Get the sender - sender = message.sender - - # Get the source - source = message.source - - # Do something with the message - if content == "!hello": - self.api.send_message(source, f"Hello, {sender.name}!") - - @handler(GroupNormalMessageReceived) - def on_group_message(self, ctx): - # Get the message - message = ctx.event.message - - # Get the content - content = message.content - - # Get the sender - sender = message.sender - - # Get the source - source = message.source - - # Do something with the message - if content == "!hello": - self.api.send_message(source, f"Hello, {sender.name}!") -``` - -### Sending Messages - -```python -# Send a message to a person -person_source = PersonSource("qq", "12345678") -self.api.send_message(person_source, "Hello!") - -# Send a message to a group -group_source = GroupSource("qq", "87654321") -self.api.send_message(group_source, "Hello, everyone!") - -# Reply to a message -self.api.reply_message(message, "This is a reply") -``` - -### Creating Messages - -```python -from langbot.message import Message, PersonSource, Sender - -# Create a person source -source = PersonSource("qq", "12345678") - -# Create a sender -sender = Sender("87654321", "John Doe") - -# Create a message -message = Message( - source=source, - content="Hello, World!", - message_id="msg123", - sender=sender, - time=1234567890 -) -``` - -## Platform-Specific Considerations - -Different messaging platforms may have different capabilities and limitations. Here are some platform-specific considerations: - -### QQ - -- Supports text, images, and rich media -- Group messages can mention specific users -- Message IDs are platform-specific - -### WeChat - -- Supports text, images, and rich media -- Limited API for personal accounts -- More features available for official accounts - -### Discord - -- Supports text, images, embeds, and rich formatting -- Has a robust API with many features -- Supports slash commands - -### Telegram - -- Supports text, images, and rich formatting -- Has a comprehensive API -- Supports inline bots and commands - -## Best Practices - -- Always check the platform before using platform-specific features -- Handle message content carefully, as it may contain special formatting -- Use the appropriate source type (PersonSource or GroupSource) when sending messages -- Consider rate limits and message size limits when sending messages -- Handle errors gracefully, as message sending may fail for various reasons +| Platform | Adapter | Access Method | Description | +| --- | --- | --- | --- | +| OneBot v11| aiocqhttp | adapter.bot | The bot object corresponds to the CQHttp object of [aiocqhttp](https://github.com/nonebot/aiocqhttp), please refer to the aiocqhttp documentation for detailed usage | +| QQ Official API | qqofficial | adapter.bot | The bot object is the SDK object under libs/qq_official_api, for detailed usage, you can refer to the way of initiating HTTP requests in the `send_group_text_msg` method in `QQOfficialClient` in libs/qq_official_api/api.py, please check the QQ official API documentation for specific interface documentation| +| Personal WeChat | gewechat | adapter.bot | The bot object is the GewechatClient object of [hanfangyuan4396/gewechat-client](https://github.com/hanfangyuan4396/gewechat-python), please refer to the documentation for detailed usage | +| WeCom | wecom | adapter.bot | Refer to the way of initiating HTTP requests in the `send_image` method in `WecomClient` in libs/wecom_api/api.py, please check the WeCom API documentation for specific interface documentation | +| WeChat Official Account | officialaccount | - | Please refer to the interface documentation information in the WeChat Official Account API documentation, the relevant credential information can be obtained from adapter.bot | +| Feishu | lark | adapter.api_client | Feishu SDK's API Client object, please refer to [oapi-sdk-python](https://github.com/larksuite/oapi-sdk-python) | +| DingTalk | dingtalk | - | Please refer to the DingTalk API documentation, the relevant credential information can be obtained from adapter.bot | +| Discord | discord | adapter.bot | The bot object is the Discord object of [Rapptz/discord.py](https://github.com/Rapptz/discord.py), please refer to the documentation for detailed usage | +| Telegram | telegram | adapter.bot | The bot object is the Telegram object of [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot), please refer to the documentation for detailed usage | +| Slack | slack | adapter.bot | The bot object is the object in libs/slack_api/api.py, please refer to its code and Slack SDK for detailed usage | From 0ee098146ec720a098d9c9080d249b9fc697eee2 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 15:51:38 +0000 Subject: [PATCH 12/17] Add English platform-specific documentation files to resolve dead links Co-Authored-By: Junyan Qin --- docs/en/deploy/platforms/dingtalk.md | 45 ++++++++ docs/en/deploy/platforms/discord.md | 55 +++++++++ docs/en/deploy/platforms/lark.md | 76 +++++++++++++ .../deploy/platforms/qq/aiocqhttp/lagrange.md | 67 +++++++++++ .../deploy/platforms/qq/aiocqhttp/llonebot.md | 27 +++++ .../deploy/platforms/qq/aiocqhttp/napcat.md | 33 ++++++ .../deploy/platforms/qq/official_webhook.md | 107 ++++++++++++++++++ docs/en/deploy/platforms/slack.md | 53 +++++++++ docs/en/deploy/platforms/telegram.md | 29 +++++ docs/en/deploy/platforms/wechat/gewechat.md | 97 ++++++++++++++++ docs/en/deploy/platforms/wecom/wecom.md | 64 +++++++++++ docs/en/deploy/platforms/wecom/wecomcs.md | 25 ++++ docs/en/deploy/platforms/wxoa.md | 55 +++++++++ docs/en/deploy/settings.md | 97 ++++++++++++++++ 14 files changed, 830 insertions(+) create mode 100644 docs/en/deploy/platforms/dingtalk.md create mode 100644 docs/en/deploy/platforms/discord.md create mode 100644 docs/en/deploy/platforms/lark.md create mode 100644 docs/en/deploy/platforms/qq/aiocqhttp/lagrange.md create mode 100644 docs/en/deploy/platforms/qq/aiocqhttp/llonebot.md create mode 100644 docs/en/deploy/platforms/qq/aiocqhttp/napcat.md create mode 100644 docs/en/deploy/platforms/qq/official_webhook.md create mode 100644 docs/en/deploy/platforms/slack.md create mode 100644 docs/en/deploy/platforms/telegram.md create mode 100644 docs/en/deploy/platforms/wechat/gewechat.md create mode 100644 docs/en/deploy/platforms/wecom/wecom.md create mode 100644 docs/en/deploy/platforms/wecom/wecomcs.md create mode 100644 docs/en/deploy/platforms/wxoa.md create mode 100644 docs/en/deploy/settings.md diff --git a/docs/en/deploy/platforms/dingtalk.md b/docs/en/deploy/platforms/dingtalk.md new file mode 100644 index 000000000..c8793dd29 --- /dev/null +++ b/docs/en/deploy/platforms/dingtalk.md @@ -0,0 +1,45 @@ +# Deploy DingTalk Bot + +Deploy LangBot to DingTalk. + +## Create Bot + +Enter the [DingTalk Developer Backend](https://open-dev.dingtalk.com/?spm=ding_open_doc.document.0.0.74f445e5MkawbT#/), log in and enter the organization. After successful login, enter the open platform, which looks like this: +![DingTalk Open Platform](/assets/image/zh/deploy/bots/dingtalk/dingtalk1.png) + +Click `Application Development` at the top, then click the blue button `Create Application` on the right, fill in the basic information of the bot, and click save. + +Enter the bot's backend, for example, if we have a bot named langbot2, its management page looks like this: +![Bot Page](/assets/image/zh/deploy/bots/dingtalk/dingtalk2.png) + +## Configure Bot + +Click `Add Application Capability`, `Other Application Capabilities`, `Configure` in `Robot`, click configure and fill in the information, as shown below: +![Bot Configuration](/assets/image/zh/deploy/bots/dingtalk/dingtalk3.png) + +Click `Publish` at the bottom of the page, after successful publication, click `Version Management and Publishing` at the bottom left of the bot page, as shown: +![Version Management and Publishing](/assets/image/zh/deploy/bots/dingtalk/dingtalk4.png) + +If this is the first time creating a bot, the right side is empty, you need to click `Create New Version`, set information in it, then set `Application Visibility Range`, and click save. + +Click `Event Subscription`, change the push method to `Stream Mode Push`. + +## Fill in Configuration Information + +Click `Credentials and Basic Information`, record `Client ID` and `Client Secret`, +Click `Application Capabilities`, `Robot`, record the RobotCode and Robot Name. +`markdown_card` is whether to enable Markdown form replies. +Record the above configuration items and fill them in the LangBot bot configuration form + +![Connect to LangBot](/assets/image/zh/deploy/bots/dingtalk/connect_to_langbot.png) + +**Start LangBot**. + +## Add Bot + +This article uses DingTalk Windows desktop version as an example, click `Search` at the top, `Features`, then enter the name of the bot you just created, as shown: +![robot_png](/assets/image/zh/deploy/bots/dingtalk/dingtalk5.png) + +Click on the bot to chat with it. + +If you want to add it to a group, click `Group Management`, `Robots`, `Add Robot` in the DingTalk group, then search for the bot name to use it in the group. diff --git a/docs/en/deploy/platforms/discord.md b/docs/en/deploy/platforms/discord.md new file mode 100644 index 000000000..4f63f3867 --- /dev/null +++ b/docs/en/deploy/platforms/discord.md @@ -0,0 +1,55 @@ +# Connect to Discord Bot + +## Create Bot + +Go to the [Discord Developer Portal](https://discord.com/developers/applications), log in, and create an application. + +![alt text](/assets/image/zh/deploy/bots/discord/discord_01.png) + +Modify the basic information, click `Save Changes` to save. + +![alt text](/assets/image/zh/deploy/bots/discord/discord_02.png) + +Click `Bot` on the left, click `Reset Token`, copy the generated Token. + +![alt text](/assets/image/zh/deploy/bots/discord/discord_03.png) + +And check all options in `Privileged Gateway Intents` on this page, click `Save Changes` to save. + +![alt text](/assets/image/zh/deploy/bots/discord/discord_05.png) + +**Finally, click `OAuth2` on the left, copy the `Client ID` on the page.** + +## Connect to LangBot + +Click to add a new bot, fill in the `Client ID` and `Token` obtained in the previous step into the configuration. + +![alt text](/assets/image/zh/deploy/bots/discord/connect_to_langbot.png) + +:::warning + +If you are using this adapter within mainland China, you may need to set up a network proxy. + +::: + +After filling in, start LangBot. If configured successfully, the log will show `[02-02 16:48:15.263] manager.py (68) - [INFO] : Initializing platform adapter 1: discord`, keep LangBot running. + +## Invite Bot to Server + +You need to create a server first, then invite the bot to the server. + +Go to the `OAuth2` page, select `URL Generator`, select `bot`, check the permissions as shown in the image. + +![alt text](/assets/image/zh/deploy/bots/discord/discord_06.png) + +![alt text](/assets/image/zh/deploy/bots/discord/discord_07.png) + +Click `Copy`, paste the generated URL into your browser to open it, and invite the bot to your server. + +![alt text](/assets/image/zh/deploy/bots/discord/discord_08.png) + +## Use Bot + +Now you can use the bot normally in groups or private messages. + +![alt text](/assets/image/zh/deploy/bots/discord/discord_09.png) diff --git a/docs/en/deploy/platforms/lark.md b/docs/en/deploy/platforms/lark.md new file mode 100644 index 000000000..565b2e9cc --- /dev/null +++ b/docs/en/deploy/platforms/lark.md @@ -0,0 +1,76 @@ +# Connect to Feishu Bot + +## Create Bot + +Go to the [Feishu Open Platform](https://open.feishu.cn/app), log in, and create an enterprise self-built application. + +![alt text](/assets/image/zh/deploy/bots/lark/lark_01.png) + +![alt text](/assets/image/zh/deploy/bots/lark/lark_02.png) + +Add bot capability to the application: +![alt text](/assets/image/zh/deploy/bots/lark/lark_03.png) + +Add the permissions shown in the image in the permission management: + +![alt text](/assets/image/zh/deploy/bots/lark/lark_04.png) + +## Connect to LangBot + +Find `app_id` and `app_secret` on the `Credentials and Basic Information` page. + +![alt text](/assets/image/zh/deploy/bots/lark/lark_05.png) + + + +Open LangBot's Webui configuration page, create a new bot + +Fill in the relevant information in the form + +![alt text](/assets/image/zh/deploy/bots/lark/connect_to_langbot.png) + +After filling in, start LangBot. If configured successfully, the log will show `[01-29 23:42:56.796] manager.py (68) - [INFO] : Initializing platform adapter 1: lark`, keep LangBot running. + +:::warning +By default, WebSocket long connection mode is used, corresponding to the long connection subscription below. But in some cases (such as international version of Feishu), long connection mode is not available, in which case you need to use Webhook mode, corresponding to the `Send events to developer server` mode, please refer to the following configuration: + +- `enable-webhook`: Set to `true` +- `encrypt-key`: Set to `Encrypt Key` in the `Encryption Strategy` of the `Events and Callbacks` page + +Note that in Webhook mode, LangBot needs to be deployed on a server with a public IP, and ensure that the firewall has opened the port configured above. +::: + +## Configure Event Subscription + +Go to the `Events and Callbacks` page, configure the subscription method as `Long Connection`: + +![alt text](/assets/image/zh/deploy/bots/lark/lark_07.png) + +And add the event: `Receive Messages` + +![alt text](/assets/image/zh/deploy/bots/lark/lark_08.png) + +:::warning +Webhook mode configuration method: + +![alt text](/assets/image/zh/deploy/bots/lark/lark_13.png) + +Please start LangBot first, fill in your server address and port here, the path is `/lark/callback`, click `Save`. + +::: + +## Publish Bot + +Click `Create Version` at the top, fill in the version number and other information, click `Save` below. + +![alt text](/assets/image/zh/deploy/bots/lark/lark_09.png) + +Add the bot to a Feishu group to use it: + +![alt text](/assets/image/zh/deploy/bots/lark/lark_10.png) + +![alt text](/assets/image/zh/deploy/bots/lark/lark_11.png) + +Private chat can also be used directly + +![alt text](/assets/image/zh/deploy/bots/lark/lark_12.png) diff --git a/docs/en/deploy/platforms/qq/aiocqhttp/lagrange.md b/docs/en/deploy/platforms/qq/aiocqhttp/lagrange.md new file mode 100644 index 000000000..1b800b11a --- /dev/null +++ b/docs/en/deploy/platforms/qq/aiocqhttp/lagrange.md @@ -0,0 +1,67 @@ +# Deploy Message Platform Lagrange + +## Lagrange Introduction + +Lagrange is a QQNT protocol reverse engineering framework that is lightweight and relatively stable. Connect through the OneBot 11 protocol, you need to select `OneBot v11` as the adapter. + +::: warning +You need to use NT QQ to interact with the bot, old versions of QQ will not work properly +::: + +## Deployment Steps + +The following only provides general steps for Windows, for detailed steps or other platforms, please refer to the [Lagrange official documentation](https://lagrangedev.github.io/Lagrange.Doc/Lagrange.OneBot/) + +### Environment Configuration + +Install .NET 8 runtime, [Click here to go directly to Microsoft's official website to download](https://dotnet.microsoft.com/zh-cn/download/dotnet/thank-you/runtime-desktop-8.0.4-windows-x64-installer) + +### Download the latest build from Github Actions + +You need to log in to Github to download. If you don't need certain features, you can download from Release, see image 3 + +[Github Actions](https://github.com/KonataDev/Lagrange.Core/actions) + +Follow the images to download: + +![Image 1](/assets/image/zh/deploy/bots/qq/onebot/lagrange/dl_lgr_1.png) + +![Image 2](/assets/image/zh/deploy/bots/qq/onebot/lagrange/dl_lgr_2.png) + +![Image 3](/assets/image/zh/deploy/bots/qq/onebot/lagrange/dl_lgr_3.png) + +### Final Steps + +Extract the zip you downloaded, navigate to the directory containing Lagrange.OneBot.exe, open a cmd command prompt in this directory, and enter + +```bash +.\Lagrange.OneBot +``` + +Run it once, if a QR code is output, you can directly scan it with the bot account to log in (if the QR code is not clear, you can look for the image file in the lagrange data directory). + +If you cannot log in, please check if you have correctly filled in the signature address SignServerUrl in the lagrange configuration file appsettings.json. Please look for the signature address in the lagrange documentation. + +## Modify Configuration + +You need to configure Lagrange to connect to LangBot. Please edit Lagrange's configuration file appsettings.json and ensure that the connection configuration in Implementations matches the content in the image below: + +![Configure Connection](/assets/image/zh/deploy/bots/qq/onebot/lagrange/config_lgr.png) + +The Type must be `ReverseWebSocket`; +Host is the IP of the host running LangBot, if on the same host, you can write `127.0.0.1`; +Suffix must be `/ws`; + +## Lagrange Integration + +Next, open the LangBot configuration page + +Click on Bots, then click Add + +Select `OneBot v11` for `Platform/Adapter Selection` + +![napcat_webui_03](/assets/image/zh/deploy/bots/qq/onebot/napcat/napcat_webui_03.png) + +::: info +Note, if you have network connection issues involving multiple docker containers, please refer to [Network Configuration Details](/en/workshop/network-details) +::: diff --git a/docs/en/deploy/platforms/qq/aiocqhttp/llonebot.md b/docs/en/deploy/platforms/qq/aiocqhttp/llonebot.md new file mode 100644 index 000000000..aadd9fff9 --- /dev/null +++ b/docs/en/deploy/platforms/qq/aiocqhttp/llonebot.md @@ -0,0 +1,27 @@ +# Deploy Message Platform LiteLoaderQQNT + +Connect through the OneBot 11 protocol, you need to select ``OneBot v11`` as the adapter. + +# LiteLoaderQQNT + +- [LiteLoaderQQNT's OneBot API plugin](https://github.com/LLOneBot/LLOneBot) injects into the QQ client and converts it to the OneBot 11 protocol + +Installation tutorial for LiteLoaderQQNT: https://llonebot.github.io/zh-CN/guide/getting-started + +![Image 1](/assets/image/zh/deploy/bots/qq/onebot/llonebot/llob_cfg.png) + +Make sure the IP address in the address is the address of the host running LangBot (if running on the same network, use 127.0.0.1) + +## Lagrange Integration + +Next, open the LangBot configuration page + +Click on Bots, then click Add + +Select `OneBot v11` for `Platform/Adapter Selection` + +![napcat_webui_03](/assets/image/zh/deploy/bots/qq/onebot/napcat/napcat_webui_03.png) + +::: info +Note, if you have network connection issues involving multiple docker containers, please refer to [Network Configuration Details](/en/workshop/network-details) +::: diff --git a/docs/en/deploy/platforms/qq/aiocqhttp/napcat.md b/docs/en/deploy/platforms/qq/aiocqhttp/napcat.md new file mode 100644 index 000000000..74669eb95 --- /dev/null +++ b/docs/en/deploy/platforms/qq/aiocqhttp/napcat.md @@ -0,0 +1,33 @@ +# Deploy Message Platform NapCat + +Connect through the OneBot 11 protocol, you need to select ``OneBot v11`` as the adapter. + +## NapCat Installation Tutorial + +Follow the [NapCat official documentation for installation](https://napcat.napneko.icu/guide/start-install) + +## NapCat Configuration Tutorial + +Only using webui operation as an example, for other configuration methods, please check the NapCat documentation. + +After logging in, you can check the logs in the console or Bash, find `[NapCat] [WebUi] WebUi Publish Panel Url: http://x.x.x.x:6099/webui?token=xxxxx ` +![napcat_webui_01](/assets/image/zh/deploy/bots/qq/onebot/napcat/napcat_webui_01.png) + +Copy the Url to your browser or on Windows 11, press Ctrl and click the Url. After scanning the QR code to log in, you will enter the network configuration page. + +Click Add Network Configuration, select WebSocket Client, click Enable, then fill in `ws://127.0.0.1:2280/ws` in the Url field and save. +![napcat_webui_02](/assets/image/zh/deploy/bots/qq/onebot/napcat/napcat_webui_02.png) + +## NapCat Integration + +Next, open the LangBot configuration page + +Click on Bots, then click Add + +Select `OneBot v11` for `Platform/Adapter Selection` + +![napcat_webui_03](/assets/image/zh/deploy/bots/qq/onebot/napcat/napcat_webui_03.png) + +::: info +Note, if you have network connection issues involving multiple docker containers, please refer to [Network Configuration Details](/en/workshop/network-details) +::: diff --git a/docs/en/deploy/platforms/qq/official_webhook.md b/docs/en/deploy/platforms/qq/official_webhook.md new file mode 100644 index 000000000..69468bb5e --- /dev/null +++ b/docs/en/deploy/platforms/qq/official_webhook.md @@ -0,0 +1,107 @@ +# Using QQ Official Bot API (Using webhook method) + +This article only provides the deployment of test bots. For the official launch of bots, please follow the [release process](https://q.qq.com/qqbot/#/home). + +## Register QQ Bot (Test Bot) + +### Configure Basic Bot Information + +Go to the [QQ Open Platform](https://q.qq.com/#/), find `Application Management` at the bottom, click Create Bot, fill in the basic information, and then enter the bot page, as shown: + +![Bot Main Page](/assets/image/zh/deploy/bots/qq/official/qqofficial1.png) + +The `Release Process` in the `Home` option is the step to launch the bot. Since you are deploying a test version bot, you only need to complete the `Information` and `Sandbox Configuration` sections.
+ +First fill in the information, then click on Sandbox Configuration. + +#### Configure Sandbox Settings + +Click on `Sandbox Configuration`. +If you want to deploy in a QQ group, follow the requirements under `QQ Group ID` to select a group chat, and add users with private chat permissions in the message list configuration.
+If you want to deploy in a QQ channel, follow the requirements under `Channel ID` to select a channel ID, with bot type 0. + +#### Configure Development Management + +Click on `Development Management`. + +![Development Management Page](/assets/image/zh/deploy/bots/qq/official/qqofficial2.png) + +Record the `AppID`, `Token`, and `AppSecret`, and add the IP address of the server where LangBot is located to the IP whitelist. + +## Connect to LangBot + +Next, open the LangBot configuration page + +Click on Bots, then click Add + +Select `OneBot v11` for `Platform/Adapter Selection` + +![QQ Official Bot Integration with LangBot](/assets/image/zh/deploy/bots/qq/official/qqofficial3.png) + + +## Configure Callback Address + +Since QQ official bots require the callback address to be an https request, but LangBot only provides http, you need to configure a reverse proxy yourself. + +This article recommends using [Caddy](https://caddy2.dengxiaolong.com/docs/) for reverse proxy, with the process as follows. + +### Caddy Operation Process + +#### Install Caddy + +Go to the [Caddy Installation Documentation](https://caddy2.dengxiaolong.com/docs/install). Choose the installation steps for your operating system and install. + +#### Fill in Caddyfile + +This article assumes that LangBot is deployed on an Ubuntu system, where the default location of Caddyfile is `/etc/caddy/Caddyfile`. +Use vim or nano to edit Caddyfile, and fill in the Caddyfile as: +```json +your_domain_name { + reverse_proxy 127.0.0.1:2284 +} +``` +For example, if you have a domain testlb.com that resolves to your machine, and you have port **443** open, fill in: +```json +testlb.com { + reverse_proxy 127.0.0.1:2284 +} +``` + +Save and exit the file. + +**Note, the Caddyfile format is very strict, please fill it in correctly. If you encounter problems, please refer to the Caddy documentation or ask AI.** + +#### Start Caddy +Enter the command +```bash +sudo systemctl start caddy +``` + +After successful startup, check the Caddy status with: +```bash +sudo systemctl status caddy +``` + + +#### Subsequent Configuration + +If Caddy fails to start, please read the [Caddy documentation](https://caddy2.dengxiaolong.com/docs/quick-starts/caddyfile) or join the LangBot community group for help.
+ +Here are some possible issues: + +- Port 443 is not open +- The domain is not resolved to your machine +- Caddyfile was not saved successfully + +### Set Management Portal Callback Address + + +First **start LangBot**. + +Click on `Callback Configuration` in the bot management portal, check all events under `Add Event`. In the request address, enter the domain set in Caddyfile, with the suffix `/callback/command`. + +For example, if the domain is `testlb.com`, then enter `testlb.com/callback/command` in the request address, and click to confirm the configuration. If the callback address is saved successfully, it means the deployment is successful. If you see `Verification Failed`, please carefully check if the above configuration items are filled in correctly. + +## Publication Method + +Enter the release process in `Home` and follow the process to publish and launch the bot. diff --git a/docs/en/deploy/platforms/slack.md b/docs/en/deploy/platforms/slack.md new file mode 100644 index 000000000..6a540041a --- /dev/null +++ b/docs/en/deploy/platforms/slack.md @@ -0,0 +1,53 @@ +# Connect to Slack Bot + +## Create Bot + +Go to the [Slack App Platform](https://api.slack.com/apps). + +![Slack Platform](/assets/image/zh/deploy/bots/slack/slack_01.jpg) + +Click `Create New App` in the top right corner, select `from a manifest`, select the workspace where you want to deploy the bot, select next, and choose to create the bot. + +Enter the `Basic Information` option on the left, record the `signing_secret`. + +Enter `OAuth & Permissions` on the left, scroll down to `Scopes`, add the following bot permissions: + +![Bot Permissions 1](/assets/image/zh/deploy/bots/slack/slack_02.jpg) + + +![Bot Permissions 2](/assets/image/zh/deploy/bots/slack/slack_03.jpg) + +After adding the above permissions, click `Install to your workspace`, record the token starting with `xoxb-xxxxx`.
+ +**Note:** +**If you didn't add all permissions at once, you'll need to reinstall the App later.**
+ +Enter the `App Home` tab on the left, scroll to the bottom where you'll find `Message Tab`, turn it on and check +`Allow users to send Slash commands and messages from the messages tab`. + +## Connect to LangBot + +Enter `Event Subscriptions` on the left, turn on the switch, click `Subscribe to bot events`, +add the following permissions: +![Event Permissions](/assets/image/zh/deploy/bots/slack/slack_04.jpg) + +After completing all the above operations, open the LangBot Webui configuration page, add a new bot + +![Event Permissions](/assets/image/zh/deploy/bots/slack/connect_to_langbot.png) + +## Start Bot + +After filling in the Slack configuration items, **start LangBot**. +Then in the `Request URL` of the Event Subscriptions you just configured, fill in the deployment address of your LangBot. + +::: info +Since Slack officially requires https, you can refer to the [Caddy configuration part in the QQ official bot deployment process](/en/deploy/platforms/qq/official_webhook.md#caddy-operation-process), note that the port uses the port configured in the Slack adapter. + +::: + +After filling in all the above configuration items, click save link. If it shows a green **Verified**, it means the deployment is successful. +Otherwise, check if the above configuration items are filled in correctly. + +## Effect Demonstration + +![Effect Demonstration](/assets/image/zh/deploy/bots/slack/slack_05.jpg) diff --git a/docs/en/deploy/platforms/telegram.md b/docs/en/deploy/platforms/telegram.md new file mode 100644 index 000000000..715b958d1 --- /dev/null +++ b/docs/en/deploy/platforms/telegram.md @@ -0,0 +1,29 @@ +# Connect to Telegram Bot + +## Create Bot + +Go to the [Telegram Bot Creation Page](https://t.me/botfather), follow the prompts, and create a bot. + +![Telegram Bot Creation](/assets/image/zh/deploy/bots/telegram/telegram_01.png) + +![Telegram Bot Creation](/assets/image/zh/deploy/bots/telegram/telegram_02.png) + +After creation, BotFather will return an API Token, please keep it safe. Next, turn off `Privacy Mode` to use it in group chats. + +![Telegram Bot API Token](/assets/image/zh/deploy/bots/telegram/telegram_03.png) + +## Connect to LangBot + +Open the LangBot Webui page, create a new bot + +![Connect to LangBot](/assets/image/zh/deploy/bots/telegram/connect_to_langbot.png) + +After filling in, start LangBot. If configured successfully, the log will show `[02-02 16:48:15.263] manager.py (68) - [INFO] : Initializing platform adapter 1: telegram`, keep LangBot running. + +## Use Bot + +Now you can use the bot normally in groups or private messages. + +![Telegram Bot Usage](/assets/image/zh/deploy/bots/telegram/telegram_04.png) + +![Telegram Bot Usage](/assets/image/zh/deploy/bots/telegram/telegram_05.png) diff --git a/docs/en/deploy/platforms/wechat/gewechat.md b/docs/en/deploy/platforms/wechat/gewechat.md new file mode 100644 index 000000000..f5e6c3c75 --- /dev/null +++ b/docs/en/deploy/platforms/wechat/gewechat.md @@ -0,0 +1,97 @@ +# Connect to Personal WeChat via Gewechat + +:::warning +The Gewechat project has stopped maintenance, please do not use it. +::: + +**This tutorial requires LangBot to be deployed as a Docker container**, please deploy LangBot to Docker according to the LangBot deployment documentation. + +:::info +- It is recommended to deploy on Ubuntu Linux system. +- Recommended to use [Alibaba Cloud, server prices as low as 38 yuan per year, with an additional 20% discount](https://www.aliyun.com/minisite/goods?userCode=ys4ad8gs), choose Ubuntu 22.04 or 24.04 as the system. +::: + +## Create Docker Network + +We need to deploy Gewechat and LangBot to the same Docker network to facilitate their communication. + +```bash +docker network create langbot-network +``` + +Add network configuration to the `docker-compose.yaml` file in the LangBot directory (add to the `services.langbot` field, and add the `langbot-network` network configuration under the `networks` field): + +```yaml +services: + langbot: + ... + networks: + - langbot-network + ... + +networks: + langbot-network: + external: true +``` + +In the next step when deploying Gewechat, please add an additional network configuration `--network langbot-network` after `-itd` in the startup command (docker run command). + +```bash +# For example, the original command is +docker run -itd -v /root/temp:/root/temp -p 2531:2531 -p 2532:2532 --privileged=true --name=gewe gewe /usr/sbin/init + +# After adding network configuration, the command becomes +docker run -itd --network langbot-network -v /root/temp:/root/temp -p 2531:2531 -p 2532:2532 --privileged=true --name=gewe gewe /usr/sbin/init +``` + +## Deploy Gewechat + +Please check the [Gewechat documentation](https://github.com/Devo919/Gewechat) and execute up to the `Start Service` step. + +## Fill in Information in LangBot and Start + +```json + { + "adapter": "gewechat", + "enable": true, + "gewechat_url": "http://gewe:2531", + "gewechat_file_url": "http://gewe:2532", + "port": 2286, + "callback_url": "http://langbot:2286/gewechat/callback", + "app_id": "", + "token": "" + } +``` + +- `enable` Whether to enable the personal WeChat adapter, please modify to `true` +- `gewechat_url` The Gewechat container address deployed in the previous step, the default port is 2531, no need to fill in the path +- `gewechat_file_url` Gewechat file download address, the default port is 2532, the host is the same as `gewechat_url`, no need to fill in the path +- `port` The port for LangBot to listen for Gewechat messages, default is 2286, do not modify unless necessary +- `callback_url` The callback address, which is the address for the Gewechat container to push messages to the LangBot container, needs to be filled with the complete path, for example `http://langbot:2286/gewechat/callback`, the port is consistent with `port`, the path must be `/gewechat/callback` +- `app_id` and `token` **Do not fill in**, these are used to store login information and will be automatically saved after scanning the QR code to log in for the first time + +:::info +Here we set the hostname of `gewechat_url` to `gewe`, which is the name of the Gewechat container deployed in the previous step. +Set the hostname of `callback_url` to `langbot`, which is the name of the LangBot container deployed in the previous step. + +- If you deploy according to the requirements of this tutorial, you can use the above defaults. +- If you used other container names, or other situations, please change accordingly. +::: + +## Start LangBot + +Start LangBot according to the startup command in the LangBot deployment documentation. + +After LangBot starts, a QR code will be displayed, please scan this QR code with your personal WeChat to log in. + +![alt text](/assets/image/zh/deploy/bots/gewechat/gewechat_01.png) + +After successful login, login information will be displayed, and it will start listening for personal WeChat messages. + +![alt text](/assets/image/zh/deploy/bots/gewechat/gewechat_02.png) + +:::info +After logging in, `app_id` and `token` will be saved to the configuration file, and you won't need to scan the QR code to log in again the next time you start. +::: + +Now you can chat with LangBot in private messages or group chats. diff --git a/docs/en/deploy/platforms/wecom/wecom.md b/docs/en/deploy/platforms/wecom/wecom.md new file mode 100644 index 000000000..abfc749dc --- /dev/null +++ b/docs/en/deploy/platforms/wecom/wecom.md @@ -0,0 +1,64 @@ +# Deploy WeCom Bot + +Deploy WeCom (Enterprise WeChat) bot to connect with LangBot. + +:::info +This integration method is for internal enterprise applications, used within the enterprise. If you need external customer service, please check the [WeCom Customer Service integration solution](wecomcs.md). +::: + + +## Create Bot + +Enter the [WeCom Admin Portal](https://work.weixin.qq.com/), log in to your account, after entering the main page, click `App Management`, `Build Your Own`, `Create App`, fill in the basic information of the bot, after creation, you will see a page similar to this: +![Bot Page](/assets/image/zh/deploy/bots/wecom/wecom/wecom1.png) + +## Set Callback Address + +### Get WeCom Configuration Items + +1. Open the WeCom Admin Portal homepage, click `My Enterprise`, and record the Enterprise ID at the bottom. + +![Enterprise ID](/assets/image/zh/deploy/bots/wecom/wecom/wecom2.png) + +2. Enable Contact Sync Permission + +:::info +- This configuration item is for implementing LangBot's mass messaging feature, the specific code is located in libs/wecom_api/api.py. +- LangBot is still under development, so the functionality related to this configuration item has not been implemented yet. +- This configuration item can be filled with random characters, but **cannot be empty**. +::: + +![Contact Permission](/assets/image/zh/deploy/bots/wecom/wecom/wecom5.png) + +Click `Security and Management`, `Management Tools`, `Contact Sync` + +![Contact Sync Secret](/assets/image/zh/deploy/bots/wecom/wecom/wecom3.jpg) + +Click `View Secret`, record it, this is the contacts_secret (Contact Sync Secret). + +3. Click `App Management`, find the bot you just created, view the bot's secret, and record it. + +![Bot Secret](/assets/image/zh/deploy/bots/wecom/wecom/wecom4.png) + +At this point, you have obtained three configuration items: corpid (Enterprise ID), secret (Bot Secret), and contacts_secret (Contact Sync Secret). + +### Configure Callback Address + +Enter the bot page. + +Click `Enterprise Trusted IP` at the bottom and add the server where LangBot is deployed. + +Click `Receive Messages`, `Set API Reception`, and start filling in the server message reception configuration. + +Fill in the URL as `http://your_ip_address:2290/callback/command`, where your IP address is the address of the server where LangBot is deployed. + +Click to randomly generate the Token and EncodingAESKey, and record them + +## Connect to LangBot + +![Connect to LangBot](/assets/image/zh/deploy/bots/wecom/wecom/connect_to_langbot.png) + +## Save Callback Address +When the five configuration items above have been correctly obtained and accurately filled into the WeCom adapter, then **start LangBot**. + +Return to the callback address configuration page and click save. If the above configuration items are filled in correctly, the save will be successful, which means that WeCom and LangBot can communicate (deployment successful). If you see **openapi request callback address failed**, please triple-check that your configuration items are filled in correctly. diff --git a/docs/en/deploy/platforms/wecom/wecomcs.md b/docs/en/deploy/platforms/wecom/wecomcs.md new file mode 100644 index 000000000..88248f18e --- /dev/null +++ b/docs/en/deploy/platforms/wecom/wecomcs.md @@ -0,0 +1,25 @@ +# Deploy WeCom Customer Service + +:::info + +This article details the process of deploying LangBot to a WeCom customer service account, which allows conversations between the customer service account and regular WeChat. If you only need to use it within WeCom, please refer to [WeCom Internal Application Implementation](wecom.md). + +**Deploying a WeCom customer service account requires enterprise qualifications**. Please check the [WeCom Customer Service Integration Guide](https://developer.work.weixin.qq.com/document/path/94638) and provide the corresponding enterprise qualification certificates according to this document. + +::: + +## Effect Demonstration + +![Effect Demonstration](/assets/image/zh/deploy/bots/wecom/wecomcs/wecomcs_01.jpg) + +## Connect to LangBot + +![Connect to LangBot](/assets/image/zh/deploy/bots/wecom/wecom/connect_to_langbot.png) + +The subsequent configuration method and configuration items are the same as [WeCom Internal Application](wecom.md), except that `contacts_secret` is not required. + +## Notes + +1. Be sure to follow the steps in the official WeCom documentation above and add the customer service account to `API Management`. +2. The steps are the same as for WeCom internal applications, don't miss anything. You also need to save the callback address, because essentially it's just adding other permissions to the WeCom internal application. +3. LangBot will not read sensitive information such as chat records and enterprise qualification content. diff --git a/docs/en/deploy/platforms/wxoa.md b/docs/en/deploy/platforms/wxoa.md new file mode 100644 index 000000000..5564e4405 --- /dev/null +++ b/docs/en/deploy/platforms/wxoa.md @@ -0,0 +1,55 @@ +# Deploy WeChat Official Account Bot + +Deploy WeChat Official Account bot to connect with LangBot. + +## LangBot WeChat Official Account Integration Modes + +Currently, LangBot provides two ways to integrate with WeChat Official Accounts: + +1. **Traditional Message Exchange Mode** + - Official accounts require requests to be processed within **15 seconds**, AI-generated content that exceeds this timeout cannot be sent. + - When AI content generation timeout occurs, LangBot will print request timeout information in the console. + +2. **"Send Any Text to Get Reply" Mode** + - This method can break the **15 second** limit, but **very long text** may not be sent successfully. + - When using this method, after sending a message to the official account, it will reply "AI is thinking, please send any content to get the answer". At this point, you need to send any content to get the answer. + +For more complex functionality, please contact the **LangBot team** for customized support. + +## Create Bot + +Enter the [WeChat Official Account Platform](https://mp.weixin.qq.com/), if you already have an official account, scan the QR code to log in and enter the console. +If you haven't registered an official account before, click Register Now, select `Official Account`, fill in the information in sequence, and enter the console. The console looks like this: +![Console Page](/assets/image/zh/deploy/bots/wxoa/wxoa1.png) + + +## Set Callback Address + +### Get Configuration Items + +Click `Settings and Development`, `Development Interface Management`, and you'll see a page like this: +![Set Development Items](/assets/image/zh/deploy/bots/wxoa/wxoa2.png) + +Record the `Developer ID (AppID)`, `Developer Password (AppSecret)`, and add the server where LangBot is deployed to the `IP Whitelist`. + +### Set Up Reverse Proxy + +Since WeChat Official Accounts only support http (port 80) and https (port 443), this article assumes that port 80 is already occupied and uses port 443 to reverse proxy to LangBot. + +The specific reverse proxy configuration tutorial is the same as the [QQ Official Bot Reverse Proxy Configuration](/en/deploy/platforms/qq/official_webhook.md#caddy-operation-process), except that `reverse_proxy 127.0.0.1:2284` is changed to `reverse_proxy 127.0.0.1:2287`. + +### Save Callback Address + +Click Set Server Configuration, enter `Token`, `EncodingAESKey`, and change the message encryption/decryption method to `Safe Mode`. + +At this point, open the LangBot Webui page, create a new bot, and fill in the four configuration items obtained (AppID, AppSecret, token, EncodingAESKey) + +![Set Development Items](/assets/image/zh/deploy/bots/wxoa/connect_to_langbot.png) + +In the configuration items, the `Mode` option is the selected integration mode, where the default is `drop`. `drop` is the traditional message exchange mode, and `passive` is the "send any text to get reply" mode. + +The `LoadingMessage` option is the loading message, which can be changed to other content, and is only valid when the mode is `passive`. Set `enable` to `true`. + +After filling in successfully, **start LangBot**. + +Fill in the URL as `https://your_domain_name/callback/command`, where `your_domain_name` is the domain name filled in Caddy. Click Save. If it can be saved successfully, it means the deployment is successful. If you see **System error, please try again later**, please recheck whether the above configuration content is filled in correctly. diff --git a/docs/en/deploy/settings.md b/docs/en/deploy/settings.md new file mode 100644 index 000000000..cb3702d31 --- /dev/null +++ b/docs/en/deploy/settings.md @@ -0,0 +1,97 @@ +# System Environment Settings + +Configure some environment information for system operation, located in the `data/config.yaml` file. +Generally, no modification is needed. + +```yaml +# Administrator session list, only valid when processing commands +# Format is session ID (person_xxxx or group_xxxx) +# Session ID can be found in the console logs when sending messages +# For example: +# admins: +# - person_1234567890 +# - group_1234567890 +admins: [] + +# API port +api: + port: 5300 + +# Command configuration +command: + # Command prefixes, messages with these prefixes will be recognized as commands + prefix: + - '!' + - ! + + # Command permission configuration, key is the command prefix, value is the permission type + privilege: {} + +# Concurrency settings +concurrency: + # Single pipeline concurrency + pipeline: 20 + # Single session concurrency + session: 1 + +# MCP configuration +# Should be configurable in WebUI in the future +mcp: + # MCP server list + # Currently supports two protocol MCP servers: + # - SSE + # - Stdio (Python) + # + # Format: + # SSE mode: + # name Server name, self-defined + # enable Whether to enable this Server + # mode Fixed as SSE + # url MCP SSE Server access URL + # headers Connection headers, optional + # timeout Connection timeout + # + # stdio mode: + # name Server name, self-defined + # enable Whether to enable this Server + # mode Fixed as stdio + # command Execution command + # args Command parameters + # env Command environment variables, optional + # + # For example: + # servers: + # - name: 'SSE Server' + # enable: true + # mode: SSE + # url: 'http://127.0.0.1:8000/sse' + # headers: {} + # timeout: 10 + # - name: 'stdio Server' + # enable: true + # mode: stdio + # command: 'python' + # args: ['-m', 'weather'] + # env: {} + servers: [] + +# Proxy configuration +proxy: + # HTTP proxy address + # If proxy is already set in environment variables, no configuration is needed + # For example: + # proxy: + # http: 'http://127.0.0.1:7890' + # https: 'http://127.0.0.1:7890' + http: '' + https: '' + +# System configuration +system: + # JWT configuration + jwt: + # JWT expiration time in seconds + expire: 604800 + # JWT secret key, a key will be automatically generated on first startup + secret: 'xxxx' +``` From df4c3fb3d9cbe791f3a0d7c88ea6a69e330e4c91 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 15:52:44 +0000 Subject: [PATCH 13/17] Add English network-details documentation Co-Authored-By: Junyan Qin --- docs/en/workshop/network-details.md | 62 +++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 docs/en/workshop/network-details.md diff --git a/docs/en/workshop/network-details.md b/docs/en/workshop/network-details.md new file mode 100644 index 000000000..790160f92 --- /dev/null +++ b/docs/en/workshop/network-details.md @@ -0,0 +1,62 @@ +# Network Configuration Details + +::: info +Some messaging platforms (such as personal QQ accounts) require an independent protocol endpoint to process messages. If you are using such messaging platforms, please refer to the following network configuration. +At the same time, if your LangBot instance needs to connect to other services running in containers (such as Ollama), you can also refer to the following configuration, replacing `messaging platform` with `the service you want to connect to`. +::: + +LangBot and some messaging platforms can both be deployed using Docker. Below are network configuration details for four different scenarios. + +:::info Table of Contents +[[toc]] +::: + +## Both LangBot and Messaging Platform Running in Docker Containers + +If the messaging platform (NapCat/Lagrange) and LangBot are both started using Docker, you need to configure Docker's network connection. + +Create a new network: + +```bash +docker network create langbot-network +``` + +Add network configuration to the `docker-compose.yaml` file in the LangBot directory (add to the `services.langbot` field, and add the `langbot-network` network configuration under the `networks` field): + +```yaml +services: + langbot: + ... + networks: + - langbot-network + ... + +networks: + langbot-network: + external: true +``` + +Then add the same network configuration to the messaging platform's `docker-compose.yaml` file (if started directly with docker run command, add `--network langbot-network` to the startup command). Then in the messaging platform configuration file, change the IP address in the WS reverse connection address to `langbot` (for example: `ws://langbot:2280/ws`). + +Restart both containers. + + + +## Only LangBot Running in Docker Container + +When only LangBot is deployed using Docker, ports 2280 to 2290 are already mapped to outside the container by default. At this time, LangBot can only act as a WebSocket server, accepting connections from messaging platforms as clients. + +Please set the messaging platform to ReverseWebSocket (called `reverse ws` or `WebSocket client`), with the port corresponding to the port enabled by LangBot's aiocqhttp adapter, connecting to local 127.0.0.1. + + +## Neither LangBot nor Messaging Platform Using Docker Deployment + +Configure the messaging platform and LangBot settings according to the documentation, set both ports to the same unused port, and both connect to local 127.0.0.1. + +## Only Messaging Platform Running in Docker Container (Not Recommended) + +:::warning +Only supports Linux, or other systems that support host mode docker networking. +::: + +Currently, LangBot's aiocqhttp adapter only supports reverse WebSocket connections. When the messaging platform runs in a Docker container while LangBot runs directly on the host machine, the messaging platform must act as a client connecting to LangBot. In this case, you can only set the messaging platform's network to `host` mode, and then set its WS connection address to 127.0.0.1, with the port corresponding to the port enabled by LangBot's aiocqhttp adapter. From cd867f5c6f6326ef5980a5291768b9c3e2db3f93 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 15:53:54 +0000 Subject: [PATCH 14/17] Fix index.md files to use VitePress-compatible redirects Co-Authored-By: Junyan Qin --- docs/en/index.md | 6 +++--- docs/zh/index.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/en/index.md b/docs/en/index.md index 0613a5da9..73e6f63f7 100644 --- a/docs/en/index.md +++ b/docs/en/index.md @@ -1,3 +1,3 @@ - +--- +redirect: /en/insight/guide +--- diff --git a/docs/zh/index.md b/docs/zh/index.md index 8c7b3173f..aba5fb1c3 100644 --- a/docs/zh/index.md +++ b/docs/zh/index.md @@ -1,3 +1,3 @@ - +--- +redirect: /zh/insight/guide +--- From ff77a388920040b278b88a27fcc0911d62a9c2f4 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 13 May 2025 15:54:40 +0000 Subject: [PATCH 15/17] Fix root index.md to use VitePress-compatible redirect Co-Authored-By: Junyan Qin --- docs/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/index.md b/docs/index.md index 8c7b3173f..aba5fb1c3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,3 +1,3 @@ - +--- +redirect: /zh/insight/guide +--- From 33651b2fb13b811a639f5ee142e4d5ba1c460a19 Mon Sep 17 00:00:00 2001 From: Junyan Qin Date: Wed, 14 May 2025 11:50:09 +0800 Subject: [PATCH 16/17] doc: fix problems --- docs/.vitepress/config.mts | 5 +- docs/en/deploy/langbot/docker.md | 33 ++ docs/en/deploy/langbot/manual.md | 86 +++++ docs/en/deploy/langbot/one-click/bt.md | 53 +++ docs/en/deploy/platforms/readme.md | 14 +- docs/en/index.md | 6 +- docs/en/insight/community.md | 18 +- docs/en/insight/guide.md | 8 +- docs/en/plugin/dev/messages.md | 4 +- docs/en/workshop/dify-service-api.md | 184 ++-------- docs/en/workshop/impl-platform-adapter.md | 393 ++-------------------- docs/en/workshop/mcp-details.md | 25 ++ docs/en/workshop/ppio-integration.md | 5 + docs/index.md | 6 +- docs/zh/index.md | 6 +- docs/zh/insight/guide.md | 2 +- 16 files changed, 280 insertions(+), 568 deletions(-) create mode 100644 docs/en/deploy/langbot/docker.md create mode 100644 docs/en/deploy/langbot/manual.md create mode 100644 docs/en/deploy/langbot/one-click/bt.md create mode 100644 docs/en/workshop/mcp-details.md create mode 100644 docs/en/workshop/ppio-integration.md diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 8a2088264..b6c5fe974 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -211,6 +211,7 @@ export default defineConfig({ }, }, en: { + title: "LangBot Docs", label: "English", lang: "en", link: "/en/", @@ -315,7 +316,7 @@ export default defineConfig({ ], }, { - text: "Configure Dialogue Models", + text: "Configure Models", link: "/en/deploy/models/readme", }, { @@ -323,7 +324,7 @@ export default defineConfig({ link: "/en/deploy/pipelines/readme", }, { - text: "System Environment Settings", + text: "System Settings", link: "/en/deploy/settings", }, { diff --git a/docs/en/deploy/langbot/docker.md b/docs/en/deploy/langbot/docker.md new file mode 100644 index 000000000..b2d3a2d5d --- /dev/null +++ b/docs/en/deploy/langbot/docker.md @@ -0,0 +1,33 @@ +# Docker Deployment + +:::warning +It is recommended that you first learn the following: + + - Usage of `Docker` and `Docker Compose` + - Docker container network communication configuration methods +::: + +:::info +- Please ensure Git, Docker, and Docker Compose are installed +::: + +Clone the project: + +```bash +git clone https://github.com/RockChinQ/LangBot +cd LangBot +``` + +Start the container: + +```bash +docker compose up +``` + +The container maps port `5300` for WebUI access. You can visit `http://127.0.0.1:5300` to view the WebUI. +It also maps ports `2280-2290` for reserved for message platform adapters. + +After completing the LangBot deployment, please continue reading: + +- [Bot Configuration Documentation](/en/deploy/platforms/readme). +- [Model Configuration Documentation](/en/deploy/models/readme). diff --git a/docs/en/deploy/langbot/manual.md b/docs/en/deploy/langbot/manual.md new file mode 100644 index 000000000..d95952c47 --- /dev/null +++ b/docs/en/deploy/langbot/manual.md @@ -0,0 +1,86 @@ +# Manual Deployment + +:::warning +1. Please use Python 3.10.1 (not including 3.10.0) or higher version, 3.10.14 is recommended. You need to install Python if you don't have it. +2. This method is not recommended, manual deployment may not be supported in the future. Please consider using Docker deployment. +::: + +## Installing the Main Program + +1. Go to the [Release](https://github.com/RockChinQ/LangBot/releases) page to download the latest version of the zip package (recommended) `langbot-xxx-all.zip` (do not download Source Code as it does not include WebUI). Extract it and open a command line (terminal) in the extracted directory. + +![Download Release](/assets/image/zh/deploy/langbot/manual/dl_release.png) + +:::info + +You can also use the following commands to clone the latest code (may contain unstable code) and use it: + +```bash +# Clone the main repository +git clone https://github.com/RockChinQ/LangBot +cd LangBot +``` + +If you are in mainland China, you can clone from the [GitCode mirror](https://gitcode.com/RockChinQ/LangBot): + +```bash +git clone https://gitcode.com/RockChinQ/LangBot +cd LangBot +``` + +Then manually build the frontend: + +```bash +# Build frontend, requires NodeJS >= 22 +cd web +npm install && npm run build +cd .. +``` + +::: + +2. (Recommended) Create a virtual environment + +On some Linux systems that use system package managers to manage Python dependencies, dependency conflicts may occur, so please use venv to create a virtual environment. Windows users can skip this step. + +```bash +python -m venv venv +source venv/bin/activate +``` + +3. Install dependencies + +```bash +pip install -r requirements.txt +``` + +Or use Tsinghua mirror + +```bash +pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple +``` + +4. Run the main program once to generate configuration files + +```bash +python main.py +``` + +You will see the following message: + +``` + _ ___ _ +| | __ _ _ _ __ _| _ ) ___| |_ +| |__/ _` | ' \/ _` | _ \/ _ \ _| +|____\__,_|_||_\__, |___/\___/\__| + |___/ + +⭐️Open Source: https://github.com/RockChinQ/LangBot +📖Documentation: https://docs.langbot.app + +The following files do not exist and have been automatically generated. Please modify the configuration files as needed and restart: +- plugins/__init__.py +... +``` + +You can now use Ctrl+C to exit the program and continue to the [Deploy Message Platform](/en/deploy/platforms/readme) page. diff --git a/docs/en/deploy/langbot/one-click/bt.md b/docs/en/deploy/langbot/one-click/bt.md new file mode 100644 index 000000000..bc51a4a05 --- /dev/null +++ b/docs/en/deploy/langbot/one-click/bt.md @@ -0,0 +1,53 @@ +# aaPanel Deployment + +Deploy LangBot with one click using [aaPanel](https://www.bt.cn/new/download.html?r=dk_LangBot). + +:::warning +- Unless you plan to use message platforms like Discord or Feishu that don't require additional containers, or you are familiar with Docker container networking on aaPanel, it's not recommended to use one-click deployment with aaPanel. Please consider using [Docker deployment](/en/deploy/langbot/docker). +- If you already have aaPanel installed, please update to version **9.2.0** or higher +::: + + +## Installing aaPanel + +> Recommended server systems (from aaPanel): Debian 12, Ubuntu 22, etc. +> +> This document assumes you have a server with a public IPv4 address. If you're using a home machine without a public IP, you can access the relevant pages through the internal network mentioned below. + +Open [aaPanel](https://www.bt.cn/new/download.html?r=dk_LangBot), use the Linux panel installation script provided by aaPanel official, and select the corresponding script based on your system. + +![Install aaPanel](/assets/image/zh/deploy/langbot/one-click/bt_install_01.png) + +Copy the corresponding script and paste it into your server to execute. + +## Installing LangBot + +After waiting for a while, aaPanel installation will be complete. + +You'll see a message similar to this, copy the external panel address and open it in your browser. + +![aaPanel Access Address](/assets/image/zh/deploy/langbot/one-click/bt_install_02.png) + +When you first open it, you should see a popup for `Recommended Installation Suite`. You can skip this by closing it. + +Then click on `docker` in the sidebar. If it's your first time, you'll be prompted to install Docker and Docker Compose services. Click "Install Now" if prompted, or ignore if already installed. + +Then go to `Docker -> App Store`, search for `LangBot` (note the case sensitivity), click `Install`, and use the default configuration. + +After a short wait, you'll see a container named `langbot_XXXX` showing as `Running`. + +![Create LangBot Container](/assets/image/zh/deploy/langbot/one-click/bt_langbot_01.png) + +Click the red circle in the image above to view the running logs. + +![View LangBot Container Logs](/assets/image/zh/deploy/langbot/one-click/bt_langbot_02.png) + +You can see the external and internal access paths for the WebUI configuration page (visual configuration page). Copy the public address, for example `http://xxx.xxx.xxx.xxx:5300` (where xxx.xxx.xxx.xxx is your aaPanel IP, and 5300 is the port number, which may vary as shown in the image). + +After opening it, enter your admin email and password, then click initialize. + +You can then proceed with configuration in the visual interface. + +If you prefer to directly modify JSON configuration files, you can look at the local mapping directory shown in the image above. Go to the installation directory, where the `data` folder is used to store configuration files, and the `plugins` folder is used for plugin-related files. + +Next, please continue reading the [Deploy Message Platform](/en/deploy/platforms/readme) page. diff --git a/docs/en/deploy/platforms/readme.md b/docs/en/deploy/platforms/readme.md index ab981fbbd..d97bc457b 100644 --- a/docs/en/deploy/platforms/readme.md +++ b/docs/en/deploy/platforms/readme.md @@ -7,14 +7,14 @@ Bots are used to receive message events from messaging platforms and call pipeli | Platform | Status | Notes | Tutorial | | ------ | ---- | ---- | ---- | -| OneBot v11 | ✅ | Uses a regular QQ account as a bot, requires an independent protocol client, supports OneBot v11 | [NapCat](/en/deploy/platforms/qq/aiocqhttp/napcat)
[Lagrange](/en/deploy/platforms/qq/aiocqhttp/lagrange)
[LiteLoaderQQNT](/en/deploy/platforms/qq/aiocqhttp/llonebot) | -| QQ Official Bot | ✅ | Connects to the QQ Open Platform bot API | [Tutorial](/en/deploy/platforms/qq/official_webhook) | +| Discord | ✅ | | [Tutorial](/en/deploy/platforms/discord) | +| Telegram | ✅ | | [Tutorial](/en/deploy/platforms/telegram) | +| Slack | ✅ | | [Tutorial](/en/deploy/platforms/slack) | +| Lark | ✅ | | [Tutorial](/en/deploy/platforms/lark) | +| DingTalk | ✅ | | [Tutorial](/en/deploy/platforms/dingtalk) | | WeCom (Enterprise WeChat) | ✅ | Enterprise internal bot | [Tutorial](/en/deploy/platforms/wecom/wecom) | | WeCom Customer Service | ✅ | Enterprise external bot | [Tutorial](/en/deploy/platforms/wecom/wecomcs) | | WeChat Official Account | ✅ | WeChat public platform bot | [Tutorial](/en/deploy/platforms/wxoa) | | Personal WeChat | ✅ | Personal WeChat bot | [Gewechat](/en/deploy/platforms/wechat/gewechat) | -| Feishu (Lark) | ✅ | | [Tutorial](/en/deploy/platforms/lark) | -| DingTalk | ✅ | | [Tutorial](/en/deploy/platforms/dingtalk) | -| Discord | ✅ | | [Tutorial](/en/deploy/platforms/discord) | -| Telegram | ✅ | | [Tutorial](/en/deploy/platforms/telegram) | -| Slack | ✅ | | [Tutorial](/en/deploy/platforms/slack) | +| OneBot v11 | ✅ | Uses a personal QQ account as a bot, requires an independent protocol client, supports OneBot v11 | [NapCat](/en/deploy/platforms/qq/aiocqhttp/napcat)
[Lagrange](/en/deploy/platforms/qq/aiocqhttp/lagrange)
[LiteLoaderQQNT](/en/deploy/platforms/qq/aiocqhttp/llonebot) | +| QQ Official Bot | ✅ | Connects to the QQ Open Platform bot API | [Tutorial](/en/deploy/platforms/qq/official_webhook) | diff --git a/docs/en/index.md b/docs/en/index.md index 73e6f63f7..8c7b3173f 100644 --- a/docs/en/index.md +++ b/docs/en/index.md @@ -1,3 +1,3 @@ ---- -redirect: /en/insight/guide ---- + diff --git a/docs/en/insight/community.md b/docs/en/insight/community.md index 5e4e1e33b..eecdf60a7 100644 --- a/docs/en/insight/community.md +++ b/docs/en/insight/community.md @@ -6,20 +6,6 @@ Collected at: https://blog.thelazy.top/tags/langbot -## Project Discussion QQ Groups +## 🥰 Community -> Please read the documentation carefully before joining the group to ask questions, if you cannot solve the problem then join the group to ask - -### 🥰 Community Main Groups - -Answering various questions - -- LangBot Community (3): 966235608 ([Link](https://qm.qq.com/q/JLi38whHum)) -- [Full] LangBot Community (2): 1030838208 ([Link](https://qm.qq.com/q/PF9OuQCCcM)) -- [Full] LangBot Community (1): 619154800 ([Link](https://qm.qq.com/q/1K9GjQuza)) - -### 🧩 Plugin Development Group - -Plugin development, publishing, and discussion - -- LangBot Plugin Development: 1032327138 ([Link](https://qm.qq.com/q/G7ENGTjeou)) +Join our community on Discord: [![Discord](https://img.shields.io/discord/1335141740050649118?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb)](https://discord.gg/wdNEHETs87) \ No newline at end of file diff --git a/docs/en/insight/guide.md b/docs/en/insight/guide.md index 13a28f602..31ef660d0 100644 --- a/docs/en/insight/guide.md +++ b/docs/en/insight/guide.md @@ -1,10 +1,6 @@ -# Welcome to LangBot Documentation Center +# Welcome to LangBot Documentation -LangBot is an open-source Large Language Model (LLM) native instant messaging robot platform that supports multiple messaging platforms and model providers. It provides support for LLMOps platforms such as Dify and Alibaba Cloud Bailian. It also offers a WebUI for users to quickly deploy and use. - -:::info -This is the documentation for LangBot 4.0. For 3.0 documentation, please [visit here](https://v3.docs.langbot.app). -::: +LangBot is an open-source Large Language Model (LLM) native instant messaging bot platform that supports multiple messaging platforms and model providers. It provides support for LLMOps platforms such as Dify. It also offers a WebUI for users to quickly deploy and use. ## Documentation Structure diff --git a/docs/en/plugin/dev/messages.md b/docs/en/plugin/dev/messages.md index 09553f7e8..5a273bbae 100644 --- a/docs/en/plugin/dev/messages.md +++ b/docs/en/plugin/dev/messages.md @@ -81,10 +81,10 @@ Message platform events are defined in `pkg/platform/types/events.py`. In addition to the above entities, there are also entities such as `Friend` and `Group`, which are defined in `pkg/platform/types/entities.py`. They are generally included in the above event objects, from which you can get some information not included in the plugin events. -## Accessing Message Platform Underlying API +## Accessing Message Platform Low-Level API In order to smooth out the differences between various platforms, LangBot provides an abstraction layer on top of the message platform APIs, namely the content described above and the platform adapters in the `pkg/platform/sources` directory. -However, due to the large differences between platforms, the entities and API encapsulation in the abstraction layer are not complete. If your plugin needs to access specific platform functions, you can access the underlying API according to the following instructions. +However, due to the large differences between platforms, the entities and API encapsulation in the abstraction layer are not complete. If your plugin needs to access specific platform functions, you can access the low-level API according to the following instructions. :::info The `adapter` here is the instantiated object of each adapter, corresponding to the `xxxAdapter(adapter.MessagePlatformAdapter)` class in each file under `pkg/platform/sources/`. It can be obtained from the plugin event's `query.adapter`, for example: diff --git a/docs/en/workshop/dify-service-api.md b/docs/en/workshop/dify-service-api.md index c6ab0315c..7c3b3a03f 100644 --- a/docs/en/workshop/dify-service-api.md +++ b/docs/en/workshop/dify-service-api.md @@ -1,176 +1,42 @@ -# How to Integrate with Dify? +# Using Dify with LangBot -This guide explains how to integrate LangBot with Dify, a powerful LLMOps platform that allows you to create and deploy AI applications. +[Dify](https://dify.ai) is an open-source large language model (LLM) application development platform. It combines the concepts of Backend as Service and LLMOps, enabling developers to quickly build production-grade generative AI applications. +Dify can create chat assistants (including Chatflow), Agents, text generation applications, workflows, and other types of applications. -## What is Dify? +LangBot currently supports three types of Dify applications: `Chat Assistant` (including Chatflow), `Agent`, and `Workflow`. -[Dify](https://dify.ai) is an open-source LLMOps platform that helps you build, deploy, and monitor AI applications. It provides a user-friendly interface for creating AI applications without writing code, as well as APIs for integrating with other systems. +## Creating an Application in Dify -## Integration Methods +Please follow the [Dify documentation](https://docs.dify.ai) to deploy Dify and create your application. -There are two ways to integrate LangBot with Dify: +After publishing your application, go to the `Access API` page of your application to generate an API key. -1. **Service API**: Use Dify's Service API to access applications created in Dify -2. **Completion API**: Use Dify's Completion API as a model provider in LangBot +![Dify Application API Key](/assets/image/zh/workshop/dify-service-api/dify_sv_api_01.png) -This guide focuses on the Service API integration, which is the recommended approach for most use cases. +Save the API server and API key for configuring LangBot's pipeline `AI Capability`. -## Prerequisites +:::info +The above is an example for Dify cloud service version. If you are using the self-hosted community version, please use your own Dify service address as the `base-url` in LangBot, and add `/v1` as the path. -Before you begin, you need: +- If LangBot and Dify are deployed on the same host and both are deployed using Docker, you can refer to the article: [Network Configuration Details](/zh/workshop/network-details.html#langbot-%E5%92%8C%E6%B6%88%E6%81%AF%E5%B9%B3%E5%8F%B0%E5%9D%87%E8%BF%90%E8%A1%8C%E5%9C%A8-docker-%E5%AE%B9%E5%99%A8%E4%B8%AD). In this case, add `langbot-network` to the `networks` of all containers in the docker-compose.yaml file that starts Dify, add the container name `dify-nginx` to the `nginx` container, and finally set `base-url` to `http://dify-nginx/v1` in the LangBot configuration. +- For other cases, please consult your company's operations team. +::: -1. A Dify account and a deployed application -2. The API key for your Dify application -3. LangBot version 4.0 or higher +## Configuring LangBot -## Step 1: Create a Dify Application +Open the LangBot WebUI page, add a new pipeline or switch to the AI capability configuration page in an existing pipeline. -1. Sign up or log in to [Dify](https://dify.ai) -2. Create a new application or use an existing one -3. Configure your application with the desired model, prompt, and other settings -4. Deploy your application -5. Go to the API Reference section and copy your API key +![Setting Development Item](/assets/image/zh/deploy/pipelines/dify/dify01.png) -## Step 2: Configure LangBot +::: info +### Workflow Output Key -### Method 1: Configure via WebUI +If you are using a Dify workflow application, please use `summary` as the key to pass the output content. -1. Access the LangBot WebUI at `http://your-server-ip:5300` -2. Navigate to the "Models" section -3. Click "Add Provider" -4. Select "Dify" as the provider type -5. Enter your Dify API key and application ID -6. Save the configuration +![Dify Workflow Application Output Key](/assets/image/zh/workshop/dify-service-api/dify_workflow_output_key.png) -### Method 2: Configure via Configuration File +## Output Processing -Edit the `data/config.yaml` file and add the Dify provider: - -```yaml -providers: - - id: dify - type: dify - config: - api_key: "your-dify-api-key" - application_id: "your-dify-application-id" - base_url: "https://api.dify.ai/v1" # Optional, defaults to Dify's official API endpoint -``` - -## Step 3: Test the Integration - -Send a test message to your bot: - -``` -!model use dify -Hello, how are you? -``` - -The bot should respond using your Dify application. - -## Advanced Configuration - -### Using Multiple Dify Applications - -You can configure multiple Dify applications in LangBot: - -```yaml -providers: - - id: dify-general - type: dify - config: - api_key: "your-dify-api-key-1" - application_id: "your-dify-application-id-1" - - - id: dify-specialized - type: dify - config: - api_key: "your-dify-api-key-2" - application_id: "your-dify-application-id-2" -``` - -### Configuring Input Variables - -If your Dify application uses input variables, you can configure them in LangBot: - -```yaml -providers: - - id: dify - type: dify - config: - api_key: "your-dify-api-key" - application_id: "your-dify-application-id" - input_variables: - - name: "user_name" - value: "{sender.name}" - - name: "group_name" - value: "{group.name}" -``` - -### Using Dify's File Capabilities - -If your Dify application supports file inputs, you can configure LangBot to handle them: - -```yaml -providers: - - id: dify - type: dify - config: - api_key: "your-dify-api-key" - application_id: "your-dify-application-id" - file_enabled: true -``` - -When file support is enabled, users can send files to the bot, and they will be forwarded to Dify: - -``` -!model use dify -[Attach a file] -Can you analyze this document? -``` - -## Troubleshooting - -### Connection Issues - -If you're having trouble connecting to Dify, check: - -1. Your API key is correct -2. Your application ID is correct -3. Your Dify application is deployed and active -4. Your server can access the Dify API endpoint - -### Response Format Issues - -If the responses from Dify don't look right: - -1. Check your Dify application's prompt configuration -2. Make sure your application is using the correct model -3. Verify that your application is configured for chat completion (not text completion) - -### Rate Limiting - -If you're hitting rate limits: - -1. Check your Dify plan's rate limits -2. Consider implementing rate limiting in LangBot -3. Upgrade your Dify plan if necessary - -## Example Use Cases - -### Customer Support Bot - -Create a Dify application for customer support, with knowledge of your products and services, and integrate it with LangBot to provide automated customer support in your messaging channels. - -### Content Creation Assistant - -Create a Dify application for content creation, with templates and guidelines for your brand, and integrate it with LangBot to help your team create content more efficiently. - -### Data Analysis Bot - -Create a Dify application for data analysis, with the ability to interpret and visualize data, and integrate it with LangBot to provide data insights on demand. - -## Conclusion - -Integrating LangBot with Dify allows you to leverage Dify's powerful LLMOps capabilities while using LangBot's messaging platform integrations. This combination provides a flexible and powerful solution for deploying AI applications across multiple messaging platforms. - -For more information, check out the [Dify documentation](https://docs.dify.ai) and the [LangBot documentation](https://docs.langbot.app). +When using a workflow application or Agent application, if you enable `track-function-calls` in LangBot's pipeline `Output Processing`, a message of `calling function xxx` will be output to the user when Dify executes each tool call. +However, if you are using `ChatFlow` under the `chat` application (Chat Assistant -> Workflow Orchestration), it will only output the text returned by the Answer (direct reply) node regardless of the settings. +::: diff --git a/docs/en/workshop/impl-platform-adapter.md b/docs/en/workshop/impl-platform-adapter.md index a91573c9f..9983ea85c 100644 --- a/docs/en/workshop/impl-platform-adapter.md +++ b/docs/en/workshop/impl-platform-adapter.md @@ -1,389 +1,50 @@ -# How to Implement a Message Platform Adapter? +# Implementing a Message Platform Adapter -This guide explains how to implement a custom message platform adapter for LangBot, allowing you to connect the bot to new messaging platforms. +This article will use the `aiocqhttp` adapter as an example to explain how to implement a new message platform adapter for LangBot, in order to integrate new message platform protocols such as WhatsApp and Discord. -## Overview +## Registration -LangBot uses a modular architecture that allows for easy integration with various messaging platforms. The core component for platform integration is the `MessageSourceAdapter`, which handles the communication between LangBot and the messaging platform. +```text +Add a new message platform adapter, for example: integrate WhatsApp, Discord, etc. -![architecture](/assets/image/zh/deploy/bots/arch.png) +- Base class: `pkg.platform.adapter.MessageSourceAdapter` +- Decorator: `pkg.platform.adapter.adapter_class(name: str)` +- Reference implementation: `pkg.platform.sources/` +``` -## Prerequisites +To implement a message platform adapter, we essentially need to implement a class that inherits from the `base class` and register it using the `decorator`. Finally, it will be available for users to select and use during runtime. -Before implementing a platform adapter, you should: +### Contributing to the Main Repository -1. Understand the API of the messaging platform you want to integrate with -2. Have basic knowledge of Python and asynchronous programming -3. Be familiar with LangBot's architecture and event system - -## Implementation Steps - -### Step 1: Create a New Adapter Class - -Create a new Python file in the `langbot/bot/adapters` directory: +Create a new file `aiocqhttp.py` in the `pkg/platform/sources/` directory: ```python -from langbot.bot import MessageSourceAdapter -from langbot.message import Message, PersonSource, GroupSource, Sender -from langbot.event import PersonNormalMessageReceived, GroupNormalMessageReceived +from .. import adapter -class MyPlatformAdapter(MessageSourceAdapter): - """ - Adapter for MyPlatform. - """ - - def __init__(self, config): - """ - Initialize the adapter. - - Args: - config: The adapter configuration - """ - super().__init__(config) - self.config = config - # Initialize platform-specific client - self.client = None +@adapter.adapter_class("aiocqhttp") +class AiocqhttpAdapter(adapter.MessageSourceAdapter): + pass ``` -### Step 2: Implement the Required Methods - -Implement the required methods for the adapter: +Also import this module in `pkg/platform/manager.py` at line 40: ```python -async def start(self): - """ - Start the adapter. - """ - # Initialize and start the platform client - self.client = MyPlatformClient( - api_key=self.config.get("api_key"), - # Other platform-specific configuration - ) - - # Register event handlers - self.client.on_message(self.on_message) - - # Start the client - await self.client.start() - -async def stop(self): - """ - Stop the adapter. - """ - # Stop the platform client - if self.client: - await self.client.stop() - -async def send_message(self, source, content, **kwargs): - """ - Send a message to a specific destination. - - Args: - source: The destination to send the message to - content: The content of the message - **kwargs: Additional arguments - - Returns: - The message ID - """ - # Send the message using the platform client - if isinstance(source, PersonSource): - # Send a direct message - message_id = await self.client.send_direct_message( - source.identity, - content, - **kwargs - ) - elif isinstance(source, GroupSource): - # Send a group message - message_id = await self.client.send_group_message( - source.identity, - content, - **kwargs - ) - else: - raise ValueError(f"Unsupported source type: {type(source)}") - - return message_id + from .sources import nakuru, aiocqhttp, qqbotpy ``` -### Step 3: Implement Event Handlers +Finally, write your own manifest file by referring to other adapters' manifest files. -Implement event handlers to convert platform-specific events to LangBot events: +## Event and Message Converters -```python -async def on_message(self, platform_message): - """ - Handle a message from the platform. - - Args: - platform_message: The platform-specific message - """ - # Convert the platform message to a LangBot message - message = self.convert_message(platform_message) - - # Determine the type of message and emit the appropriate event - if isinstance(message.source, PersonSource): - # Emit a person message event - await self.emit(PersonNormalMessageReceived(message)) - elif isinstance(message.source, GroupSource): - # Emit a group message event - await self.emit(GroupNormalMessageReceived(message)) - -def convert_message(self, platform_message): - """ - Convert a platform-specific message to a LangBot message. - - Args: - platform_message: The platform-specific message - - Returns: - A LangBot message - """ - # Extract message information - message_id = platform_message.get("id") - content = platform_message.get("content") - time = platform_message.get("timestamp") - - # Extract sender information - sender_id = platform_message.get("sender_id") - sender_name = platform_message.get("sender_name") - sender = Sender(sender_id, sender_name) - - # Determine the source type - if platform_message.get("is_group"): - # Group message - group_id = platform_message.get("group_id") - source = GroupSource("my_platform", group_id) - else: - # Direct message - source = PersonSource("my_platform", sender_id) - - # Create and return the message - return Message( - source=source, - content=content, - message_id=message_id, - sender=sender, - time=time - ) -``` +The main work of a message platform adapter is to `receive events from the message platform protocol, convert them into LangBot's format`, pass them to the core components, and after LangBot's core components process them, pass the content that needs to be replied to the message platform adapter, which then `converts the content into the platform protocol format and sends it to the message platform`. -### Step 4: Register the Adapter +The following diagram shows the processing patterns for two scenarios: message platform protocol without SDK and with mature SDK. In the first case, we need to implement connection management and network format data conversion to LB message Python objects according to the message platform protocol documentation. In the second case, the message platform has a mature SDK, we only need to create connections according to the SDK documentation, register event listeners, and implement message converters. -Register the adapter in the `langbot/bot/adapters/__init__.py` file: +![alt text](/assets/image/zh/workshop/impl-platform-adapter/workshop_impl_platform_adapter_01.png) -```python -from .my_platform import MyPlatformAdapter +In the `pkg.platform.sources.aiocqhttp` file, we can see not only `AiocqhttpAdapter`, but also `class AiocqhttpMessageConverter(adapter.MessageConverter):` and `class AiocqhttpEventConverter(adapter.EventConverter):`. -__all__ = [ - # Other adapters - "MyPlatformAdapter", -] -``` +- `MessageConverter` is used for converting between LangBot messages and platform messages. +- `EventConverter` is used for converting between LangBot events and platform events. -### Step 5: Add Configuration Schema - -Add the configuration schema for your adapter in the `langbot/schema/bot.py` file: - -```python -MY_PLATFORM_SCHEMA = { - "type": "object", - "properties": { - "type": {"type": "string", "enum": ["my_platform"]}, - "api_key": {"type": "string"}, - # Other platform-specific configuration - }, - "required": ["type", "api_key"], -} - -# Add to the BOT_SCHEMA -BOT_SCHEMA = { - "type": "object", - "properties": { - "bots": { - "type": "array", - "items": { - "oneOf": [ - # Other adapter schemas - MY_PLATFORM_SCHEMA, - ], - }, - }, - }, -} -``` - -## Example: Implementing a Discord Adapter - -Here's a simplified example of implementing a Discord adapter: - -```python -import discord -from langbot.bot import MessageSourceAdapter -from langbot.message import Message, PersonSource, GroupSource, Sender -from langbot.event import PersonNormalMessageReceived, GroupNormalMessageReceived - -class DiscordAdapter(MessageSourceAdapter): - """ - Adapter for Discord. - """ - - def __init__(self, config): - """ - Initialize the adapter. - - Args: - config: The adapter configuration - """ - super().__init__(config) - self.config = config - self.client = discord.Client(intents=discord.Intents.all()) - - # Register event handlers - @self.client.event - async def on_ready(): - print(f"Logged in as {self.client.user}") - - @self.client.event - async def on_message(message): - # Ignore messages from the bot itself - if message.author == self.client.user: - return - - # Convert and emit the message - await self.on_message(message) - - async def start(self): - """ - Start the adapter. - """ - # Start the Discord client - await self.client.start(self.config.get("token")) - - async def stop(self): - """ - Stop the adapter. - """ - # Stop the Discord client - if self.client: - await self.client.close() - - async def send_message(self, source, content, **kwargs): - """ - Send a message to a specific destination. - - Args: - source: The destination to send the message to - content: The content of the message - **kwargs: Additional arguments - - Returns: - The message ID - """ - # Send the message using the Discord client - if isinstance(source, PersonSource): - # Send a direct message - user = await self.client.fetch_user(int(source.identity)) - message = await user.send(content) - return str(message.id) - elif isinstance(source, GroupSource): - # Send a group message - channel = self.client.get_channel(int(source.identity)) - message = await channel.send(content) - return str(message.id) - else: - raise ValueError(f"Unsupported source type: {type(source)}") - - async def on_message(self, discord_message): - """ - Handle a message from Discord. - - Args: - discord_message: The Discord message - """ - # Convert the Discord message to a LangBot message - message = self.convert_message(discord_message) - - # Determine the type of message and emit the appropriate event - if isinstance(message.source, PersonSource): - # Emit a person message event - await self.emit(PersonNormalMessageReceived(message)) - elif isinstance(message.source, GroupSource): - # Emit a group message event - await self.emit(GroupNormalMessageReceived(message)) - - def convert_message(self, discord_message): - """ - Convert a Discord message to a LangBot message. - - Args: - discord_message: The Discord message - - Returns: - A LangBot message - """ - # Extract message information - message_id = str(discord_message.id) - content = discord_message.content - time = discord_message.created_at.timestamp() - - # Extract sender information - sender_id = str(discord_message.author.id) - sender_name = discord_message.author.name - sender = Sender(sender_id, sender_name) - - # Determine the source type - if discord_message.guild: - # Group message - group_id = str(discord_message.channel.id) - source = GroupSource("discord", group_id) - else: - # Direct message - source = PersonSource("discord", sender_id) - - # Create and return the message - return Message( - source=source, - content=content, - message_id=message_id, - sender=sender, - time=time - ) -``` - -## Testing Your Adapter - -To test your adapter: - -1. Add your adapter configuration to the `data/config.yaml` file: - -```yaml -bots: - - id: my_platform - type: my_platform - config: - api_key: your-api-key - # Other platform-specific configuration -``` - -2. Start LangBot and check the logs for any errors -3. Send a test message on your platform and verify that LangBot receives and processes it -4. Send a test message from LangBot to your platform and verify that it's delivered correctly - -## Best Practices - -- Handle errors gracefully and provide meaningful error messages -- Implement proper rate limiting to avoid being blocked by the platform -- Use asynchronous programming to avoid blocking the event loop -- Document your adapter thoroughly, including configuration options and limitations -- Follow the platform's terms of service and API guidelines -- Implement proper logging to help with debugging -- Consider implementing reconnection logic for platforms with unstable connections -- Test your adapter thoroughly with different types of messages and scenarios - -## Conclusion - -Implementing a custom message platform adapter allows you to extend LangBot to work with new messaging platforms. By following the steps and best practices outlined in this guide, you can create a robust and reliable adapter that integrates seamlessly with LangBot's architecture. - -If you encounter any issues or have questions, feel free to ask in the [community chat](https://qm.qq.com/q/Nnz7Vbj8OU) or open an issue on [GitHub](https://github.com/RockChinQ/LangBot/issues). +TBC... diff --git a/docs/en/workshop/mcp-details.md b/docs/en/workshop/mcp-details.md new file mode 100644 index 000000000..2acc1839c --- /dev/null +++ b/docs/en/workshop/mcp-details.md @@ -0,0 +1,25 @@ +# MCP Ecosystem Details + +The [MCP Protocol](https://modelcontextprotocol.io/) is a protocol that describes the interaction between applications and LLMs, and has been adopted by various products. LangBot can act as an MCP Client to connect to MCP Servers, providing extensive tools for LLMs to use during conversations with users. + +## Finding MCP Servers + +To use MCP tools in LangBot, you need to find and connect to existing MCP Servers. +There are two types of MCP Servers in the market: + +- Executable files implemented in Node or Python, communicating with LangBot through `stdio` (standard input/output) +- Web services based on HTTP protocol, communicating with LangBot through HTTP protocol + +:::info + +- You can check existing MCP Servers in the [MCP Server List](https://github.com/punkpeye/awesome-mcp-servers). +- For online hosted MCP Servers, you can find them at [Composio MCP](https://mcp.composio.dev/). +- To write your own MCP Server, you can refer to the [MCP Protocol Documentation](https://modelcontextprotocol.io/quickstart/server) and [MCP Server Examples](https://modelcontextprotocol.io/examples). + +::: + +## Configuring MCP Server + +Please refer to the [System Settings Documentation](/zh/deploy/settings.html) + +After configuration, you can use the `!func` command in chat to view registered MCP tools. When chatting with models that support Function Calling, LangBot will automatically call MCP tools. \ No newline at end of file diff --git a/docs/en/workshop/ppio-integration.md b/docs/en/workshop/ppio-integration.md new file mode 100644 index 000000000..0f1094732 --- /dev/null +++ b/docs/en/workshop/ppio-integration.md @@ -0,0 +1,5 @@ +# Integrating Models with PPIO Cloud API + +Open the WebUI page, and select [PPIO Cloud](https://ppio.cn/user/register?from=ppinfra&invited_by=QJKFYD&utm_source=github_langbot) when adding a model. + +![model](/assets/image/zh/workshop/ppio-integration/model.png) \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index aba5fb1c3..8c7b3173f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,3 +1,3 @@ ---- -redirect: /zh/insight/guide ---- + diff --git a/docs/zh/index.md b/docs/zh/index.md index aba5fb1c3..8c7b3173f 100644 --- a/docs/zh/index.md +++ b/docs/zh/index.md @@ -1,3 +1,3 @@ ---- -redirect: /zh/insight/guide ---- + diff --git a/docs/zh/insight/guide.md b/docs/zh/insight/guide.md index dac263495..84ba8875b 100644 --- a/docs/zh/insight/guide.md +++ b/docs/zh/insight/guide.md @@ -1,4 +1,4 @@ -# 欢迎来到 LangBot 文档中心 +# 欢迎访问 LangBot 文档 LangBot 是一个开源的大语言模型(LLM)原生即时通信机器人平台,支持多种消息平台和模型供应商。提供对 Dify、阿里云百炼等 LLMOps 平台的支持。并提供 WebUI 供用户快速部署和使用。 From 0f1defd9dd00bd16e4fc0260f04c9805dc9730aa Mon Sep 17 00:00:00 2001 From: Junyan Qin Date: Wed, 14 May 2025 11:51:35 +0800 Subject: [PATCH 17/17] chore: delete legacy en docs --- .backup/en/config/function/command.md | 42 ---- .backup/en/config/function/pipeline.md | 203 --------------- .backup/en/config/function/platform.md | 124 --------- .backup/en/config/function/provider.md | 234 ----------------- .backup/en/config/function/system.md | 131 ---------- .../en/config/metadata/adapter-qq-botpy.md | 21 -- .backup/en/config/metadata/llm-models.md | 53 ---- .backup/en/config/metadata/sensitive-words.md | 3 - .backup/en/deploy/langbot/docker.md | 35 --- .backup/en/deploy/langbot/manual.md | 84 ------- .backup/en/deploy/langbot/one-click/bt.md | 51 ---- .backup/en/deploy/network-details.md | 54 ---- .../en/deploy/platforms/aiocqhttp/lagrange.md | 56 ----- .../en/deploy/platforms/aiocqhttp/llonebot.md | 13 - .../en/deploy/platforms/aiocqhttp/napcat.md | 27 -- .../en/deploy/platforms/aiocqhttp/shamrock.md | 75 ------ .backup/en/deploy/platforms/gocq.md | 150 ----------- .backup/en/deploy/platforms/mirai.md | 153 ------------ .backup/en/deploy/platforms/official.md | 11 - .backup/en/deploy/quick-config/config.md | 150 ----------- .backup/en/deploy/update.md | 9 - .backup/en/develop/comp-arch.md | 3 - .backup/en/index.md | 39 --- .backup/en/insight/community.md | 19 -- .backup/en/insight/guide.md | 18 -- .backup/en/insight/intro.md | 114 --------- .backup/en/plugin/dev/api-ref.md | 59 ----- .backup/en/plugin/dev/extension.md | 104 -------- .backup/en/plugin/dev/messages.md | 82 ------ .backup/en/plugin/dev/tutor.md | 225 ----------------- .backup/en/plugin/plugin-intro.md | 117 --------- .backup/en/plugin/tech-info.md | 110 -------- .backup/en/public/QChatGPT-0516.png | Bin 222242 -> 0 bytes .../c12e8803945846f4ea730e249db79574.txt | 1 - .backup/en/public/chrome-192.png | Bin 3658 -> 0 bytes .backup/en/public/chrome-512.png | Bin 11998 -> 0 bytes .backup/en/public/favicon.ico | Bin 9662 -> 0 bytes .backup/en/public/langbot-logo-0.5x.png | Bin 11949 -> 0 bytes .backup/en/public/langbot-logo-block.png | Bin 14953 -> 0 bytes .backup/en/public/langbot-logo-final.png | Bin 14431 -> 0 bytes .backup/en/public/langbot-logo.png | Bin 24667 -> 0 bytes .backup/en/public/social.png | Bin 160443 -> 0 bytes .backup/en/tob/contact.md | 6 - .backup/en/tob/index.md | 5 - .backup/en/tob/platform.md | 33 --- .backup/en/tob/provider.md | 13 - .backup/en/usage/command.md | 43 ---- .backup/en/usage/faq.md | 27 -- .backup/en/webui/intro.md | 20 -- .backup/en/webui/settings.md | 31 --- .backup/en/webui/system.md | 17 -- .backup/en/workshop/dify-integration.md | 236 ------------------ .backup/en/workshop/dify-service-api.md | 20 -- .backup/en/workshop/impl-platform-adapter.md | 71 ------ .backup/en/workshop/one-api.md | 73 ------ 55 files changed, 3165 deletions(-) delete mode 100644 .backup/en/config/function/command.md delete mode 100644 .backup/en/config/function/pipeline.md delete mode 100644 .backup/en/config/function/platform.md delete mode 100644 .backup/en/config/function/provider.md delete mode 100644 .backup/en/config/function/system.md delete mode 100644 .backup/en/config/metadata/adapter-qq-botpy.md delete mode 100644 .backup/en/config/metadata/llm-models.md delete mode 100644 .backup/en/config/metadata/sensitive-words.md delete mode 100644 .backup/en/deploy/langbot/docker.md delete mode 100644 .backup/en/deploy/langbot/manual.md delete mode 100644 .backup/en/deploy/langbot/one-click/bt.md delete mode 100644 .backup/en/deploy/network-details.md delete mode 100644 .backup/en/deploy/platforms/aiocqhttp/lagrange.md delete mode 100644 .backup/en/deploy/platforms/aiocqhttp/llonebot.md delete mode 100644 .backup/en/deploy/platforms/aiocqhttp/napcat.md delete mode 100644 .backup/en/deploy/platforms/aiocqhttp/shamrock.md delete mode 100644 .backup/en/deploy/platforms/gocq.md delete mode 100644 .backup/en/deploy/platforms/mirai.md delete mode 100644 .backup/en/deploy/platforms/official.md delete mode 100644 .backup/en/deploy/quick-config/config.md delete mode 100644 .backup/en/deploy/update.md delete mode 100644 .backup/en/develop/comp-arch.md delete mode 100644 .backup/en/index.md delete mode 100644 .backup/en/insight/community.md delete mode 100644 .backup/en/insight/guide.md delete mode 100644 .backup/en/insight/intro.md delete mode 100644 .backup/en/plugin/dev/api-ref.md delete mode 100644 .backup/en/plugin/dev/extension.md delete mode 100644 .backup/en/plugin/dev/messages.md delete mode 100644 .backup/en/plugin/dev/tutor.md delete mode 100644 .backup/en/plugin/plugin-intro.md delete mode 100644 .backup/en/plugin/tech-info.md delete mode 100644 .backup/en/public/QChatGPT-0516.png delete mode 100644 .backup/en/public/c12e8803945846f4ea730e249db79574.txt delete mode 100644 .backup/en/public/chrome-192.png delete mode 100644 .backup/en/public/chrome-512.png delete mode 100644 .backup/en/public/favicon.ico delete mode 100644 .backup/en/public/langbot-logo-0.5x.png delete mode 100644 .backup/en/public/langbot-logo-block.png delete mode 100644 .backup/en/public/langbot-logo-final.png delete mode 100644 .backup/en/public/langbot-logo.png delete mode 100644 .backup/en/public/social.png delete mode 100644 .backup/en/tob/contact.md delete mode 100644 .backup/en/tob/index.md delete mode 100644 .backup/en/tob/platform.md delete mode 100644 .backup/en/tob/provider.md delete mode 100644 .backup/en/usage/command.md delete mode 100644 .backup/en/usage/faq.md delete mode 100644 .backup/en/webui/intro.md delete mode 100644 .backup/en/webui/settings.md delete mode 100644 .backup/en/webui/system.md delete mode 100644 .backup/en/workshop/dify-integration.md delete mode 100644 .backup/en/workshop/dify-service-api.md delete mode 100644 .backup/en/workshop/impl-platform-adapter.md delete mode 100644 .backup/en/workshop/one-api.md diff --git a/.backup/en/config/function/command.md b/.backup/en/config/function/command.md deleted file mode 100644 index 438cf33d9..000000000 --- a/.backup/en/config/function/command.md +++ /dev/null @@ -1,42 +0,0 @@ -# command.json Configuration - -:::info Table of Contents -[[toc]] -::: - -The `data/config/command.json` file is used to configure command-related settings. - -## Command Privilege - -Set the privilege configuration for each command. - -The privilege level for regular users is 1, while the privilege level for administrators (set in `system.json`) is 2. - -Here, you can set the minimum privilege level for each command. If set to 1, both users and administrators can use the command. If set to 2, only administrators can use the command. - -Subcommands need to be specified with a dot separator, such as `"plugin.on"`. Example: - -```json -{ - "plugin": 1, - "plugin.on": 2 -} -``` - -This means the minimum privilege level for the `plugin` command is 1, and the minimum privilege level for the `plugin.on` command is 2. Each command has a default privilege level. - -If not set, the default level will be used. The default privilege level for each command class is defined in `pkg/command/operators`. - -## Command Prefix - -Set the command prefix. It is configured as an array, and the program will treat messages that match the prefix as commands (in group chats, they must also comply with group response rules). - -The following example sets the command prefix to `!`, `!`, and `#`: - -```json -"command-prefix": [ - "!", - "!", - "#" -] -``` \ No newline at end of file diff --git a/.backup/en/config/function/pipeline.md b/.backup/en/config/function/pipeline.md deleted file mode 100644 index 5252cf23d..000000000 --- a/.backup/en/config/function/pipeline.md +++ /dev/null @@ -1,203 +0,0 @@ -# pipeline.json Configuration - -:::info Table of Contents -[[toc]] -::: - -The `data/config/pipeline.json` file configures the operational flow of processing a request. After the platform adapter receives a message event, it sends it to the pipeline for queued processing. You can configure features such as blacklists and whitelists, group response rules, whether to check incoming messages, incoming message ignore rules, whether to check for sensitive words, Baidu Cloud content review settings, incoming message length limits, and rate limiting. - -## Access Control - -```json -"access-control":{ - "mode": "blacklist", - "blacklist": [], - "whitelist": [] -}, -``` - -`mode`: Set the mode to either `whitelist` (only process messages from sessions in the `whitelist`) or `blacklist` (ignore messages from sessions in the `blacklist`). - -`blacklist`: Format `{type}_{id}`, example: `"blacklist": ["group_12345678", "person_12341234"]`. - -`whitelist`: Format `{type}_{id}`, example: `"whitelist": ["group_12345678", "person_12341234"]`. - -## Group Message Response Rules - -Only process messages from sessions that pass the `access-control` check. Multiple keys can be included. To set specific response rules for a group, use the group ID as the key. Groups not specified will use the default response rules. - -```json -"respond-rules": { - "default": { - "at": true, - "prefix": [ - "/ai", "!ai", "!ai", "ai" - ], - "regexp": [], - "random": 0.0 - } -}, -``` - -`at`: `true` or `false`. When `true`, all messages that mention the bot will be responded to. - -`prefix`: Set response prefixes. Messages with the specified prefixes will be responded to even if they do not mention the bot. The prefix will be removed before sending to the AI. For example: - -```json -"prefix": ["/ai", "!ai", "!ai", "ai"] - -In this case, the message "ai hello" will be responded to, and the prefix "ai" will be removed before sending to the AI, resulting in "hello". -``` - -`regexp`: Regular expression matching. [Regular Expression Tutorial](https://www.runoob.com/regexp/regexp-syntax.html). - -`random`: Random match probability, ranging from 0.0 to 1.0, corresponding to 0% to 100%. When set to 1.0, all messages will be responded to. - -You can set specific response rules for individual groups, for example: - -```json -"respond-rules": { - "default": { - "at": true, - "prefix": [ - "/ai", "!ai", "!ai", "ai" - ], - "regexp": [], - "random": 0.0 - }, - "123456":{ - "at": true, - "prefix": [ - "/ai", "!ai", "!ai", "ai" - ], - "regexp": [], - "random": 0.0 - }, - "789012":{ - "at": true, - "prefix": [ - "/ai", "!ai", "!ai", "ai" - ], - "regexp": [], - "random": 0.0 - } -}, -``` - -## Incoming Message Check Switch - -```json -"income-msg-check": true, -``` - -`income-msg-check`: Whether to check incoming messages. - -## Incoming Message Ignore Rules - -```json -"ignore-rules": { - "prefix": ["/"], - "regexp": [] -}, -``` - -Incoming messages that match the rules will be ignored. Only effective when `income-msg-check` is `true`. - -`prefix`: Ignore messages with the prefix `/`. - -`regexp`: Ignore messages that match the regular expression. - -## Local Sensitive Word Check - -```json -"check-sensitive-words": true, -``` - -`check-sensitive-words`: Whether to check for sensitive words. The sensitive word list is located in `data/config/sensitive-words.json`. - -## Baidu Cloud Content Review Configuration - -```json -"baidu-cloud-examine": { - "enable": false, - "api-key": "", - "api-secret": "" -}, -``` - -`baidu-cloud-examine`: Controls whether to use Baidu Cloud content review. - -`enable`: When `enable=true`, it will always check the AI response results. Only checks incoming messages when `income-msg-check` is `true`. - -`api-key`: The `API_KEY` from the Baidu AI Open Platform. - -`api-secret`: The `SECRET_KEY` from the Baidu AI Open Platform. - -*`API_KEY` and `SECRET_KEY` are assigned to users after creating an application. They are strings used to identify users and perform signature verification for access. They can be viewed in the **Application List** in the AI Service Console.* - -## Rate Limit Rules - -```json -"rate-limit": { - "strategy": "drop", - "algo": "fixwin", - "fixwin": { - "default": { - "window-size": 60, - "limit": 60 - } - } -} -``` - -`strategy`: The handling strategy when the request rate exceeds the limit in a session. `drop` discards new requests, while `wait` waits for the request rate to drop below the limit. - -`algo`: The algorithm used. Currently, only `fixwin` (fixed window) is supported, which limits the number of requests processed within a window period. You can implement other rate-limiting algorithms by referring to the plugin development tutorial. - -`fixwin`: Specific rate settings, limiting the number of requests processed within a window period. Supports specifying rate limits for specific sessions in the format `{type}_{id}`, example: `group_12345678`, `person_12341234`. - -`window-size`: The size of the window period in seconds. - -`limit`: The maximum number of requests processed within the window period. - -For example: - -```json -"fixwin": { - "default": { - "window-size": 60, - "limit": 60 - }, - "group_12345678": { - "window-size": 30, - "limit": 60 - }, - "person_12341234": { - "window-size": 60, - "limit": 60 - } -} -``` - -This will set: - -- A default group window period of 60 seconds, with a maximum of 60 requests processed. -- A group with ID 12345678 has a window period of 30 seconds, with a maximum of 60 requests processed. -- A user with ID 12341234 has a window period of 60 seconds, with a maximum of 60 requests processed. - -## Message History Truncation - -Truncates the current session's message history before sending it to the model to limit the length of the message passed to the model. - -```json -"msg-truncate": { - "method": "round", - "round": { - "max-round": 10 - } -} -``` - -`method`: The truncation method. `round` truncates by rounds. Currently, only `round` is supported. You can implement other truncation methods by referring to the plugin development tutorial. - -`round`: Truncates by rounds. `max-round` specifies the maximum number of historical message rounds to retain. \ No newline at end of file diff --git a/.backup/en/config/function/platform.md b/.backup/en/config/function/platform.md deleted file mode 100644 index ee5a758f3..000000000 --- a/.backup/en/config/function/platform.md +++ /dev/null @@ -1,124 +0,0 @@ -# platform.json Configuration - -:::info Table of Contents -[[toc]] -::: - -The `data/config/platform.json` file configures the message platforms that the program connects to and related message processing settings. This includes the list of enabled message adapters, whether to track the process of content function calls, whether to quote the original message when replying in a group, whether to @ the original user when replying in a group, the forced message delay range, long message handling strategies, and whether to hide AI exception information from users. - -## Message Platform Adapters (platform-adapters) - -```json -"platform-adapters": [ - { - "adapter": "nakuru", - "enable": false, - "host": "127.0.0.1", - "ws_port": 8080, - "http_port": 5700, - "token": "" - }, - { - "adapter": "aiocqhttp", - "enable": false, - "host": "0.0.0.0", - "port": 2280, - "access-token": "", - }, - { - "adapter": "qq-botpy", - "enable": false, - "appid": "", - "secret": "", - "intents": [ - "public_guild_messages", - "direct_message" - ] - } -], -``` - -Currently, four message platform adapters are supported: `go-cqhttp`, `aiocqhttp`, and `qq-botpy`. Multiple adapters of the same or different types can be enabled simultaneously. -For configuration details of each message platform, please refer to the deployment documentation. - -### Multiple Adapters Example - -```json -"platform-adapters": [ - { - "adapter": "aiocqhttp", - "enable": false, - "host": "0.0.0.0", - "port": 2280, - "access-token": "", - }, - { - "adapter": "nakuru", - "enable": true, - "host": "127.0.0.1", - "ws_port": 8081, - "http_port": 5701, - "token": "token" - } -] -``` - -## Track Content Function Calls (track-function-calls) - -```json -"track-function-calls": true, -``` - -Whether to track the process of content function calls. If enabled, the records of content function calls during the conversation will also be sent to the user. - -If disabled (false), only the final result will be sent to the user. - -> For details on the content function feature, please refer to the plugin documentation. - -## Quote Original Message (quote-origin) - -```json -"quote-origin": false, -``` - -Whether to quote the original message when replying in a group. - -## @ Original User (at-sender) - -```json -"at-sender": false, -``` - -Whether to @ the original user when replying in a group. - -## Forced Message Delay Range (force-delay) - -```json -"force-delay": [0, 0], -``` - -The forced message delay range before sending the response back to the user, to prevent risk control. The unit is seconds. - -## Long Message Handling Strategy (long-text-process) - -```json -"long-text-process": { - "threshold": 256, - "strategy": "forward", - "font-path": "" -}, -``` - -`threshold`: The threshold. Messages longer than this value will use the long message handling strategy. - -`strategy`: The long message handling strategy. Currently supported strategies are `forward` (forward message component) and `image` (convert text to image). - -`font-path`: The font used for rendering images. If not set, on Windows, the system's Microsoft YaHei font will be attempted. If not found, the strategy will switch to `forward`. If not set on non-Windows systems, it will directly switch to `forward`. - -## Hide AI Interface Exception Information from Users (hide-exception-info) - -``` -"hide-exception-info": true -``` - -Whether to hide AI exception information from users. If true, when an exception occurs while requesting the AI interface, an error prompt will be returned to the user, and the error details will be output to the console. \ No newline at end of file diff --git a/.backup/en/config/function/provider.md b/.backup/en/config/function/provider.md deleted file mode 100644 index 2106be924..000000000 --- a/.backup/en/config/function/provider.md +++ /dev/null @@ -1,234 +0,0 @@ -# provider.json Configuration - -:::info Table of Contents -[[toc]] -::: - -The `data/config/provider.json` file contains configuration items related to AI interface providers. - -## Enable Chat Function (enable-chat) - -```json -"enable-chat": true, -``` - -`enable-chat`: Whether to enable the AI chat function. - -## Enable AI Vision Function (enable-vision) - -```json -"enable-vision": true, -``` - -`enable-vision`: Whether to enable the AI vision function. The model used must also support the vision function. For details, refer to the metadata section. - -## Model API Keys (keys) - -::: info -Different model providers may use different API formats. LangBot determines the usage of each model through a triplet (requester, key group, model name). To view supported models or add custom models, refer to the metadata configuration section in the `llm-models.json` file. -::: - -```json -"keys": { - "openai": [ - "sk-1234567890" - ], - "anthropic": [ - "sk-1234567890" - ], - "moonshot": [ - "sk-1234567890" - ], - "deepseek": [ - "sk-1234567890" - ], - "gitee-ai": [ - "XXXXX" - ] -}, -``` - -`keys`: Configure key groups in dictionary format, where each key group's name is the key and the value is a list of keys. For the correspondence between models and key groups, refer to the metadata section in the `llm-models.json` file. - -Currently supported key groups: - -- `openai`: Key group for [OpenAI](https://openai.com/). If you don't have an OpenAI API Key, you can [get one here](https://api.qhaigc.net/). -- `anthropic`: Key group for [Anthropic](https://anthropic.com/). -- `moonshot`: Key group for [Moonshot](https://moonshot.cn/) (Moonshot AI). -- `deepseek`: Key group for [Deepseek](https://deepseek.com/) (Deepseek AI). -- `gitee-ai`: Key group for [Gitee AI](https://ai.gitee.com/) Serverless API. - -## Large Model Requester (requester) - -```json -"requester": { - "openai-chat-completions": { - "base-url": "https://api.openai.com/v1", - "args": {}, - "timeout": 120 - }, - "anthropic-messages": { - "base-url": "https://api.anthropic.com", - "args": { - "max_tokens": 1024 - }, - "timeout": 120 - }, - "moonshot-chat-completions": { - "base-url": "https://api.moonshot.cn/v1", - "args": {}, - "timeout": 120 - }, - "deepseek-chat-completions": { - "base-url": "https://api.deepseek.com", - "args": {}, - "timeout": 120 - }, - "ollama-chat": { - "base-url": "http://127.0.0.1:11434", - "args": {}, - "timeout": 600 - }, - "gitee-ai-chat-completions": { - "base-url": "https://ai.gitee.com/v1", - "args": {}, - "timeout": 120 - } - }, -``` - -`requester`: Configure requesters, where each requester's name is the key and the value is the requester configuration. For the correspondence between models and requesters, refer to the metadata section in the `llm-models.json` file. For implementing requesters, refer to the plugin development tutorial. - -Currently supported requesters (unless a new requester is implemented via a plugin, new requester configurations cannot be added to `requester`): - -- `openai-chat-completions`: ChatCompletion requester for [OpenAI](https://openai.com/). -- `anthropic-messages`: Requester for [Anthropic](https://anthropic.com/) (Claude). -- `moonshot-chat-completions`: Requester for [Moonshot](https://moonshot.cn/) (Moonshot AI). -- `deepseek-chat-completions`: Requester for [Deepseek](https://deepseek.com/) (Deepseek AI). -- `ollama-chat`: Requester for [Ollama](https://ollama.com/), which does not require a key and directly requests the Ollama service at the target address. -- `gitee-ai-chat-completions`: Requester for [Gitee AI](https://ai.gitee.com/) Serverless API (OpenAI-compatible interface). - -`base-url`: Set the API endpoint. - -`args`: Additional parameters for the request, excluding the model, set in dictionary format. Refer to the documentation of each provider for details. - -`timeout`: Set the request timeout in seconds. For models that take longer, it is recommended to set a larger value. - -## Model to Use (model) - -```json -"model": "gpt-3.5-turbo", -``` - -`model`: Set the name of the model to use. This model must exist in the `llm-models.json` metadata. - -For the list of supported models and the corresponding requesters and key groups for each model, refer to the metadata section in the `llm-models.json` file. - -## Scenario Preset (Personality) (prompt) - -```json -"prompt-mode": "normal", -"prompt": { - "default": "If the user wants help later, please say 'Input !help to get help.'." -} -``` - -`prompt-mode`: Set the scenario preset mode, with values `normal` (single preset mode) and `full-scenario` (full historical conversation mode). - -In `normal` mode, use the `prompt` below, which can have multiple entries. - -```json -"prompt": { - "default": "If the user wants help later, please say 'Input !help to get help.'.", - "help": "If the user wants help later, please say 'Input !help to get help.'." -} -``` - -The `normal` mode also supports reading the contents of files in the `data/prompts` directory as individual System Prompts, where the file name is the `prompt` name. - -In use, set it as the default via the `!default set ` command (replace `` entirely). - -### Full-Scenario Mode Configuration - -Add complete historical conversations as files to the `data/scenario/` directory, referencing `data/scenario/default.json`. - -```json -{ - "prompt": [ - { - "role": "system", - "content": "You are a helpful assistant. If I need help, you should say 'Input !help to get help.'" - }, - { - "role": "assistant", - "content": "Okay, I am a capable AI assistant. If you need help, I will say 'Input !help to get help.'" - } - ] -} -``` - -`role` is the role of the message, which can be `user` (user), `assistant` (AI), or `system` (system). -`content` is a string representing the message content. -In use, set it as the default via the `!default set ` command (replace `` entirely). - -## Request Runner (runner) - -```json -"runner": "local-agent", -``` - -Set which runner will handle the chat messages. The default is `local-agent`. -Currently supported: - -- `local-agent`: A local Agent mechanism implemented by LangBot, managing conversations and invoking plugins. Must be set to `local-agent` to use `content functions`. -- `dify-service-api`: Use the Service API mechanism of [Dify](https://dify.ai/), supporting chat assistants, Agents, and workflow applications. - -## Dify Service API Configuration (dify-service-api) - -```json - "dify-service-api": { - "base-url": "https://api.dify.ai/v1", - "app-type": "chat", - "chat": { - "api-key": "app-1234567890", - "timeout": 120 - }, - "agent": { - "api-key": "app-1234567890", - "timeout": 120 - }, - "workflow": { - "api-key": "app-1234567890", - "output-key": "summary", - "timeout": 120 - } - } -``` - -Only when `runner` is set to `dify-service-api`, configure the following: - -- `base-url`: The address of the Dify Service API, default is `https://api.dify.ai/v1`, which is the address of the official Dify cloud service. If you are using a self-deployed community edition, set it to your self-deployed address. -- `app-type`: The type of Dify application to use. Supports `chat` - chat assistant (including advanced orchestration), `agent` - Agent, `workflow` - workflow; fill in the corresponding API parameters for the application type below. -- `chat`: Configuration for Dify chat assistant applications. - - `api-key`: API key for the Dify chat assistant application. - - `timeout`: Request timeout for the Dify chat assistant application, in seconds, default is 120 seconds. -- `agent`: Configuration for Dify Agent applications. - - `api-key`: API key for the Dify Agent application. - - `timeout`: Request timeout for the Dify Agent application, in seconds, default is 120 seconds. -- `workflow`: Configuration for Dify workflow applications. - - `api-key`: API key for the Dify workflow application. - - `output-key`: Output key for the Dify workflow application, used to retrieve the output results of the workflow application. Default is `summary`, corresponding to the output variable of the end node in the workflow orchestration. - - `timeout`: Request timeout for the Dify workflow application, in seconds, default is 120 seconds. - -![Dify Workflow Application Output Key](/assets/image/config_provider_01.png) - -When using workflows, LangBot will explicitly pass the following parameters, which you can add to the start node of the Dify workflow: - -- `langbot_user_message_text`: Plain text of the user's message. -- `langbot_session_id`: User session ID, `person_` for private chats, `group_` for group chats. -- `langbot_conversation_id`: User conversation ID, generated by LangBot. It will be regenerated after the user resets the conversation. - -![Dify Workflow Start Node Configuration](/assets/image/config_provider_02.png) - -When using workflow applications or Agent applications, if `track-function-calls` in `platform.json` is enabled, a message `Calling function xxx` will be output to the user when Dify executes each tool call. -However, if using `ChatFlow` under the `chat` application (chat assistant -> workflow orchestration), only the text returned by the Answer (direct reply) node will be output. \ No newline at end of file diff --git a/.backup/en/config/function/system.md b/.backup/en/config/function/system.md deleted file mode 100644 index 25421f3c8..000000000 --- a/.backup/en/config/function/system.md +++ /dev/null @@ -1,131 +0,0 @@ -# system.json Configuration Items - -:::info Table of Contents -[[toc]] -::: - -The `data/config/system.json` file is used to configure system settings, such as administrators, network proxies, whether to report telemetry data for developer analysis, logging level, session message processing concurrency, pipeline message processing concurrency, help messages, and more. - -## Admin Sessions (admin-sessions) - -```json -"admin-sessions": [], -``` - -`admin-sessions`: Set administrator sessions in the format {type}_{id}, where type is either "group" or "person". For example: - -```json -"admin-sessions": ["group_123456789", "person_123456789"], -``` - -::: info -1. If you are using the official QQ API, the session ID is the mapped value. You can refer to [qq-botpy metadata](https://docs.langbot.app/config/metadata/adapter-qq-botpy.html) or check the console output when the program processes messages. -2. If a session specifies a group, all messages from everyone in that group will be considered as administrator messages. -::: - -## Network Proxies (network-proxies) - -::: info -It is recommended to set proxies via environment variables, as the configuration file settings **will override environment variables**. -Environment variables to set: `http_proxy` and `https_proxy` -::: - -```json -"network-proxies": { - "http": null, - "https": null -}, -``` - -`network-proxies`: Set up network proxies, which are forward proxies. Both http and https must be filled in. For example: - -```json -"network-proxies": { - "http": "http://127.0.0.1:7890", - "https": "http://127.0.0.1:7890" -}, -``` - -## Report Telemetry Data (report-usage) - -```json -"report-usage": true, -``` - -`report-usage`: Whether to report telemetry data for developer analysis. This does not include sensitive information. - -## Logging Level (logging-level) - -```json -"logging-level": "info", -``` - -`logging-level`: Currently unused. Debug logging can only be enabled via the environment variable DEBUG=true. - -## Session Message Processing Concurrency (session-concurrency) - -```json -"session-concurrency": { - "default": 1 -}, -``` - -`session-concurrency`: The concurrency level for session message processing, granular to each session. The session format is {type}_{id}. Sessions not specified will use the default configuration. - -## Pipeline Message Processing Concurrency (pipeline-concurrency) - -```json -"pipeline-concurrency": 20, -``` - -`pipeline-concurrency`: The concurrency level for pipeline message processing, granular to the entire program. Currently, the FCFS algorithm is used to schedule requests. - -## Telemetry Server URL (qcg-center-url) - -The target URL for pushing telemetry data during LangBot operation. The default is the official URL. If you have deployed [qcg-center](https://github.com/RockChinQ/qcg-center) yourself, you can change it to your own URL. - -```json -"qcg-center-url": "https://api.qchatgpt.rockchin.top/api/v2" -``` - -## Help Message (help-message) - -```json -"help-message": "LangBot - 😎High stability, 🧩plugin support, 🦄multimodal ChatGPT QQ bot🤖\nLink: https://langbot.app" -``` - -`help-message`: The help message displayed when a user sends the !help command. - -## HTTP Interface (http-api) - -Configure the HTTP service, which needs to be enabled to access the WebUI. - -```json -"http-api": { - "enable": true, - "host": "0.0.0.0", - "port": 5300, - "jwt-expire": 604800 -}, -``` - -`enable`: Whether to enable the HTTP interface -`host`: The address the HTTP interface listens on -`port`: The port the HTTP interface listens on -`jwt-expire`: JWT expiration time in seconds - -## Persistence Configuration (persistence) - -Configure database information. - -```json -"persistence": { - "sqlite": { - "path": "data/persistence.db" - }, - "use": "sqlite" -} -``` - -`sqlite`: Use SQLite database, `path` is the database file path -`use`: Which database to use, currently only `sqlite` is supported \ No newline at end of file diff --git a/.backup/en/config/metadata/adapter-qq-botpy.md b/.backup/en/config/metadata/adapter-qq-botpy.md deleted file mode 100644 index a22549146..000000000 --- a/.backup/en/config/metadata/adapter-qq-botpy.md +++ /dev/null @@ -1,21 +0,0 @@ -# adapter-qq-botpy.json Metadata - -`data/metadata/adapter-qq-botpy.json` is the metadata for the QQ official API adapter. When using the group chat interface of the official API, the group ID and member ID are in string format, while LangBot internally only supports integer IDs. To ensure that the same correspondence is maintained after a restart, this data needs to be saved to a file. - -## Format - -```json -{ - "mapping": { - "groups": { - "15944D6D2B525C6E3536134D4669F067": 443241151434655 - }, - "members": { - "547A6244A731B4BEEBE5EE2AE4CD00A8": 232437565267568 - } - } -} -``` - -- `groups`: Group ID mapping, where the key is the string group ID and the value is the integer group ID. -- `members`: Member ID mapping, where the key is the string member ID and the value is the integer member ID. \ No newline at end of file diff --git a/.backup/en/config/metadata/llm-models.md b/.backup/en/config/metadata/llm-models.md deleted file mode 100644 index 7adea2310..000000000 --- a/.backup/en/config/metadata/llm-models.md +++ /dev/null @@ -1,53 +0,0 @@ -# llm-models.json Metadata - -`data/metadata/llm-models.json` is a list of supported large language models. This file specifies the models that can be used in the `model` list within `data/config/provider.json`, and it defines the corresponding `Key Manager` and `API Requester` for each model. - -## Format - -```json -{ - "list": [ - { - "name": "default", - "requester": "openai-chat-completions", - "token_mgr": "openai", - "tool_call_supported": false, - "vision_supported": false - }, - { - "name": "gpt-3.5-turbo", - "tool_call_supported": true - }, - { - "name": "gpt-4o", - "tool_call_supported": true, - "vision_supported": true - } - ] -} -``` - -The models are listed in the `list` array, and each model has the following fields: - -- `name`: The name of the model, used to specify the model in the `model` list within `data/config/provider.json`. -- `model_name`: The model name used when making requests, used to distinguish between the same model using different requesters. If not set, the `name` field will be used. -- `requester`: The name of the requester, specifying which requester to use to get responses from this model. Different requesters correspond to different API formats. - - For supported requesters, please refer to the `requester` field in the `provider.json` configuration file. - - To implement a new requester, you need to write a plugin. Please refer to the `Component Extension` section in the plugin development documentation. -- `token_mgr`: The Key Manager, specifying the key group used by this model, corresponding to the `keys` list in `data/config/provider.json`. -- `tool_call_supported`: Whether the model supports tool calls, indicating if the model can use `content functions`. -- `vision_supported`: Whether the model supports image input, indicating if the model can use `image input`. - -The `default` model is mandatory and serves as the default configuration for models. Other models do not need to set all fields; only the `name` field is required, and other fields will inherit the configuration from the `default` model. - -## Automatic Update Mechanism - -Every time LangBot starts, the program automatically fetches the latest model list from the central server. The `name` field in the local `llm-models.json` is compared with the model list returned by the central server. If a model does not exist locally, it will be added. If it already exists locally, it will be overwritten by the remote version. - -## Custom Models - -You can add custom models by following the format described above. - -- If you need to use an interface (requester) that is currently not supported, you will need to extend the requester yourself. Please refer to the plugin development documentation -> Component Extension. -- You can add your own key groups in the `keys` list within `data/config/provider.json`. -- It is not recommended to modify the default model configuration, as it will be overwritten by the automatically updated information upon startup. \ No newline at end of file diff --git a/.backup/en/config/metadata/sensitive-words.md b/.backup/en/config/metadata/sensitive-words.md deleted file mode 100644 index 9460fc54d..000000000 --- a/.backup/en/config/metadata/sensitive-words.md +++ /dev/null @@ -1,3 +0,0 @@ -# sensitive-words.json Metadata - -This is a local sensitive word dictionary. You can modify it by referring to the content of `data/metadata/sensitive-words.json`. \ No newline at end of file diff --git a/.backup/en/deploy/langbot/docker.md b/.backup/en/deploy/langbot/docker.md deleted file mode 100644 index 7f0dccfe7..000000000 --- a/.backup/en/deploy/langbot/docker.md +++ /dev/null @@ -1,35 +0,0 @@ -# Docker Deployment (Recommended for Linux Only) - -:::warning -Please **ensure** you have the following capabilities: - - Familiarity with `Docker` and `Docker Compose` - - Understanding of network communication configuration between containers - - Knowledge of container file mounting mechanisms - - Strong hands-on skills and ability to search for resources -::: - -:::info -Please make sure Git, Docker, and Docker Compose are installed first. -::: - -Clone the project using Git: - -```bash -git clone https://github.com/RockChinQ/LangBot -cd LangBot -``` - -Start the container: - -```bash -docker compose up -``` - -On the first startup, you will be prompted to create a configuration file. Please proceed with the configuration as instructed. - -The container will map port `5300` for the WebUI, which you can access at `http://127.0.0.1:5300`. -It will also map ports `2280-2290` for reverse connections from message platform adapters using the OneBot protocol. Please continue reading the [Message Platform Integration Documentation](/deploy/platforms/aiocqhttp/napcat). - -## Notes - -- Installed plugins will be saved in the `plugins` directory (mapped to the local `plugins` directory). When installing plugins, the necessary dependencies may be automatically installed. If you **recreate** the container, the installed plugins will be loaded, but the incremental dependencies will not be installed, which may cause issues. You can delete the plugin directory and restart, then reinstall the plugins to allow the program to automatically install the required dependencies. \ No newline at end of file diff --git a/.backup/en/deploy/langbot/manual.md b/.backup/en/deploy/langbot/manual.md deleted file mode 100644 index 5e3f0b63c..000000000 --- a/.backup/en/deploy/langbot/manual.md +++ /dev/null @@ -1,84 +0,0 @@ -# Manual Deployment (Recommended, Works on All Platforms) - -:::warning -1. Please use Python 3.10.1 (excluding 3.10.0) or higher. Version 3.10.14 is recommended. If you don't have Python installed, you need to install it yourself. -::: - -## Installing the Main Program - -1. Go to the [Release](https://github.com/RockChinQ/LangBot/releases) page and download the latest version of the compressed package (recommended) `langbot-xxx-all.zip` (do not download the Source Code as it does not include the WebUI). Extract the package and open the command line (terminal) in the extracted directory. - -![Download Release](/assets/image/dl_release.png) - -:::info - -You can also use the following command to clone the latest code (which may contain unstable code) and use it: - -```bash -git clone https://github.com/RockChinQ/LangBot -cd LangBot - -# Build the frontend, requires NodeJS >= 22 -cd web -npm install && npm run build -cd .. -``` -::: - -2. (Recommended) Create a virtual environment - -On some Linux systems that use the system package manager to manage Python dependencies, dependency conflicts may occur. Therefore, please use venv to create a virtual environment. Windows users can skip this step. - -```bash -python -m venv venv -source venv/bin/activate -``` - -3. Install dependencies - -```bash -pip install -r requirements.txt -``` - -Or use the Tsinghua mirror - -```bash -pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple -``` - -3. Run the main program once to generate the configuration file - -```bash -python main.py -``` - -The following message will be displayed - -``` - _ ___ _ -| | __ _ _ _ __ _| _ ) ___| |_ -| |__/ _` | ' \/ _` | _ \/ _ \ _| -|____\__,_|_||_\__, |___/\___/\__| - |___/ - -⭐️Open Source Address: https://github.com/RockChinQ/LangBot -📖Documentation Address: https://docs.langbot.app - -The following files do not exist and have been automatically generated. Please modify the configuration files as needed and restart: -- plugins/__init__.py -- plugins/plugins.json -- data/config/command.json -- data/config/pipeline.json -- data/config/platform.json -- data/config/provider.json -- data/config/system.json -- data/config/sensitive-words.json -- data/scenario/default.json -``` - -You can now exit the program using Ctrl+C and continue to the [Deploying Message Platform](/deploy/platforms/aiocqhttp/napcat.html) page. - -After deploying the message platform and filling in the configuration files, run the program again: -```bash -python main.py -``` \ No newline at end of file diff --git a/.backup/en/deploy/langbot/one-click/bt.md b/.backup/en/deploy/langbot/one-click/bt.md deleted file mode 100644 index 64d86a681..000000000 --- a/.backup/en/deploy/langbot/one-click/bt.md +++ /dev/null @@ -1,51 +0,0 @@ -# Deploying with BT Panel - -Deploy LangBot with one click using the [BT Panel](https://www.bt.cn/new/download.html?r=dk_LangBot). - -## Installing BT Panel - -:::warning -If you have already installed BT Panel, please update to version **9.2.0** or above. -::: - -> Recommended server systems (from BT Panel): Debian 12, Ubuntu 22, etc. -> -> This guide assumes the server has a public IPv4 address. If you are using a home machine without a public IP, you can access the relevant pages via the intranet as described below. - -Open the [BT Panel](https://www.bt.cn/new/download.html?r=dk_LangBot) and use the Linux panel installation script provided by BT Panel. Choose the script corresponding to your system. - -![Installing BT Panel](/assets/image/bt_install_01.png) - -Copy the corresponding script and paste it into your server to execute. - -## Installing LangBot - -After waiting for a while, the BT Panel installation will be completed. - -You will see a prompt similar to the following. Copy the external panel address and open it in your browser. - -![BT Panel Access Address](/assets/image/bt_install_02.png) - -The first time you open it, a `Recommended Installation Suite` popup may appear. You can skip this by closing it. - -Then, click on the `docker` option in the sidebar. If it’s your first time, you will be prompted to install Docker and Docker Compose services. Click `Install Now`. If they are already installed, you can ignore this step. - -Next, go to `Docker -> App Store`, search for `LangBot` (note the case sensitivity), and click `Install` using the default configuration. - -After a short wait, you will see a container named `langbot_XXXX` showing as `Running`. - -![Creating LangBot Container](/assets/image/bt_langbot_01.png) - -Click on the red circle in the image above to view the running logs. - -![Viewing LangBot Container Logs](/assets/image/bt_langbot_02.png) - -You will see the external and internal access paths for the WebUI configuration page (visual configuration page). Copy the public address, for example, `http://xxx.xxx.xxx.xxx:5300` (where `xxx.xxx.xxx.xxx` is the BT Panel IP and `5300` is the port number, which may vary as shown in the image). - -Open the address, enter the admin email and password, and then click `Initialize`. - -You can now configure settings in the visual interface. - -If you prefer to directly modify the JSON configuration file, you can refer to the local mapped directory shown in the image above. Navigate to the installation directory, where the `data` folder stores configuration files and the `plugins` folder stores plugin-related files. - -Next, please continue reading the [Deploying Message Platform](/deploy/platforms/aiocqhttp/napcat.html) page. \ No newline at end of file diff --git a/.backup/en/deploy/network-details.md b/.backup/en/deploy/network-details.md deleted file mode 100644 index afe2f183a..000000000 --- a/.backup/en/deploy/network-details.md +++ /dev/null @@ -1,54 +0,0 @@ -# Detailed Network Configuration - -LangBot and some messaging platforms can be deployed using Docker. Below is a detailed explanation of network configurations for four scenarios. - -:::info Table of Contents -[[toc]] -::: - -## Both LangBot and Messaging Platform Run in Docker Containers - -If both the messaging platform (NapCat/Lagrange) and LangBot are started using Docker, you need to configure the Docker network connection. - -Create a new network: - -```bash -docker network create langbot-network -``` - -Add the network configuration to the `docker-compose.yaml` file in the LangBot directory (add it under the `services.langbot` field and include the `langbot-network` configuration under the `networks` field): - -```yaml -services: - langbot: - ... - networks: - - langbot-network - ... - -networks: - langbot-network: - external: true -``` - -Then, add the same network configuration to the `docker-compose.yaml` file of the messaging platform (if the messaging platform is started using the `docker run` command, add `--network langbot-network` to the startup command). Next, modify the IP address in the WS reverse connection address in the messaging platform configuration file to `langbot` (e.g., `ws://langbot:2280/ws`). - -Restart both containers. - -## Only LangBot Runs in a Docker Container - -When only LangBot is deployed using Docker, ports 2280 to 2290 are mapped to the host by default. In this case, LangBot can only act as a WebSocket server, accepting connections from the messaging platform as a client. - -Set the messaging platform to ReverseWebSocket (referred to as `reverse ws` or `WebSocket client`), with the port corresponding to the port enabled by LangBot's aiocqhttp adapter, and connect to localhost 127.0.0.1. - -## Neither LangBot nor Messaging Platform Uses Docker Deployment - -Configure the messaging platform and LangBot settings according to the documentation, ensuring both use the same unused port, and connect to localhost 127.0.0.1. - -## Only Messaging Platform Runs in a Docker Container (Not Recommended) - -:::warning -Only supported on Linux or other systems that support host mode Docker networking. -::: - -Currently, LangBot's aiocqhttp adapter only supports reverse WebSocket connections. When the messaging platform runs in a Docker container and LangBot runs directly on the host, the messaging platform must act as a client to connect to LangBot. In this case, you must set the messaging platform's network to `host` mode and configure its WS connection address to 127.0.0.1, with the port corresponding to the port enabled by LangBot's aiocqhttp adapter. \ No newline at end of file diff --git a/.backup/en/deploy/platforms/aiocqhttp/lagrange.md b/.backup/en/deploy/platforms/aiocqhttp/lagrange.md deleted file mode 100644 index 36a985730..000000000 --- a/.backup/en/deploy/platforms/aiocqhttp/lagrange.md +++ /dev/null @@ -1,56 +0,0 @@ -# Deploying the Message Platform Lagrange - -## Introduction to Lagrange - -Lagrange is a reverse engineering framework for the QQNT protocol, designed to be lightweight and relatively stable. It connects via the OneBot 11 protocol and requires the use of `aiocqhttp` as the adapter. - -::: warning -You need to use NT QQ to interact with the bot; the older version of QQ will not work properly. -::: - -## Deployment Steps - -The following provides a general guide for deployment on Windows. For detailed steps or instructions for other platforms, please refer to the [Lagrange Official Documentation](https://lagrangedev.github.io/Lagrange.Doc/Lagrange.OneBot/). - -### Environment Setup - -Install the .NET 8 runtime. [Click here to download directly from the Microsoft official website](https://dotnet.microsoft.com/zh-cn/download/dotnet/thank-you/runtime-desktop-8.0.4-windows-x64-installer). - -### Download the Latest Build from Github Actions - -You need to log in to Github to download. If you don't need certain features, you can download from the Release section. Refer to image 3. - -[Github Actions](https://github.com/KonataDev/Lagrange.Core/actions) - -Follow the images to download: - -![Image 1](/assets/image/dl_lgr_1.png) - -![Image 2](/assets/image/dl_lgr_2.png) - -![Image 3](/assets/image/dl_lgr_3.png) - -### Final Steps - -Unzip the downloaded zip file, navigate to the directory containing `Lagrange.OneBot.exe`, and open a command prompt in this directory. Enter the following command: - -```bash -.\Lagrange.OneBot -``` - -Run it once. If a QR code is output, you can directly scan it with your bot account to log in (if the QR code is unclear, you can find the image file in the `lagrange/data` directory). - -If you cannot log in, please check if the `SignServerUrl` in the `appsettings.json` configuration file of Lagrange is correctly filled. You can find the signing address in the Lagrange documentation. - -## Modifying Configuration - -To configure Lagrange to connect to LangBot, edit the `appsettings.json` configuration file of Lagrange. Ensure that the connection settings under `Implementations` match the content shown in the image below: - -![Configuration Connection](/assets/image/config_lgr.png) - -The `Type` must be `ReverseWebSocket`; -The `Host` should be the IP address of the host running LangBot. If it's on the same host, you can use `127.0.0.1`; -The `Suffix` must be `/ws`; -The `Port` must match the port that the `aiocqhttp` adapter in the LangBot message platform configuration is listening on. - -Next, please read [Filling in Configuration Information](/deploy/quick-config/config). \ No newline at end of file diff --git a/.backup/en/deploy/platforms/aiocqhttp/llonebot.md b/.backup/en/deploy/platforms/aiocqhttp/llonebot.md deleted file mode 100644 index 2ab739db4..000000000 --- a/.backup/en/deploy/platforms/aiocqhttp/llonebot.md +++ /dev/null @@ -1,13 +0,0 @@ -# Deploying the Message Platform LiteLoaderQQNT - -# LiteLoaderQQNT - -- [LiteLoaderQQNT's OneBot API Plugin](https://github.com/LLOneBot/LLOneBot) injects into the QQ client and converts it to the OneBot 11 protocol. - -Tutorial for installing LiteLoaderQQNT: https://llonebot.github.io/zh-CN/guide/getting-started - -![image-20240515185909108](/assets/image/llob_cfg.png) - -Ensure that the IP address in the URL is the address of the host running LangBot (use 127.0.0.1 if running on the same network), the port must match the port that the aiocqhttp adapter in the LangBot message platform configuration is listening on, and the suffix must be `/ws`: - -Next, please read [Filling in Configuration Information](/deploy/quick-config/config). \ No newline at end of file diff --git a/.backup/en/deploy/platforms/aiocqhttp/napcat.md b/.backup/en/deploy/platforms/aiocqhttp/napcat.md deleted file mode 100644 index 163391060..000000000 --- a/.backup/en/deploy/platforms/aiocqhttp/napcat.md +++ /dev/null @@ -1,27 +0,0 @@ -# Deploying the Messaging Platform NapCat - -## NapCat Installation Tutorial - -Follow the [NapCat Official Documentation for Installation](https://napcat.napneko.icu/guide/start-install). - -## NapCat Configuration Tutorial - -This example uses the webui operation. For other configuration methods, please refer to the NapCat documentation. - -After logging in, you can view the logs in the console or Bash. Look for the following line: -`[NapCat] [WebUi] WebUi Publish Panel Url: http://x.x.x.x:6099/webui?token=xxxxx` -![napcat_webui_0](/assets/image/napcat_webui_0.png) - -Copy the URL to your browser, or on Windows 11, press Ctrl and click the URL. After scanning the QR code to log in, you will be directed to the network configuration page. - -Click to add a network configuration for the WebSocket client, then enable it. In the URL field, enter `ws://127.0.0.1:2280/ws` and save it. -![napcat_webui_1](/assets/image/napcat_webui_1.png) -![napcat_webui_2](/assets/image/napcat_webui_2.png) - -::: info -- The port number depends on the `port` field of `aiocqhttp` in `data/config/platform.json`. The default is 2280. Please refer to [Filling in Configuration Information](/deploy/quick-config/config) to continue the configuration. -- You must use the WebSocket Client (Reverse WS) to connect to LangBot. Make sure to modify the IP address and port according to your actual situation, and the suffix must be `/ws`. -- If both LangBot and NapCat are running in containers, please refer to [Container Network Configuration](/deploy/network-details). -::: - -Next, please read [Filling in Configuration Information](/deploy/quick-config/config). \ No newline at end of file diff --git a/.backup/en/deploy/platforms/aiocqhttp/shamrock.md b/.backup/en/deploy/platforms/aiocqhttp/shamrock.md deleted file mode 100644 index ea3edec6b..000000000 --- a/.backup/en/deploy/platforms/aiocqhttp/shamrock.md +++ /dev/null @@ -1,75 +0,0 @@ -# Deploying the Shamrock Messaging Platform - -> This document only covers the basic steps. For detailed operations, troubleshooting, and the latest information, please refer to the [OpenShamrock Official Documentation](https://whitechi73.github.io/OpenShamrock/). - -::: tip - -Project archived, not recommended for continued use. - -::: - -## Introduction to OpenShamrock - -While tools like go-cqhttp simulate the QQ protocol for communication, Shamrock uses Android hooks to simulate clicks on QQ, offering higher stability. - -Optional platforms (or devices): - -- Emulator -- Linux server -- PVE -- Cloud phone -- Physical phone - -## Steps (Emulator) - -### Environment Setup - -**Note: It is recommended to use the [mumu emulator](https://mumu.163.com/).** - -Refer to [this document](https://forum.libfekit.so/d/60-mumu12mo-ni-qi-an-zhuang-magiskhe-lsposed) to install Magisk and LSPosed. - -Remember to enable the writable system disk and root permissions in the mumu emulator. - -![](/assets/image/sham_env_1.png) - -![image-20240119154032682](/assets/image/sham_env_2.png) - -### Installing OpenShamrock - -Download the latest OpenShamrock installation package from [OpenShamrock's Actions](https://github.com/whitechi73/OpenShamrock/actions). Make sure to download the version with the `all` suffix, such as `Shamrock-v1.0.7.r253.81be383-all.zip`. Extract the package and install it in the mumu emulator. - -#### Configuring Shamrock - -1. Modify the settings on the Shamrock settings page as shown: - -Enable passive WebSocket and fill in the address and port that the LangBot's aiocqhttp adapter is listening on (see the configuration information page for details). - -![img](/assets/image/sham_cfg_1.png) - -![img](/assets/image/sham_cfg_2.png) - -The passive WebSocket address should be the address and port that the LangBot's aiocqhttp adapter is listening on, and the path must be `/ws`, for example, `ws://127.0.0.1:2280/ws`. Refer to the configuration information page for details. - -2. Go to the LSPosed module management page and enable the Shamrock module. - -![image-20240119154110877](/assets/image/sham_cfg_3.png) - -### Installing QQ - -Refer to [this page](https://whitechi73.github.io/OpenShamrock/guide/faq.html#%E6%94%AF%E6%8C%81%E7%9A%84qq%E7%89%88%E6%9C%AC) for the supported QQ versions and choose the latest one. - -Download the corresponding version from [here](https://qq.cn.uptodown.com/android/versions) and install it in the mumu emulator. - -### Logging into the Bot QQ Account - -1. Open QQ, log in to the bot QQ account, close QQ, and restart the emulator. -2. Start LSPosed, and Shamrock will automatically run. Start QQ. -3. Open the Shamrock page, and it should display `Activated` and show the account information and related message logs. - -### Next Steps - -Refer to the configuration information page to connect via the aiocqhttp adapter. - -![img](https://cos.thelazy.cn/pictures/202405292250017.jpeg) - -Next, please read [Filling in Configuration Information](/deploy/quick-config/config). \ No newline at end of file diff --git a/.backup/en/deploy/platforms/gocq.md b/.backup/en/deploy/platforms/gocq.md deleted file mode 100644 index 336d9f6b0..000000000 --- a/.backup/en/deploy/platforms/gocq.md +++ /dev/null @@ -1,150 +0,0 @@ -# Deploying the Messaging Platform go-cqhttp - -> This document only covers the basic steps. For detailed operations, troubleshooting, and the latest information, please refer to the [official go-cqhttp documentation](https://docs.go-cqhttp.org/). - -::: warning - -gocq is not archived, but the original developer has stated: [https://github.com/Mrs4s/go-cqhttp/issues/2471](https://github.com/Mrs4s/go-cqhttp/issues/2471) - -::: - -## Download go-cqhttp - -Download the latest go-cqhttp executable from the [go-cqhttp Release](https://github.com/Mrs4s/go-cqhttp/releases/latest) (it is recommended to directly download the executable zip file instead of the installer). - -![Download gocq](/assets/image/dl_gocq.png) - -## Run and Configure go-cqhttp - -Unzip and run the file. The first time you run it, it will ask for the network protocol to be opened. **Please enter `02` and press Enter. You must enter `02`❗❗❗❗❗❗❗** - -``` -C:\Softwares\go-cqhttp.old> .\go-cqhttp.exe -Configuration file not found, generating configuration file for you! -Please select the communication method you need: -> 0: HTTP communication -> 1: Cloud function service -> 2: Forward Websocket communication -> 3: Reverse Websocket communication -Please enter the number(s) you need (0-9), you can enter multiple, and the same number can be entered multiple times (e.g., 233) -Your choice is:02 -``` - -A prompt will indicate that the `config.yml` file has been generated. Close go-cqhttp. - -Open the `config.yml` file in the same directory as go-cqhttp. - -1. Edit the account login information - -Only modify the `uin` and `password` below to the QQ number and password of the bot account you want to log in to. - -```yaml -account: # Account related - uin: 1233456 # QQ account - password: '' # Use QR code login when the password is empty - encrypt: false # Whether to enable password encryption - status: 0 # Online status, please refer to https://docs.go-cqhttp.org/guide/config.html#在线状态 - relogin: # Reconnection settings - delay: 3 # First reconnection delay, in seconds - interval: 3 # Reconnection interval - max-times: 0 # Maximum reconnection times, 0 means unlimited -``` - -2. Modify the websocket port - -Find the following content in `config.yml` - -```yaml - - ws: - # Forward WS server listening address - address: 0.0.0.0:8080 - middlewares: - <<: *default # Reference default middleware -``` - -**Change `0.0.0.0:8080` to `0.0.0.0:6700`**, save and close `config.yml`. - -3. Configure the signature server - -Fill in as follows, - -```yaml - sign-servers: - - url: 'https://qsign.trpgbot.com/' # Main signature server address, required - key: 'selfshare' - authorization: '-' - - url: 'https://qsign-v3.trpgbot.com/' # Secondary server address, optional - key: 'selfshare' - authorization: '-' - - auto-register: true -``` - -Write to the file /data/versions/6.json, - -```json -{ - "apk_id": "com.tencent.mobileqq", - "app_id": 537220362, - "sub_app_id": 537220362, - "app_key": "0S200MNJT807V3GE", - "sort_version_name": "9.0.56.16830", - "build_time": 1713424357, - "apk_sign": "a6b745bf24a2c277527716f6f36eb68d", - "sdk_version": "6.0.0.2560", - "sso_version": 21, - "misc_bitmap": 150470524, - "main_sig_map": 34869472, - "sub_sig_map": 66560, - "dump_time": 1713424357, - "qua": "V1_AND_SQ_9.0.56_6372_YYB_D", - "protocol_type": 6 -} -``` - -Thanks to zhaodice for bringing the QQ 9.0.56 signature server. - -4. If your server is on the public network, it is strongly recommended to fill in the `access-token` (optional) - -```yaml -# Default middleware anchor -default-middlewares: &default - # Access token, strongly recommended for servers on the public network - access-token: '' -``` - -5. Configuration is complete, restart go-cqhttp. - -## Login - -Double-click the login script. After a while, you will see a prompt. - -![Login gocq](/assets/image/first_launch.png) - -Copy the link after `Captcha link: ` and open it in Chrome or Edge. Press F12 to open the developer tools. - -Open `Network`. - -![Pass captcha](/assets/image/get_captcha.png) - -Complete the verification in the browser. In `Network`, find the request named `cap_union_new_verify`, at the very bottom. - -![Get token](/assets/image/get_gocq_login_token.png) - -The content after `ticket` is what you need to input back into go-cqhttp, but do not include the double quotes at the beginning and end. - -**Note** - -- Remember to delete the quotes (`"`, there is one at the beginning and end) when copying, as they are not part of the ticket content. -- Be quick. -- Make sure not to miss any characters (the content is long, make sure you copy it all). - -Paste the copied content into the terminal running go-cqhttp. - -![Paste token](/assets/image/paste_token.png) - -> If the login is unsuccessful after starting, please try modifying the protocol number in `device.json` according to [this document](https://docs.go-cqhttp.org/guide/config.html#%E8%AE%BE%E5%A4%87%E4%BF%A1%E6%81%AF). - -## Next Steps - -Check the configuration information page and connect via the Nakuru adapter. \ No newline at end of file diff --git a/.backup/en/deploy/platforms/mirai.md b/.backup/en/deploy/platforms/mirai.md deleted file mode 100644 index 8ab804acd..000000000 --- a/.backup/en/deploy/platforms/mirai.md +++ /dev/null @@ -1,153 +0,0 @@ -# Deploying the Mirai Messaging Platform - -> This document provides only the basic steps. For detailed operations, troubleshooting, and the latest information, please visit the [Mirai Official Repository](https://github.com/mamoe/mirai) and the [Mirai Official Forum](https://mirai.mamoe.net/). - -::: warning - -This messaging platform is unstable. Do not use it. - -::: - -## Preparation - -> Note: Due to the diverse and complex network environments, many steps may require the use of network magic. Those who understand will know. - -1. Create a Folder - - Create a new folder on any drive (D, E, F, etc.), for example, name it `qqbot`. - -2. Create Subfolders - - Double-click to enter the newly created folder, and create two subfolders (it is recommended to keep the names consistent): `mirai` and `LangBot`. - -## Installing mirai-console - -Download the corresponding version of the installer from the [mcl-installer releases page](https://github.com/iTXTech/mcl-installer/releases). Find the latest installer, which is marked with "Latest," click `show all 14 assets`, and download `mcl-installer-X.X.X-windows-amd64.exe`. For example, download `mcl-installer-1.0.7-windows-amd64.exe` and save it in the previously created `mirai` folder. - -![Screenshot 2023-11-23 223909.png](/assets/image/mirai_dl_1.png) - -![image-20231218170548696](/assets/image/mirai_dl_2.png) - -Double-click to open it and press Enter all the way. Then, you will see some additional files. - -![image-20231218170726759](/assets/image/mirai_dl_3.png) - -Continue by clicking `mcl.cmd`. If you cannot see the file extension, refer to this article to display file extensions: [How to View File Extensions](https://jingyan.baidu.com/article/9158e0004c6cbea2541228da.html). - -After double-clicking, an interface similar to the one below will appear. When `Enter to continue` appears, press Enter. - -![Screenshot 2023-11-24 174015.png](/assets/image/mirai_dl_4.png) -![Screenshot 2023-11-24 174650.png](/assets/image/mirai_dl_5.png) -When the green text appears, the installation is complete. At this point, type `exit` and press Enter to quit. - -## Installing mirai-api-http - -Open `mcl.cmd`, enter `mcl --update-package net.mamoe:mirai-api-http --channel stable-v2 --type plugin`, and press Enter. - -![image-20231218172022449](/assets/image/mah_dl_1.png) - -Open `mcl.cmd` again, press Enter all the way until it exits. - -Open `mcl.cmd` once more, and if you see the following interface, it means the installation was successful. -![Screenshot 2023-11-24 184618.png](/assets/image/mah_dl_2.png) -Press `Ctrl+C` to exit. -Locate the `mira/config/net.mamoe.mirai-api-http/setting.yml` file, right-click, and open it with Notepad. - -![image-20231218172354159](/assets/image/mah_dl_3.png) - -Delete all the content and replace it with: - -```yml -adapters: - - ws -debug: true -enableVerify: true -verifyKey: yirimirai -singleMode: false -cacheSize: 4096 -adapterSettings: - ws: - host: localhost - port: 8080 - reservedSyncId: -1 -``` - -Open `mcl.cmd` again, and you should see the following output: -![image-20231218174112536](/assets/image/mah_dl_4.png) -This indicates that mirai-api-http has been successfully installed. - -## Configuring the Signature Service - -Download the qsign one-click signature package from this link. Click to download the latest zip file, such as `qsign-1.2.1-beta-dev-d62ddce-all.zip` [as of December 19, 2023]. [Click here to go to the download page](https://github.com/MrXiaoM/qsign/releases). - -![image-20231219211250617](/assets/image/mirai_sign_1.png) - -After downloading, extract the files to get the following: - -![image-20231219211634274](/assets/image/mirai_sign_2.png) - -Copy the two files into the `mirai` folder. - -![image-20231219211904375](/assets/image/mirai_sign_3.png) - -Open the `txlib` folder and note the folder with the largest number. - -![image-20231219212926059](/assets/image/mirai_sign_4.png) - -Then, double-click to run `mcl.cmd`. After a while, when you see the prompt `mirai-console started successfully`, qsign is installed. - -Next, open the `config.yml` file located in `mirai/config/top.mrxiaom.qsign` with Notepad. - -![image-20231219213026138](/assets/image/mirai_sign_5.png) - -![image-20231219213255993](/assets/image/mirai_sign_6.png) - -Then, press `Ctrl+S` to save. - -Double-click to run `mcl.cmd` again, and you should see a similar prompt, as shown below: - -![image-20231219214603122](/assets/image/mirai_sign_7.png) - -## Running Mirai - -At this point, you have successfully installed and configured everything. Next, you can log in with your bot's QQ account. - -Enter the following, replacing `123456` with your bot's QQ number and `67890` with your bot's QQ password: - -```bash -login 12345 67890 -``` - -Then, you will see the following prompt: - -![image-20231219215234389](/assets/image/run_mirai_1.png) - -Copy the link after `Captcha link:` and open it in Chrome or Edge. Press F12 to open the developer tools. - -Open the `Network` tab. - -![img5](/assets/image/get_captcha.png) - -Complete the verification in the browser. In the `Network` tab, find the request named `cap_union_new_verify` at the bottom. - -![img6](/assets/image/get_gocq_login_token.png) - -The content after `ticket` is what you need to input back into Mirai, but do not include the quotes at the beginning and end. - -**Note** - -- Remember to delete the quotes (`"`, one at the beginning and one at the end) when copying; they are not part of the ticket content. -- Be quick. -- Make sure not to miss any characters (the content is long, so ensure you copy everything). - -Paste the copied content into the terminal running Mirai. - -![image-20231219220645708](/assets/image/paste_token.png) - -Then press Enter. If you're lucky, you will be prompted to enter a verification code. Type `yes` and press Enter, then enter the verification code and press Enter to log in successfully. - -If you're unlucky and encounter error code 237, it is recommended to try with a different account. - -## Next Steps - -Check the configuration information page and connect via the YiriMirai adapter. \ No newline at end of file diff --git a/.backup/en/deploy/platforms/official.md b/.backup/en/deploy/platforms/official.md deleted file mode 100644 index 2b441dccf..000000000 --- a/.backup/en/deploy/platforms/official.md +++ /dev/null @@ -1,11 +0,0 @@ -# Using the Official QQ Bot API - -The official QQ bot is available for anyone to apply and can be used in channels, group chats, and private messages. - -## Registering a QQ Bot - -Go to the [QQ Open Platform](https://q.qq.com/#/), locate the QQ bot documentation at the bottom, and follow the instructions to register a QQ channel bot. - -## Obtaining Credentials - -After registration, go to the bot console, find the Development Settings, and obtain the `AppId (Bot ID)` and `AppSecret (Bot Key)`. Then proceed to the step of filling in the configuration information. \ No newline at end of file diff --git a/.backup/en/deploy/quick-config/config.md b/.backup/en/deploy/quick-config/config.md deleted file mode 100644 index ee1c14409..000000000 --- a/.backup/en/deploy/quick-config/config.md +++ /dev/null @@ -1,150 +0,0 @@ -# Filling in Configuration Information - -To enable the basic functionality of LangBot, modify the following configuration files. After reading this page, it is recommended to check the [Configuration Details](/config/function/platform.md) for more in-depth explanations of the configuration items. - -- `data/config/platform.json` -- `data/config/provider.json` -- `data/config/system.json` - -## platform.json - -Modify this configuration file to specify the messaging platforms you want to connect to. You only need to focus on the `platform-adapters` field. Currently supported platform adapters include `go-cqhttp`, `aiocqhttp`, and `qq-botpy`. You can enable multiple adapters of the same or different types simultaneously. Please fill in the details based on the messaging platforms you have deployed. - -::: info -If both the messaging platform and LangBot are running in Docker, please configure the adapter settings below first. For specific network configurations, refer to [Docker Network Configuration Details](/deploy/network-details). -::: - -### aiocqhttp Adapter - -Used to connect to NapCat/Lagrange/LLOneBot and other OneBot protocol-compatible bot ends (only supports reverse WebSocket). - -```json -{ - "adapter": "aiocqhttp", - "enable": true, - "host": "0.0.0.0", - "port": 2280, - "access-token": "" -}, -``` - -`"adapter": "aiocqhttp"`, no need to change. - -`enable`: Whether to enable it. Set it to `true` after configuration. - -`host`: The IP address to listen on. Generally, keep it as `0.0.0.0`. When using aiocqhttp, LangBot acts as a server passively waiting for the framework to connect. Please set the `Passive WebSocket Address` or `Reverse WebSocket Address` (depending on the framework) in NapCat/Lagrange/LLOneBot to the address LangBot is listening on, with the path as `/ws`, e.g., `ws://127.0.0.1:2280/ws`. - -`port`: The port to listen on. Default is 2280. Set the same port in NapCat/Lagrange/LLOneBot. LangBot started in a container exposes ports 2280-2290 by default. Please choose any one of these ports. - -`access-token`: Set the access token, which should match the one set in NapCat/Lagrange/LLOneBot. - -### qq-botpy Adapter - -Used to connect to the official QQ bot API. - -```json -{ - "adapter": "qq-botpy", - "enable": false, - "appid": "", - "secret": "", - "intents": [ - "public_guild_messages", - "direct_message", - "public_messages" - ] -} -``` - -`"adapter": "qq-botpy"`, no need to change. - -`enable`: Whether to enable it. Set it to `true` after configuration. - -`appid`: The appid of the official QQ bot you applied for. - -`secret`: The secret of the official QQ bot you applied for. - -`intents`: Controls the types of events to listen to. You need to fill this in to receive corresponding messages. Currently supported event types include: - -- `public_guild_messages` QQ channel messages -- `direct_message` QQ channel private messages -- `public_messages` QQ group and list private messages - -### Nakuru Adapter - -Used to connect to go-cqhttp. - -```json -{ - "adapter": "nakuru", - "enable": false, - "host": "127.0.0.1", - "ws_port": 8080, - "http_port": 5700, - "token": "" -}, -``` - -`"adapter": "nakuru"`, no need to change. - -`enable`: Whether to enable it. Set it to `true` after configuration. - -`host`: The host address running go-cqhttp. Generally, no need to change. - -`ws_port`: The WebSocket server listening address. Keep it consistent with the `config.yml` in `go-cqhttp`. - -`http_port`: The HTTP server listening address. Keep it consistent with the `config.yml` in `go-cqhttp`. - -`token`: The verification token. Keep it consistent with the `config.yml` in `go-cqhttp`. - -## provider.json - -Modify this configuration file to set the relevant information for the connected AI providers. Currently supported interfaces and corresponding models include `openai(GPT)`, `anthropic(claude)`, and `moonshot`. - -> If you don't have an OpenAI API Key, you can [get it here](https://api.qhaigc.net/) - -The following only explains how to enable the OpenAI interface. For specific setup methods, please refer to the [Function Configuration `provider.json` Page](/config/function/provider). - -Edit `data/provider.json`, and add your OpenAI API Key under `keys`->`openai`, for example: - -```json - "keys": { - "openai": [ - "sk-AAAAAAAAAAAABBBBBBBBBBBBCCCCCCCCCCCCCDDDDDDDDD" - ], - }, -``` - -If you are using a relay station or another platform compatible with OpenAI format, set `requester`->`openai-chat-completions`->`base-url` to your relay station address. For example: - -Remember to add `/v1` after the address. - -```json - "openai-chat-completions": { - "base-url": "https://api.example.com/v1", - "args": {}, - "timeout": 120 - }, -``` - -Finally, change the `model` to the one you want to use, for example: - -```json - "model": "gpt-4o", -``` - -## system.json - -In `data/system.json`, modify `admin-sessions` to add `person_your_QQ_number` to set your session with the bot as an administrator (only effective for person session private chats), for example: - -```json - "admin-sessions": [ - "person_1010553892" - ], -``` - -::: tip - -After reading and completing the above tutorial, restart LangBot according to your deployment method. You should be able to use it successfully. If not, please read again (you might have missed or made a mistake in some steps). If you still cannot use it after fully reading and thoroughly understanding this document, you can find a community group [here](/insight/community.html). - -::: \ No newline at end of file diff --git a/.backup/en/deploy/update.md b/.backup/en/deploy/update.md deleted file mode 100644 index 5e223d21d..000000000 --- a/.backup/en/deploy/update.md +++ /dev/null @@ -1,9 +0,0 @@ -# Updating LangBot - -## Using Docker (or One-Click Deployment Based on Docker) - -Simply pull the latest image. - -## Manual Deployment - -Download the latest release from the [Releases](https://github.com/RockChinQ/LangBot/releases) page, specifically the `langbot--all.zip` file under Assets. Extract it to the previously deployed directory and restart. \ No newline at end of file diff --git a/.backup/en/develop/comp-arch.md b/.backup/en/develop/comp-arch.md deleted file mode 100644 index 3f4da8bf6..000000000 --- a/.backup/en/develop/comp-arch.md +++ /dev/null @@ -1,3 +0,0 @@ -# Component Architecture - -![Component Architecture Diagram](/assets/image/develop_comp_arch_1.png) \ No newline at end of file diff --git a/.backup/en/index.md b/.backup/en/index.md deleted file mode 100644 index 4ca560b07..000000000 --- a/.backup/en/index.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -# https://vitepress.dev/reference/default-theme-home-page -layout: home - -hero: - name: "LangBot" - image: - src: /en/public/langbot-logo-block.png - alt: LangBot-logo - text: "" - tagline: "Originally named QChatGPT; 😎 Highly stable, 🧩 Plugin-supported, 🦄 Multimodal LLM QQ / QQ Channel / OneBot bot / Agent platform" - actions: - - theme: brand - text: View Documentation - link: /en/insight/guide - - theme: alt - text: Code Repository - link: https://github.com/RockChinQ/LangBot - -features: - - title: Rich Ecosystem - details: Supports plugin extensions such as event listeners and Agents, with dozens of plugins available - - - title: Intuitive Features - details: Integrates various commonly used features such as blacklist, rate limiting, response selection, context control, and Prompt management - - - title: Multiple Models - details: Compatible with multiple providers including OpenAI, Moonshot, Anthropic, and Gitee AI, with deep support for OneAPI - - - title: High Stability - details: Adopts mainstream design principles with a clear architecture and high test coverage - - - title: Quick Deployment - details: Supports multiple deployment methods, including one-click deployment via Baota, Docker deployment, and source code deployment - - - title: Continuous Maintenance - details: Long-term maintenance and iteration, continuously following the latest application practices in the field of large models - ---- \ No newline at end of file diff --git a/.backup/en/insight/community.md b/.backup/en/insight/community.md deleted file mode 100644 index eab8855f2..000000000 --- a/.backup/en/insight/community.md +++ /dev/null @@ -1,19 +0,0 @@ -# Community Resources - -## Video Tutorials - -[【Latest in August】Deploy the Most Powerful AI Voice Chat + Tavern Role-Playing + QQ Bot in 5 Minutes, New Role-Playing Voice Chat QQ Bot](https://www.bilibili.com/video/BV1vypZeBEW1/?share_source=copy_web&vd_source=1335a044d4fd926920999a0e3ba0a731) - -## QQ Groups for Project Communication - -### Community Group - -> Please read the documentation carefully before asking questions in the group. Join the group only if you cannot resolve the issue. - -Lazy's Communication Group: 619154800 ([Link](https://qm.qq.com/q/1K9GjQuza)) - -### Official Group - -> The official group does not provide deployment support. - -Group 4: 195992197 ([Link](https://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=cSekvWmyezfCE4O8gXS7lSjkmPinjzpP&authKey=G4jHfz2%2BtQawxCRhn1ZRrQiI8bTvlepQubZL6F9fymFuz8jqZZ4FkYh6lhKLMCd9&noverify=0&group_code=195992197)) \ No newline at end of file diff --git a/.backup/en/insight/guide.md b/.backup/en/insight/guide.md deleted file mode 100644 index bd9a3e288..000000000 --- a/.backup/en/insight/guide.md +++ /dev/null @@ -1,18 +0,0 @@ -# Reading Guide - -## Deployment Section - -To deploy this project, please strictly follow the three steps below. Each step can be completed using different methods. - - - Read the **Deploy LangBot** page and choose a deployment method. - - Read the **Deploy Messaging Platform** page and select the messaging platform you want to use. - - Read the **Fill in Configuration Information** page and fill in the configuration as needed. - -## Usage Section - - Read the **Command Syntax** to understand the available commands for the bot, which allow you to switch presets, change models, and more during conversations. - - Read the **Plugin List** to obtain plugins and enhance functionality. - - Read the **FAQ** to help resolve issues during deployment and usage. - -## Configuration Section - - **Feature Configuration**: Detailed explanations of all configuration options to meet more customized needs. - - **Metadata Configuration**: Control sensitive words, model lists, and more. \ No newline at end of file diff --git a/.backup/en/insight/intro.md b/.backup/en/insight/intro.md deleted file mode 100644 index d346b040f..000000000 --- a/.backup/en/insight/intro.md +++ /dev/null @@ -1,114 +0,0 @@ -# Model Overview and Features - -## Model Overview - -### Text Conversation - -- OpenAI API: GPT-3.5 / GPT-4 series -- Moonshot / Deepseek and other major model providers supporting OpenAI API -- Anthropic: Claude series -- [OneAPI](https://github.com/songquanpeng/one-api) interface aggregation platform: Google Gemini Pro / Azure / Anthropic Claude / Zhipu ChatGLM / Baidu ERNIE Bot / iFlytek Spark Cognitive / Alibaba Tongyi Qianwen / 360 ZhiNao and other official LLM interfaces on the market -- [free-one-api](https://github.com/RockChinQ/free-one-api) reverse engineering library aggregation platform: ChatGPT / Claude / Google Bard / gpt4free / Tongyi Qianwen - -## Feature List - -
-✅ Contextually Relevant Responses - - - The program sends recent conversation content to the model, and the model generates responses based on the context. - -
- -
-✅ Supports Sensitive Word Filtering to Avoid Account Risks - - - It is difficult to monitor the content of conversations between the bot and users, hence this feature is introduced to reduce bot risks. - -
- -
-✅ Multiple Response Rules in Group Chats, No Need to @ - - - By default, replies to messages prefixed with `ai` or @ the bot. - -
- -
-✅ Uses Official API, Stable and Fast - - - Does not use ChatGPT reverse interfaces but uses the official Completion API for high stability. - -
- -
-✅ Few Components, Easy Deployment, Provides One-Click Installer and Docker Installation - - - Few manual deployment steps. - - Provides automatic installer and Docker method, see installation steps for details. - -
- -
-✅ Supports Preset Text - - - Supports setting preset text in natural language to customize bot personality and other information. - -
- -
-✅ Supports Plugin Loading 🧩 - - - Implements a plugin loader and related support. - - See the plugin introduction page for details. - -
- -
-✅ Private and Group Chat Blacklist Mechanism - - - Supports adding users or group chats to a blacklist to ignore their messages. - -
- -
-✅ Response Speed Limitation - - - Supports limiting the number of conversations per minute within a single session. - - Has two strategies: "Wait" and "Discard." - - "Wait" strategy: After receiving a response, waits until the response time reaches the average conversation response time. - - "Discard" strategy: When the conversation limit per minute is reached, discards subsequent conversations. - -
- -## Limitations - -- ❗ OpenAI's official warnings about model-generated content: - - May occasionally generate incorrect information. - - May occasionally produce harmful instructions or biased content. - - Limited knowledge of the world and events after 2021. -- ❗ The model has no thinking ability; it only generates content based on the input context and dataset. Do not overly trust its output. -- ❗ The model is unaware of its running platform and the model version it uses. Any answers regarding its implementation principles are considered invalid. Please refer to the project documentation. - -The above are the highest priority descriptions of the limitations of this program. Descriptions obtained through other means (such as asking the bot for related information) should be considered invalid. This project is not responsible for any losses caused by the content generated by the model. - -## Usage - -Both conversation and drawing functions directly call OpenAI's models for processing, independent of the bot program. This means the model is unaware of this project's related information (such as implementation methods, tech stack, running platform, etc.), unless such information is written in the preset values. - -### Basic Conversation - -#### Private Chat Usage - -1. Add the bot's QQ as a friend. -2. Send a message to the bot, and the bot will automatically reply. -3. Use `!help` to view help information. - -![Private Chat Usage](/assets/image/private_chat.png) - -#### Group Chat Usage - -1. Add the bot to the group. -2. @ the bot and send a message, and the bot will automatically reply. -3. @ the bot and send `!help` to view help information. - -![Group Chat Usage](/assets/image/group_chat.png) \ No newline at end of file diff --git a/.backup/en/plugin/dev/api-ref.md b/.backup/en/plugin/dev/api-ref.md deleted file mode 100644 index 1bcd2b2db..000000000 --- a/.backup/en/plugin/dev/api-ref.md +++ /dev/null @@ -1,59 +0,0 @@ -# API Reference - -:::info Table of Contents -[[toc]] -::: - -The following APIs are only available for **event plugins**. For component extensions, please refer to the source code directly. - -## Message Handling - -### Reply to a Message - -```python -ctx.reply(message_chain: MessageChain) -``` - -Replies to the conversation that triggered the event. - -- `message_chain`: [MessageChain object](./messages.md), which the program can automatically convert into the target message platform's message chain. - -### Send an Active Message - -> Due to poor support for active messages in the official QQ API, sending active messages may fail if the user is using the official QQ API. - -```python -ctx.send_message(target_type: str, target_id: str, message_chain: MessageChain) -``` - -Sends an active message to the target. - -- `target_type`: The type of target, with possible values: `"person"`, `"group"`. -- `target_id`: The target ID (QQ number or group number). -- `message_chain`: [MessageChain object](./messages.md), which the program can automatically convert into the target message platform's message chain. - -## Event Handling - -### Prevent Default Event Behavior - -```python -ctx.prevent_default() -``` - -Prevents the default behavior of the event, stopping subsequent processing actions (e.g., fetching a reply from the interface when a private message is received, or fetching a reply from the interface when a group message is received). - -### Prevent Subsequent Plugin Execution - -```python -ctx.prevent_postorder() -``` - -Prevents the execution of subsequent plugins for this event. The execution order of plugins can be modified by adjusting priorities as described in the `Plugin Management` section of the `Plugin Introduction`. - -### Add a Return Value - -```python -ctx.add_return(name: str, value: Any) -``` - -Adds a return value. Return values for events are **optional**. For the return values supported by each event, please refer to the comments in `pkg.plugin.events` for each event. \ No newline at end of file diff --git a/.backup/en/plugin/dev/extension.md b/.backup/en/plugin/dev/extension.md deleted file mode 100644 index 101654a38..000000000 --- a/.backup/en/plugin/dev/extension.md +++ /dev/null @@ -1,104 +0,0 @@ -# Component Extension - -> Please read the [Plugin Introduction Page](/plugin/plugin-intro) first. - -:::info Table of Contents -[[toc]] -::: - -Plugins wrapped in subclasses of `BasePlugin` can only listen to predefined events, whereas component extensions can replace entire components, enabling more powerful functionality. - -- Component extensions do not need to be wrapped in a subclass of `BasePlugin` and can be placed in any module within the plugin directory. -- Extended components are not controlled by the plugin's on/off switch. -- Extended components are typically selected via options in the configuration file. - -## Extendable Components - -![Component Extension Diagram](/assets/image/develop_comp_arch_1.png) - -To extend a component, create a new class that inherits from the component's base class, then use the corresponding decorator on the class, and configure the component to use this implementation based on the usage method. - -Most components contain an `ap: pkg.core.app.Application` object, which can be used to interact with various parts of the entire program. -Most components support implementing the `async def initialize(self)` method for asynchronous initialization when the component is loaded. -The `ap` object includes manager objects for various configuration files, which can be used to access these configurations. - -### Command - -Add a new command. - -- Base Class: `pkg.command.operator.CommandOperator` -- Decorator: `pkg.command.operator.operator_class(name: str, help: str="", usage: str=None, alias: list[str]=[], privilege: int=1, parent_class: typing.Type[CommandOperator] = None)` -- Reference Implementation: `pkg.command.operators/` -- Usage: After successful loading, execute the command `!cmd` to view all commands and their help information. - -### Content Filter - -Add a new content processor that determines whether to continue processing before and after handling a message. - -- Base Class: `pkg.pipeline.cntfilter.filter.ContentFilter` -- Decorator: `pkg.pipeline.cntfilter.filter.filter_class(name: str)` -- Reference Implementation: `pkg.pipeline.cntfilter.filters/` -- Usage: After successful loading, the `process` method of the content filter will be called at the declared inspection stage for each filter implementation. - -### Long Message Handling Strategy - -Add a new long message handling strategy. Examples include components that convert text to merged forwarded messages or text to images. - -- Base Class: `pkg.pipeline.longtext.strategy.LongTextStrategy` -- Decorator: `pkg.pipeline.longtext.strategy.strategy_class(name: str)` -- Reference Implementation: `pkg.pipeline.longtext.strategies/` -- Usage: Set the `strategy` field in the `long-text-strategy` section of `data/config/platform.json` to the name of the newly added strategy. - -### Conversation Rate Limiting Algorithm - -Add a new conversation rate limiting algorithm. The rate limiting algorithm implementation class methods are called when a conversation request enters and exits, allowing the implementation class to control waiting or dropping requests. - -- Base Class: `pkg.pipeline.ratelimit.algo.RateLimitAlgo` -- Decorator: `pkg.pipeline.ratelimit.algo.algo_class(name: str)` -- Reference Implementation: `pkg.pipeline.ratelimit.algos/` -- Usage: Set the `algo` field in the `rate-limit` section of `data/config/pipeline.json` to the name of the newly added algorithm. - -### LLM Interface Requester - -Add a new LLM interface requester, such as integrating the ChatGLM interface. - -- Base Class: `pkg.provider.modelmgr.api.LLMAPIRequester` -- Decorator: `pkg.provider.modelmgr.api.api_requester_class(name: str)` -- Reference Implementation: `pkg.provider.modelmgr.apis/` -- Usage: Add a model in `data/metadata/llm-models.json` and set the `requester` field of the model to the name of the newly added requester. For details, refer to the metadata configuration section. - -### Prompt Loader - -Add a new Prompt loader. - -- Base Class: `pkg.provider.sysprompt.loader.PromptLoader` -- Decorator: `pkg.provider.sysprompt.loader.loader_class(name: str)` -- Reference Implementation: `pkg.provider.sysprompt.loaders/` -- Usage: Set the `prompt-mode` field in `data/config/provider.json` to the name of the newly added loader. - -### Message Platform Adapter - -Add a new message platform adapter, such as integrating WhatsApp or Discord. - -- Base Class: `pkg.platform.adapter.MessageSourceAdapter` -- Decorator: `pkg.platform.adapter.adapter_class(name: str)` -- Reference Implementation: `pkg.platform.adapters/` -- Usage: The configuration information in the `platform-adapters` section of `data/config/platform.json` will automatically look up the corresponding adapter implementation based on the `adapter` name during initialization and pass the configuration to the adapter for initialization. Message platform adapters are complex to implement, so it is recommended to refer to existing implementations. - -### Conversation History Message Truncator - -Add a new conversation history message truncator that truncates historical messages before sending them to the model. - -- Base Class: `pkg.pipeline.msgtrun.truncator.Truncator` -- Decorator: `pkg.pipeline.msgtrun.truncator.truncator_class(name: str)` -- Reference Implementation: `pkg.pipeline.msgtrun.truncators/` -- Usage: Set the `method` field in the `msg-truncate` section of `data/config/pipeline.json` to the name of the newly added truncator. - -### Chat Request Runner - -Add a new chat message runner. The default is local-agent, which provides basic Prompt composition, Agent orchestration, and multi-turn requests. If you need to integrate a knowledge base or local RAG, implement it here. - -- Base Class: `pkg.provider.runner.RequesterRunner` -- Decorator: `pkg.provider.runner.runner_class(name: str)` -- Reference Implementation: `pkg.provider.runners/` -- Usage: Set the `runner` field in the `runner` section of `data/config/provider.json` to the name of the newly added runner. \ No newline at end of file diff --git a/.backup/en/plugin/dev/messages.md b/.backup/en/plugin/dev/messages.md deleted file mode 100644 index 246a1d67b..000000000 --- a/.backup/en/plugin/dev/messages.md +++ /dev/null @@ -1,82 +0,0 @@ -# Message Platform Entities - -:::info Table of Contents -[[toc]] -::: - -LangBot supports multiple message platforms, but the format of `message entities` varies across different platforms. To abstract these differences, LangBot has a unified standard. Plugin developers only need to understand and use the message entities described on this page within their plugins. LangBot's internal message processing logic will automatically handle the parsing and conversion of messages. - -:::info -LangBot's message platform entities are based on a modified implementation of [YiriMirai](https://github.com/YiriMiraiProject/YiriMirai). -::: - -## Message Chain - -The `messages` on message platforms are different from the messages used for AI interactions. Messages on message platforms are described in the form of `message chains`. Each independent message displayed on QQ is a message chain. A message chain can contain various `message chain components` such as `text`, `images`, `@ components`, etc. For example: - -![](/assets/image/plugin_dev_messages_01.png) - -This is a message chain containing a Plain component ("Hello World") and an Image component ("Usagi"). - -The definitions of message chains and message chain components are located in `pkg/platform/types/messages.py`. - -### Constructing a Message Chain - -First, import the `pkg.platform.types` package to use the message components within it. - -```python -from pkg.platform.types import * - -# Construct a message containing the text "Hello LangBot" and an image (fetched from a URL) -msg_chain = MessageChain([ - Plain("Hello LangBot"), - Image(url='https://qchatgpt.rockchin.top/langbot-logo.png') -]) - -# Construct a message containing an @All component and the text "Hello LangBot" -msg_chain = MessageChain([ - AtAll(), - Plain("Hello LangBot") -]) - -# Construct a message containing an @ component for a specific member and the text "Hello LangBot" -msg_chain = MessageChain([ - At(123456), - Plain("Hello LangBot") -]) -``` - -Currently supported message chain components: - -- `Source`: Source message chain information. If the message is received from a message platform, this component will be included at the beginning of the message chain, recording the message information. -- `Plain`: Plain text message. -- `Quote`: Quoted message. -- `Image`: Image message. -- `AtAll`: @All members message. -- `At`: @Specific member message. -- `Voice`: Voice message. - - Requires checking platform support. -- `Forward`: Forwarded message. - - Not supported on many platforms, not recommended for use. -- `File`: File message. - -For specific usage, refer to the definitions in `pkg/platform/types/messages.py`. - -## Message Platform Events - -These events are different from LangBot plugin events and are part of an independent event system for message platforms. These events are the source events for LangBot plugin events and can be obtained from each `Query.message_event`. - -The definitions of message platform events are located in `pkg/platform/types/events.py`. - -## Other Entities - -In addition to the entities mentioned above, there are entities such as `Friend` and `Group`. The definitions of these entities are located in `pkg/platform/types/entities.py`. -They are generally included in the event objects mentioned above and can be used to retrieve information not included in plugin events. - -## Backward Compatibility - -Plugins developed based on version 3.x can seamlessly adapt to version 3.4. However, `import mirai` is no longer supported in the new version, and the editor will report errors when developing plugins. Please make the following changes: - -- Change `import mirai` to `import pkg.platform.types as mirai` - - It is not recommended to directly change it to `as mirai`. Instead, use `import pkg.platform.types as platform_types` and replace the corresponding `mirai.xxx` in the plugin code with `platform_types.xxx`. -- Change `from mirai import *` to `from pkg.platform.types import *` \ No newline at end of file diff --git a/.backup/en/plugin/dev/tutor.md b/.backup/en/plugin/dev/tutor.md deleted file mode 100644 index fbc071a11..000000000 --- a/.backup/en/plugin/dev/tutor.md +++ /dev/null @@ -1,225 +0,0 @@ -# Plugin Development Tutorial - -> Please read the [Plugin Introduction Page](/plugin/plugin-intro) first. -> It is recommended to read the project source code to understand the project architecture. - -> For issues and feature requests, please create an issue in the repository. -> **Please try to solve the problem on your own before asking questions.** - -:::info Table of Contents -[[toc]] -::: - -## 💬 Introduction - -This page explains the steps for developing a regular plugin. There are two methods inside a plugin: event listeners and content functions. -Event listeners can respond to certain events and modify behavior. Content functions can be called by large language models. For more details, please refer to the plugin introduction page. - -LangBot also supports component extensions, allowing you to replace entire components without being limited to listening to predefined events. Please read this page and then check the **Component Extension** page for more details. - -## 💻 Quick Start - -Deploy this project according to the documentation and ensure it runs properly. - -Use [hello_plugin](https://github.com/RockChinQ/hello_plugin) as a template to generate a plugin code repository, then clone the repository code into the `plugins` directory. - -Modify the necessary parts in the plugin's README.md file. - -Edit `main.py` and input the following content: - -```Python -from pkg.plugin.context import register, handler, llm_func, BasePlugin, APIHost, EventContext -from pkg.plugin.events import * # Import event classes - - -""" -When receiving a private or group chat message "hello", reply with "hello, !" or "hello, everyone!" -""" - - -# Register the plugin -@register(name="Hello", description="hello world", version="0.1", author="RockChinQ") -class HelloPlugin(BasePlugin): - - # Triggered when the plugin is loaded - def __init__(self, host: APIHost): - pass - - # Asynchronous initialization - async def initialize(self): - pass - - # Triggered when a private message is received - @handler(PersonNormalMessageReceived) - async def person_normal_message_received(self, ctx: EventContext): - msg = ctx.event.text_message # Here, event is the object of PersonNormalMessageReceived - if msg == "hello": # If the message is "hello" - - # Output debug information - self.ap.logger.debug("hello, {}".format(ctx.event.sender_id)) - - # Reply with "hello, !" - ctx.add_return("reply", ["hello, {}!".format(ctx.event.sender_id)]) - - # Prevent the default behavior of this event (fetching a reply from the API) - ctx.prevent_default() - - # Triggered when a group message is received - @handler(GroupNormalMessageReceived) - async def group_normal_message_received(self, ctx: EventContext): - msg = ctx.event.text_message # Here, event is the object of GroupNormalMessageReceived - if msg == "hello": # If the message is "hello" - - # Output debug information - self.ap.logger.debug("hello, {}".format(ctx.event.sender_id)) - - # Reply with "hello, everyone!" - ctx.add_return("reply", ["hello, everyone!"]) - - # Prevent the default behavior of this event (fetching a reply from the API) - ctx.prevent_default() - - # Triggered when the plugin is unloaded - def __del__(self): - pass - -``` - -This plugin will implement: when a private chat receives a `hello` message, it replies with `hello, !`, and when a group chat receives a `hello` message, it replies with `hello, everyone!`. - -### Interpreting This Plugin Program - -- `import pkg.plugin.context` imports `register (for registering plugin classes)`, `handler (for registering event listeners)`, `llm_func (for registering content functions)`, `BasePlugin (plugin base class)`, `APIHost (API host)`, `EventContext (event context class)`, etc. -- `import pkg.plugin.events` imports all supported event classes. -- `@register` marks the class `HelloPlugin` as a plugin class, declaring the plugin name as `Hello` along with the plugin description, version, and author. -- The class `HelloPlugin` inherits from `BasePlugin`. This class can be named arbitrarily; the plugin name is only related to the parameters in the `register` call. -- The `__init__` method of this class is optional, and the code inside it will be executed when the plugin is loaded during the main program startup. -- The plugin class also supports adding an asynchronous method `async def initialize(self)` for asynchronous initialization. -- `@handler` marks the method `person_normal_message_received` as an event listener, handling the `PersonNormalMessageReceived` event (triggered when a private message is received before fetching a reply from OpenAI). This method can be named arbitrarily; the bound event is only related to the parameters in `handler`. For more supported events, please refer to the `pkg.plugin.events` file or the `API` section below. -- Debug information is output through the `self.ap.logger` logger. The `ap` object in the plugin class's parent class is the context object of the entire program, through which all objects in the program can be accessed. -- The second parameter `ctx` obtained by the event listener method is the context of this event, where `event` is the event object of this event. Relevant parameters can be extracted from it. For specific parameters that can be obtained from events, please refer to the comments of each event class in the `pkg.plugin.events` file. -- Inside the event listener method, the `text_message` parameter is extracted from the parameters, and if it is `hello`, the return value `reply` is set to `["hello, {}!".format(ctx.event.sender_id)]`. Then, the `ctx.prevent_default()` method is called to prevent the default behavior of the original program. - - For the return values supported by each event, please refer to the comments of each event in `pkg.plugin.events`. -- A similar program is used to register the `GroupNormalMessageReceived` event to handle group messages. - -After saving the changes, restart the main program and send the `!plugin` command to the bot. If the `Hello` plugin appears, it means the plugin has been successfully loaded. - -## ❗ Guidelines (Important) - -- Each plugin should be in a separate directory for easier management. It is recommended to create a repository on GitHub to store a single plugin for easy access and updates. -- Plugin names should use `PascalCase`, such as `Hello`, `ExamplePlugin`, `ChineseCommands`, etc. -- Multiple Python program files can be stored in one directory to separate the various functions of the plugin, making it easier for developers to manage. However, it is not recommended to register multiple plugins in one directory. -- The dependencies required by the plugin should be specified in the `requirements.txt` file in the plugin directory. The program will automatically install the dependencies when fetching this plugin from the repository. - -## 🪝 Content Functions - -`Content functions` are implemented through [GPT's Function Calling capability](https://platform.openai.com/docs/guides/gpt/function-calling). These are functions embedded in conversations and automatically called by GPT. - -> Your plugin does not necessarily need to include content functions. Please read the content function page to understand this feature first. - -### Steps to Write Content Functions - -1️⃣ First, follow the steps above to write the basic structure of your plugin. Now, please delete (or you can keep them for simplicity) the class functions decorated with `@handler` in the plugin content above. - -
-Class Structure After Deletion - -```python - -# Register the plugin -@register(name="Hello", description="hello world", version="0.1", author="RockChinQ") -class HelloPlugin(Plugin): - - # Triggered when the plugin is loaded - def __init__(self, plugin_host: APIHost): - pass - - # Triggered when the plugin is unloaded - def __del__(self): - pass -``` - -
- -2️⃣ Now, add the following function to the position where the functions were deleted (as a class method). - -```Python - -# Function to add -@llm_func(name="access_the_web") # Set the function name -async def access_web(self, query, url: str): - """Call this function to search about the question before you answer any questions. - - Do not search through baidu.com at any time. - - If you need to search somthing, visit https://www.google.com/search?q=xxx. - - If user ask you to open a url (start with http:// or https://), visit it directly. - - Summary the plain content result by yourself, DO NOT directly output anything in the result you got. - - Args: - url(str): url to visit - - Returns: - str: plain text content of the web page - """ - import requests - from bs4 import BeautifulSoup - # You need to first use - # pip install beautifulsoup4 - # to install the dependencies - - r = requests.get( - url, - timeout=10, - headers={ - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183" - } - ) - soup = BeautifulSoup(r.text, 'html.parser') - - s = soup.get_text() - - # Remove extra blank lines or lines with only \t and spaces - s = re.sub(r'\n\s*\n', '\n', s) - - if len(s) >= 512: # Truncate the first 512 characters of the obtained webpage plain text content - return s[:512] - - return s - -``` - -#### Please Note: - -- The function's comments must be written strictly according to the required format. For the specific format, please refer to [this document](https://github.com/RockChinQ/CallingGPT/wiki/1.-Function-Format#function-format). -- Content functions and `event listeners decorated with @handler` can coexist in the same plugin and are simultaneously controlled by the plugin switch in `plugins/plugins.json`. -- Ensure that the model you are using supports function calling. - -3️⃣ Now your program has web access capabilities. Restart the program and ask the bot about online content or directly send an article link to request a summary. - -- This is just an example. For more efficient web access capabilities, please refer to [WebwlkrPlugin](https://github.com/RockChinQ/WebwlkrPlugin). - -## 📄 Terminology Explanation - -### Explanation - -The `ctx: EventContext` object obtained by event listener methods can be viewed in the `pkg.plugin.context` module. The `event` property of the `ctx` object is the event object of this event, from which relevant parameters can be extracted. For specific parameters that can be obtained from events, please refer to the comments of each event class in the `pkg.plugin.events` file. -Event return values are **optional** and can be submitted by calling `ctx.add_return(key: str, ret)`. - -### Events - -If an event is part of a request (user sending a message), its event object will contain a `query` object, which includes the context data of this request (the process of handling a user sending a message, where one request process may trigger multiple events). -For all supported events, please refer to the comments of each event class in the `pkg.plugin.events` file. -For `message chain components`, please refer to [Message Platform Entities](./messages). - -### BasePlugin Structure - -#### self.ap - -Accessing `self.ap` in plugin methods refers to the object of the `pkg.core.app.Application` class, which contains the context object of the entire program. All objects in the program can be accessed through this object. - -#### self.host - -Accessing `self.host` in plugin methods refers to the object of the `pkg.plugin.context.APIHost` class, which provides some APIs of the main program. For more details, please refer to its source code. - -### API Reference - -For the APIs that plugins can call, please refer to the [API Reference](./api-ref). \ No newline at end of file diff --git a/.backup/en/plugin/plugin-intro.md b/.backup/en/plugin/plugin-intro.md deleted file mode 100644 index 264ce78c4..000000000 --- a/.backup/en/plugin/plugin-intro.md +++ /dev/null @@ -1,117 +0,0 @@ -# Plugin Introduction - -:::info Table of Contents -[[toc]] -::: - -## Plugin Categories - -**Plugins** are extension modules of LangBot, which include two types of executable content: **event listeners** and **content functions**. Event listeners are driven by events during the main program's execution, while content functions are driven by content generated by GPT. Both driving methods are uniformly packaged within the plugin class. - -### Event Listeners - -Plugins listen to various events during the operation of LangBot. When an event occurs, LangBot calls the registered listener of the plugin with relevant event information. Plugins can register listeners to be triggered under specified conditions. - -### Content Functions - -Content functions differ from plugins triggered by events. During a conversation, when the dialogue content matches the description of a content function, GPT specifies the function to be called, and LangBot invokes it. This is implemented based on [GPT's Function Calling capability](https://platform.openai.com/docs/guides/gpt/function-calling), which is a function automatically called by GPT embedded in the conversation. - -> It's similar to the official ChatGPT plugins. - -For example, if we provide GPT with a function `access_the_web`, along with its detailed description and parameter descriptions, then when we engage in a conversation involving content like the following: - -``` -Q: Please search for QQ bot projects on GitHub. -Q: Please search for some good cloud service provider websites for me. -Q: Read and summarize this article: https://zhuanlan.zhihu.com/p/607570830 -Q: Check the weather in Qingyuan today. -``` - -GPT will respond with a function call request for `access_the_web`, and LangBot will call the content function provided by the plugin, returning the call result to GPT to generate a new response. -Of course, the function calling feature is not limited to web access; it can also implement functions such as image processing, scientific calculations, and itinerary planning. Theoretically, we can achieve the same functionality as `ChatGPT Plugins` through content functions. - -## Plugin Usage - -### Installation - -> This section only provides the basic installation method for plugins. For detailed instructions, please refer to the documentation of each plugin or consult its developer. - -#### Repository Cloning (Recommended) - -During runtime, use an administrator account to send a private message to the bot with `!plugin get ` to automatically fetch the source code and install the plugin. The program will automatically install the required libraries based on the `requirements.txt` file in the repository. - -For example, to install the `hello_plugin` plugin: -``` -!plugin get https://github.com/RockChinQ/hello_plugin -``` - -After installation, restart the program. - -#### Manual Installation - -Place the obtained plugin program in the `plugins` directory. For specific usage, please refer to the documentation of each plugin or consult its developer. - -### Management - -#### !plugin Commands - -``` -!plugin List all installed plugins -!plugin get Install a plugin from a GitHub repository address (requires administrator privileges) -!plugin update all Update all plugins (requires administrator privileges, only supports plugins installed from repositories) -!plugin update Update a specific plugin -!plugin del Delete a plugin (requires administrator privileges) -!plugin on Enable a plugin (requires administrator privileges) -!plugin off Disable a plugin (requires administrator privileges) - -!func List all content functions -``` - -#### plugins/plugins.json Configuration File - -When the program starts, it synchronizes the settings here with the actual loaded plugin settings. - -**This file is automatically generated by the program. Do not manually add or delete items; only modify the priority and enabled status.** After modification, a restart is required. - -Example: - -```json -"plugins": [ - { - "name": "Nahida", - "description": "Hello Nahida", - "version": "0.1", - "author": "RockChinQ", - "source": "", - "main_file": "plugins/Nahida/main.py", - "pkg_path": "plugins/Nahida/", - "priority": 0, - "enabled": true - } -] -``` - -`source`: Source code address. If empty, it means this plugin does not support automatic updates by the program. - -`priority`: Plugin execution priority. During initialization and event calls, higher-priority plugins will be processed first. - -`enabled`: When set to false, the plugin will not be initialized or called. - -## Plugin List - -Below is a partial list of plugins. For all plugins, please check [RockChinQ's Star Collection](https://github.com/stars/RockChinQ/lists/qchatgpt-%E6%8F%92%E4%BB%B6) - -- [WebwlkrPlugin](https://github.com/RockChinQ/WebwlkrPlugin) - Allows the bot to access the internet!! -- [https://github.com/the-lazy-me/NewChatVoice](https://github.com/the-lazy-me/miHoYoVoice) - LangBot voice plugin, enabling voice chat using multiple TTS platforms -- [Yuumi0221/URLMaskerPlugin](https://github.com/Yuumi0221/URLMaskerPlugin) - Used to mask (replace) URLs to comply with QQ official bot API restrictions on URLs in messages -- [ElvisChenML/Waifu](https://github.com/ElvisChenML/Waifu) - A LangBot plugin aimed at using program logic combined with LLM to create realistic chatbots, exploring authentic chat experiences. -- [Pevernow/LangBot_Plugin_Emoticon_v3](https://github.com/Pevernow/QChatGPT_Plugin_Emoticon_v3) - LangBot v3 compatible, an emoticon plugin based on online APIs. -- [https://github.com/kirifujinagisa/RussianRoulette](https://github.com/kirifujinagisa/RussianRoulette) - A LangBot Russian Roulette game plugin -- [https://github.com/kirifujinagisa/FileSender](https://github.com/kirifujinagisa/FileSender) - A LangBot plugin for sending specified local files to applicants -- [https://github.com/ElvisChenML/GalgameMaker](https://github.com/ElvisChenML/GalgameMaker) - A LangBot plugin for quickly generating Galgames conducted in QQ conversations. -- [https://github.com/zzseki/QChatGPT_Plugin_News](https://github.com/zzseki/QChatGPT_Plugin_News) - No description (guess from the repository name) -- [https://github.com/zzseki/QChatGPT_Plugin_Weather ](https://github.com/zzseki/QChatGPT_Plugin_Weather)- No description (guess from the repository name) -- [https://github.com/zzseki/QChatGPT_Plugin_Music](https://github.com/zzseki/QChatGPT_Plugin_Music) - No description (guess from the repository name) -- [https://github.com/inkOrCloud/Markdown2IMG](https://github.com/inkOrCloud/Markdown2IMG) - LangBot plugin, converting markdown text into images for output -- [https://github.com/Annalasu/QcPlugin_Md2QQImage](https://github.com/Annalasu/QcPlugin_Md2QQImage) - Displays md image links in messages as normal images -- [https://github.com/the-lazy-me/SillyTavernPlugin](https://github.com/the-lazy-me/SillyTavernPlugin) - LangBot plugin, capable of transcribing SillyTavern character cards to adapt to LangBot's personality presets \ No newline at end of file diff --git a/.backup/en/plugin/tech-info.md b/.backup/en/plugin/tech-info.md deleted file mode 100644 index 22de5f51e..000000000 --- a/.backup/en/plugin/tech-info.md +++ /dev/null @@ -1,110 +0,0 @@ -# Technical Information - -Below is the technical information about the implementation principles of LangBot. Please read carefully before contributing. - -> This document is the technical documentation for LangBot 2.x and is outdated. For version 3.x, please refer directly to the source code. -> Please read the OpenAI API documentation at https://beta.openai.com/docs/ first. The following information assumes that you are familiar with the relevant features of OpenAI models and their interface invocation methods. - -## Terminology - -Includes the terminology involved in the OpenAI API and the naming of concepts in the project. -The terms in parentheses are the corresponding terms in the program, while those without parentheses are abstract concepts. - -### Model - -AI model. The content generated by calling OpenAI's interface in the program is all generated by OpenAI's models. - -### Tokens - -Tokens as defined by OpenAI. ASCII characters are 1 token, while others are 2 tokens. - -### Prompt - -i. The prompt used when calling OpenAI's text completion model. The model interface will return a response based on the prompt. The program will encapsulate the conversation content to generate the prompt. The prompts for calling the text completion model are marked with `user_name` (default is `You`, can be modified in the configuration file) and `bot_name` (default is `Bot`, can be modified in the configuration file) to identify the conversation roles for the model. Here is an example: - -``` -You: The weather is nice today. -Bot: I'm glad you like today's weather :) -You: Thank you. -Bot: You're welcome :) -``` -For the program implementation of the completion model, please refer to the `Implementation` section below. - -ii. The prompt used when calling OpenAI's drawing model. The model will draw based on the prompt and return the image URL. - -### Object - -The program treats a single person or a single QQ group as an object. The object and the model are the two parties in a conversation during a session. - -### Session - -Sessions are only valid for the text completion function; the drawing function does not have the concept of a session. Each object uses the same session, and there are only two roles in a session: the object and the model. Therefore, all people in the group will be treated as the same role to converse with the model. - -The essence of the program obtaining a reply is `text completion`. -Since the conversation needs to maintain context, the program will send the conversation history between the model and the object as a `prompt` to OpenAI's interface to obtain a reply that fits the previous context. -However, the prompt for OpenAI's text completion interface has a length limit (the default `text-davinci-003` has a limit of 4096 tokens), -so the concept of `session` is added to manage the prompt content sent to the interface. - -The session's lifespan can be set in `config.py`, with a default of 20 minutes. After the session expires, it will be stored in the database and reset. The next time the object initiates a conversation, a new session will be started. - -### Default Prompt, Personality (default_prompt) - -The preset conversation information for each session can be set in `config.py`. The program will write the following content to the prompt when each session is created: - -``` -You: -Bot: Okay -``` - -## Implementation - -### QQ Bot - -> Program Path: -> pkg.qqbot - -- `pkg.qqbot.manager` contains `QQBotManager`, which implements functions such as receiving messages, calling the OpenAI module to process messages, reporting to the audit module to record usage, and provides methods for notifying administrators and sending messages for other modules to call. -- `pkg.qqbot.filter` provides operations related to sensitive word filtering. -- `pkg.qqbot.process` provides unified processing logic for private messages and group messages. - -Mirai and YiriMirai are used as the framework for Python to interact with QQ. For details, please refer to their documentation. -At startup, YiriMirai's function is called to create a bot object, which is used by the program to interact with QQ through Mirai. The upper-level program calls this bot object's methods to process messages. -Since YiriMirai currently cannot shut down the bot, the same bot object is maintained before and after hot reloading, meaning that the QQ bot's related configuration (QQ number, adapter, etc.) information does not support hot reloading. - -### Database - -> Program Path: -> pkg.database - -- `pkg.database.manager` contains `DatabaseManager`, which encapsulates many methods for calling the database for other modules to use. - -SQLite is used as the database, storing all historical session information of objects, api-key cost information, and api-key usage information. - -### OpenAI Interaction - -> Program Path: -> pkg.openai - -- `pkg.openai.manager` contains the `OpenAIInteract` class, which encapsulates OpenAI's text completion `Completion` API and drawing API for the bot module to call. After a successful interface call, it reports the usage information of the current api-key to the audit module. -- `pkg.openai.keymgr` implements multi-api-key management, where the `exceeded` variable records api-key overage errors at runtime and provides api-key switching functionality based on overage records. -- `pkg.openai.pricing` records the cost information of each model for estimating costs when calling the interface. The cost estimation function is no longer tied to api-key switching; api-keys are only switched when the interface call reports an overage error. -- `pkg.openai.session` contains `Session` for session management. - -### Utils Module - -#### Context Module - -Saves the objects from the aforementioned modules and allows each module to retrieve objects from here to call their methods. - -#### Hot Reload Function - -> pkg.utils.reloader - -Before reloading, save all objects in the context, execute the program shutdown process in `main.py`, use the `reload` function of `importlib` to reload all modules (including the configuration file and any new modules), restore the context after reloading, and execute the program startup process. -All modules will recreate their objects, but the bot object in the QQ bot module will not be recreated. This is because the shutdown method provided by YiriMirai cannot be used, meaning that the QQ bot configuration in `config.py` does not support hot reloading. - -#### Hot Update Function - -> pkg.utils.updater - -Use the `dulwich` library to perform a pull operation to fetch the latest source code from the remote repository and perform a hot reload to load the latest code. \ No newline at end of file diff --git a/.backup/en/public/QChatGPT-0516.png b/.backup/en/public/QChatGPT-0516.png deleted file mode 100644 index 644136ba435b4765242e02933548a9b96baf4e53..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 222242 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%0AOnL4SfK#G(1LAS8muk@ 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
A&4yBMpR1-5rIIqL9Alj7jJB#G127cwyE;W;wkg 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

EDSZmQi+#x{2%l0aJW2De;-x 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

R3_1)71HRn5CcS%iAOP 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 k-{A5&A|X#taLsm0j{y(BtjcbXVd${ilaF9 zWz6wd2i<7taia6Xi%0qP;@I^n7Qf9sw8R7TLrIwSJD`>APpkRX$F-mc1Ie%uGIHVf zLt_Lgu^RAg!fXrGM=`hUH_+hMFR7@&&4KYe)2#k%4OCj5$s1?ruOK)v6O7&ZASKbs6;hL z`O^k}xO*!rd(>L=X#$~zq=3TS?rpw(&^8>_)@gB+a4u=~JLBXnK|rzu5PL~gbICI_$wzC%Pp0{K&&8&GMRe!7R` z9Wav;#HA&_0NY6YlyM%M$jgIU{9u+V*KKP7rZ8sl>|3i%sbERy@jVATXhM8?8(K;= z0=Lti^&rE$VOfX4e`CZ|9+I7Uk?(?%b6v}-M9gq8duL8Kh0#yO9iMobyCB1b`RJW}vnW{p| zl;ngT4z`qZySnmN@_dj|iQ74Ju--bp&jmsUG=tj^FAZimfLpqEj-XRO__8BY&0}JXn3@bSAIRPZtZx3a78Kp|q?tRluD~t5C)CED+ag=FMzVfCDYXA7t=q>aJ)c8`Fpv{0JQ|Rs(bDK2KMzg{%4U=a< z5^|svfq0$I;Il;VVWfWsK_eH6G6h~#82ST%LZw6=e^q}%bHi=JCLi_VGSrU@fRpj( z##16=eFKdh6<6=XS>i_Lx3BFNsKnKY@)&2oa$*HPL;2ab-bV6~FY zo4~}Qr|ms{?O*cxQ}Bx4aA4*8Drh4J$ORS)QHXiPlfiU=I< z%1jKl`m@F(vFz~{Jk`RPBp$Dl)SKH907FF69LyvTE{h-UVnan?t9nO{72$?P3S6(j z=Ya(`>#fHKv}0s7Cc1RC${zPt%}J3CZ@59pq~l*r695(Jo1dFiA|5M>09CE7u5wa6@$`xV< zC@xO@!p2sb@{PdwPhZp`92wxu$66g9ZjPcYlsP%Hu(aX_vwe%_W=OZ~YTcNc3^^Mm zC2YAPo9=^Or7$Rn)d>q`u}06zFF#ea=Jfkog9|g^5a&4z&|K{BMBTjE4F+_pEo*p( zWtK8`PyRKDIYhCuh-Mj>plJ%=zO#AbUU*shRo z2@7#p-DPPoXFPd!@AZDi;x1M#0SN~)PV`TkJp<{M?LF@|1|-uvn{F+^C4u?rF|6Z5 zO5shYh!R%iQrFBJas>k(gXqHU>5T|X{#Ga~yNIg{q7xv7@q#SH;V_2rvGVk7jzI?; zEH_bKWN#4MapiF_0PlY#bac3fzpwLj*w zBu^QCcl-})#fvy`CR500Wigz~<7d9O z@^PmNhlwWl>%(k;f<*Et^U>e@mZj-UHz|p7RfSpd(5m>JkK3SYl$21pXrN?O(xAtU ze|WW&Roq7BX1{<=*-Lel3M*~9wE%OkrL2Y+2Z%F58FW=sdFlKYMO$AC41ju1%~(8Z z-k4#HMk1WRl!y}~l`OLV=Z#+ZkAvv!iVDTnIhq*Py!y(Yohv>}qyzyMsmkO8pDFiE zUv;lcpzwT5s%3yDMQXU7704I{rwusw$FXu+npxG(kYwHrEX{KDx#uPJTH8#f^{b?V znowX)x;zm^5l!B`e2_D-LGUsVnr20lft`zBRD$%~@ZM!kCdFTW?BI;Ph69BGwDCb- zfCkn9@3m?@;?@y@8WnjfcJa1YCsY)7Z@9cURAMZ>%cZU@n7E9@{>3I* ztSbeD1qK!E1Q|ZW52F=k<+Y$k@gF#`*lI`o1L63EtCH8%afL5jA=Rd2o0JyBpH;fe zs1PH=$HW|6f8%l*nQGz#nfgD97Od78#S1!^&DAC7nBrr4%;YX?O zpBFx`M-W`3DRJ(yXIrFBeG~*{NiXgcK84}>X&qK_C9>X7X%}%4D~IAkeKk^Ag%GcM zmFX_l^@P%@(ex}znt81*`RZBK5aE9RepM@+$%q2uSVd{x2N&oFQ1_ZSORA+M3fkSF zks{BK2*N;JTx~U*Wz8Lu6er9PuCE_M5!?TRW}t|ju{7h)VS&Q5sUncA_ z8-Wg4X{#bmi4+4(WgfQgOKa}gkEh&(`YGYv%Up?>)|@Gii&>+`t&8(UP&7)KX>-9M zkM6(V2kB9>!`LLI|Kmp#5$B+MXLVG+z@Dr>u9PB~yFpfi+=jeP66M(F?sZBz5sZ7G zWJEv+Raj~8xdM5-b={xatds9I%o9;%D0p=NqPxe-5oV`a`aGdo$wOMlf>nc=Jd!lF zpW3aIPgY~R`fd{5Ufh~ihsvFGlL<;C27XoPVYdF7Pi}-nA4?_k>i1NC?#mqTUssnJ zv!G3gGS{5L%Ac%45y&n($6300|HIPi?7Cg>H8QZJ$UxK;M}w7?sNioADrc;uFgMo; zmhn9CMo}^@tpZ7ID%I#0@}rE(W_jK5kE#HzN*qZY{ShyUb$(lT+Ccc8Z*P2kyp`lx ztd#Y;E{eWB{#r7g+j?SDb_^Rpe)t=t0WB%j;7Vre^2urNF5x z#RX!UGu#v4o3(@75nIjCT~-Z%o1uXV@Ct~_1(uej=I$=uf;?=VidfPR;QD#vxotv{a?CU9rc^$RcxD@+C zsU|wFx$-Q}ZjUi2B}wcKntHfEvc+u7G+mf2%pU9ZIYgS%+AIvj#F ze3GwaotsA^Gp-7ghYT+p_@x!f7S7Yw9thmHJlLJ?QB?B%=CfuVV*5)I^(D_Y?P;ew zd6Fgc+6-C?>*mjP+Mk~a6&6+>m)W!`+yL1h#d!U-uC)Ves)a1{{RUotP591Ivi7tX zODWHuRM0?GMRB@@C{U7>JlAta+!{>|Q+?lU6my3C{yC9xtL-ll*xJB>TYP?Oj`Ix~ zS3m*Kb4t9-zY^MKoeza4M15#!Ys&(cZ`9%@l^5^yR;)B#w=DfC(u@Of0FRhfawYkP zV?>WTdSofGqMIT1UEBE`lRcirUFsL-bF3$eOHMO&5+wfGJ_?P|LHucLQxyNc=KWrn z-3hASQ>boetEgDW;CDFLQMkfsuMpH`*m7oA|HPi{-37ZC?b1N1#*Tk|AhGQ1p4u1(*r3AALQ+9UR z>ksV}AG!-dYdM!u!o>YvMMVMBo?y>EOxiN-`Mr*uXtVwQavF(x>Zq2n3UWdF7&;qj z8P924D>G%F2MVtdh;pxxOOz-NIxq$M8B}T)juAZ0{{f#?m!iS){KshRbKe0?L=JGVCG-S zblmGUqrNd))HcI^8Dz`r*llbNj`05dwcl7w$z`3jwtsF&lh8n-B~vn%+k2##UD@ti z5j~-iHrG=%9h62^7gC6Ccq3i(ogIF+hMriPGfzXwTZj)8<|F9nqun`WE!1!>!-bbztpLsr7dx-xvEtfvJf21;%0or!Qu$2m4fqI544zS;!NCxA8ihKjQWES58B1;3VI%V5!YGv2X=pY^LSoS z(r9}h%Q#tgzfZq*ueoq`JcC8D{|Y1eWK=X=LP={QZ*O9uW|pR(z_7T4=aI@y;p3<} zw^hF4fKG$R(9)xCpU>XXuPD4L@|zRG?{v)VCfR(?^7DAHDzVeQFsM8{Wy>nz=+sK< z%R&v+6~{(8rJ3(mx}h3-@eK=0S3;?>>Z8XUgx`enuW{D-J-GdSGtT_L=Qcx|39@#0 z%HE7pl(8qEWiwT#3Bxdc%At1%WDLy7nOG-K=m1iE#fw&o5`b0iN5>@KoPoTrQRs1l zBJ;VWs_pxvq2ogq>u~0pa+s_RMij>Vug?=f+aM>vYuc=QOAU;><4^|vWwZX9#km*1 zICydqo7V<4>NooL+nq2T(etc-*3gT#7aca-+HqvL5-Wd0X^4gMy7eECnDO~##qaMA ze9||E&#;r6?jIYm#plp2V$-)6!ZkUfonEm$spydk)n9s(ZfB-BHaQoQ^D{4Ka>LH( z_Vn@ktaLD4{%3n}i`bQ!*dg&#vE(2Aae-%~V#2?7@UvG_I(8eFDK%z!U)tnbN<6JI zwZMI-_Sc^$KrS8Y4|WbZta~;>XXWv6Tdl!aEI_BhU2QqvvB0Jg&l0=(d#q@`zrm!q zq6Ciiu6=B;Y8Dm)}aMoo|a&pV%7S zcxvYDqUS4`;8{0(Y|w9=rt-%g=VyDqJ5Qd-{zyaP%#Ol_ko7l$O)>2cD=C$A^(OeU z!Y!Mv&X+!sHN+3M)Q1z=rdfzj)8QM~oY336SDO_LwAYWs*Hc&v@s8L^r8io}#B#q}+-^F2~q>A{pQW(8(v!0|DGNi z3z`_(`*Wagywa)r7TU2R_8y+BDA|ejBc{$p0;x1+=9GwPZv;-74Dft;}M4mJ1=$J1s&VnpOI8DPvds_$*JEv z`a6JNmrbPn8gy#T43@UVvr-qN1~qP=oLexcxjbFxWuO%E2-wprG&H63Q*Ky>UL|K{ zjr|(Um?3O*vo)P}b(SKAxhq-;-V^)E9nbz1)=m(rcaG85QD4IcRgyEjG8uAfv*CBSJsZh*$*?H@J7G&vi2IHMA|caZqQ;)j!JqyV?0BBIoa9t?W!SO4Yly-(h8v4$C}Qp2|HiqhtP z{AHx|@_BaRWk{j)LX%68N|sHGc$kjAnDSpXF|sB zS;?8Gi67VNk;zqe|FNH!mwD#4e-=RVP`%;~oZ=s1d!Q~am!RJlEsERd6~Vy;qXFm8 zqN$)!H+Dn6!@_?KG1x~!zr+7lJc(oslg)#b@OMklPI6(?gPMZL@B5FB8fUX*Je#|H zDCL;QqjSbKv?co3S$)S2U$Xmrb4rbsxgCK2mC*iK4)zlrO8t0fy4Xw9QsRPLu-hDt zV86Y6%XV^wbW;Da6I`tJ%2X7N3JCa`<7nDO=k-x0TB-D_&R-Tr(wN}z9OmP1@WJ5|}$#@=Y!*t~dFXjFnG2c;ZH zSfsM?k{^P`7GVH}%f1T`v(QquwX-EuAW53oaD$O5V~dOcfI(95J$iiW{Hs{WN_$@O zV8s9sr1{yi)F^pCiD}h1Fsw{O7oRmh`*B%x$+>yPTnQ8Q_G@(SX2BaqhG?Bg?taAt zJmBg1sPiF(=U`y?I44e)u&6&AlZyL!BaqBh z;yJ}a#0+`n5Z|U#1l@5k8AugPBxJk_6&h(QgwL=#^bYNEyB3O1TZ zNL!GCNfEZ(iKMt>`tcUxpD6xDOTf#7s6NSQ~0v)v^ysivJqF`)O z7--;~6$2h&ZXG|n19m1Gv@o3-rHGoE18=pS7Q1l!7Qw|4J!1V7Xv*0C(iPo(w9!Ao zunT12u(hBfM$sq>CUZb;wsL(2bC}@|*G(0MU>4ITJQjoK6erfq*(zY2Gv^;cGlxF* zTG7}G3<{C}0JSTwD3fEcfy9ZYi!(Q5l32lHwzBOu)a4tS69T7mx+ILuda5)^MQbfz77w?;AFb|sQOMN2Q3MR%v0818im>d zQKTen8-hYrcmD8DMFJ~{`q&Rs706z#22~!&6UU&)BjsVRF!)yBLcW>X2C)}LeAL8)LvJSuwo~5Qv0kq9z6O{s%)QAZ!3Rez5Lw_#sF@%ASa&r# zjYx@-5DeJAMcoF6t}!KhO+ijed>SNuYJC1E@9VJpDn}sO&;J5+kMnH?Ebh>5>Ku93 z*r!)!Q4K}Px-2RppeZk;LRXuJ!S%WT7Z$z?q$OZK3F(tHcHni^?|Itr?dW9h&-3Z+ zLwTRd+<-Nw;NS>M&PM_0N|$v<%o)XEB>58VeRbr)&OiWIm_%ROwPSBE8v7&HMVTZsFuysx?%h|mAV|{;t;(=tRyUUwq z(O>n`0{L*1I)d4P@-+Umz-PQ)(arT~bTH3STMHkt)@BQ{-4s3xUVip1`wEO^aP&J{ z=Qz~W0XsKB%IO+AvjK&KC}42?yKNU6<{S{Y^6C7W_$FEoQUa%*v?os8tFN?-@*rO% zgQsb8bA7V@CX0Mbe!fLxsh~}bE3QU9$X_7W|7`l_0c=-0a1&siaoZY?uN@MNOho9? zH>NO761ZIwZZH<5WZB3PC{92v z)()k2100K_|6W~Pg%Z+PrOmu)Ip*%i!G3edJPE@D=Z}y^%fjL0N1XwzSiGS@PQnAA z`G5ums9y<=D*96az%MhkR@JUY4yC1KeAiXcb(7IO%!)LZ5hxPSVT5^r$&isi;#L~H zRi{h?4giudK~I%sF6}Xc6hf73{wz8``3(*4L(;?P@!mX)T_vU!4ui=5Kt*(*ysbV7 z3&F+J6RxJ!?}pTHsAPD)i{%|eFS4ge@-i?iF!k~L61+d|0~8{UakRIUxTz4cRUIcl zEuhjZ?8s1dQ;r#!>|k_18_xroQvT1I$GGZ>207u&PJ&@nqY5jzh`J-D3djk7K?>Ef zKIKRS0Z)r99~5tmDbF9tXHCDTu4Oa`pG(2nnLpeA5Q$-uiw~E}3kz3t;v%bJj3AHT zF6&Auw!QYSkXN1?TFlP3At4!JJXe0d9NKZr~-L3N3(2 z2d2%P7w4e+dbBLLy%Hlqa3>k)>3|BieU1eH6%v2b(z>JlTjRBQvXNBil9GyCfKD@? zeNT(JYeNe?284M=WMm1G-%lbUA`yiTd_OrLF3-*yfM)1-@Az8~XexfkdkUbW)^J1h z0Dj5FUL+O}l0B+{`(a`eOlC#8tg_4j%HLS0pu;YwK?#W&Opr3u_Vi1Hnxi7=$LjIp zp9^isyqG=6X9EnJLE~XFLzZzYbAK0wuliJ?lVo7VF!5h(Kj^$SZ4SLn6*tGLudfF~ z_9ehWpaY%?to{S>14=-A^M*7+b@GVlmTFYcH20&cpnYvtt=($ICEFtTOil#cxNJq4 zE<`)G{25x+V#~obtRV^XorPR9*Yq1CcM-}Ga}DN^uKkG8Lv;;G=|w)!$tEc-!t#rW zCrHTG=qK7pWk0gBH6h$MhJ<{pe-)@(Ydvje$&eDZKN8B|+|pj?~Qv+DqA9MI5Z)XIDo+~{;974 zbY}7l4mu(KXIAh9EJkW#W_IbJ-eqU_Ko~}P;YcLJ55lVdH7z&H{jnW10G%IGOeBjl zeg?5CNSF!Nr~VRG;qg7S-*=kXaW!Pg<=lC?mpQE@Qm7kKpjzm)q||4piDY8yVWN)> z3RL#$-#F0ORM!st=jeZe53aCpa2{l*icUyQ9)u?30FuZ8c%`(juNXvLKr)TX;KUTT zho-B2?>dX9ff4>03{;tcG-LRmoz`gd@LEIHXv1&r=ziFA_&v7vPWQ9Vb37NspU(Wc z4Z;rQJm73Vbd%fr_m6huS%-MozBfAu!x!4j*&Pa-9cJ$T9(bdKR`}90zYAgld+f8ww~O9GCTDI8_)A7w?j zoYJzg=Eaa3$f7y?!wu$2w;`*kyL&x|D?gjp=)tIy25Ywg(NlVomvOS;kO~;(_b+zw z_j-}*gvpsSI4+*N7Kyxc>m8|5!jI`(Y`nbJdfZ>FWqOvKVe?1gZGbWP1}GjNEsIC^ z&~b+clpcr@JQ7+(Rd+$N+U;^E^VX-TZXxa|AdY5gZ+(Ml4mm&_Mc!%&OTMY&G?nG~ zSSXDEC#ZB?UOO3WCrGoToC)YC0$DrvTz`@?V*6wGy%-pWAefo&D7qQ+AKv%JX1r}% z0^lyFwkB30hsai0bN>0C-lCrC9rJg-BdK(Vc?2?AAXd;{;uitrAdBnI%$#nToHI+@ zVyOC@;eGJ{mzM=~5kQCnz<|IZ@ft2b5_EKsxWf#`A#=!2Lku1OSe=S9#7gYpK-gY> z@tZJzu~R+>AhJdqAcNbHggr)y@0$>+!wiC*!U6+~i{SP{EbPaR z16>vZ2A%C7 z?tWo95;uc~ozN+z2o6=@qg>k3e*&>C#igZ)o%8z({IeH>wyV_;E40+51qJ23gO$Wg zua0oUAokL+4FNA9ATS4D8Q3a8jm2tXP|{wLrj!HgjoPbA8l-?}Izwj(0JLlBMp~+j zq~PS=MaCw$eI&)Za(&p*diKS4EZj#22S`ebz7jc@PjOEDwmjZ?`v=A!uZ%so4zwqLu$|j`j5mn@y@ZbR zEkNl8y;|wPxD}XVtGVh$t3(M07ACgfTkVfY-+l?{Tx2AVE>vmS61t0on7Hj2FO5)x zidNMdWbF81TZd4wLOv2;z@bG(`p9@N$e_ZMC0!9M@ zuaFpELR^{h^D`ew#Ag`H!W9HufRivCnDCCVv7jpmiXIqV_(+lJ6u^06JeKyyOFIK9 z&&6-~Ro&_)cy`ZtDBYb3LOw=ETQ}(4MEeFlb}38BpGaDN@3T_?DQL$unRC%Gno^dezQ(uO)k(8-$|`Gv<2b2_Fgc_|c=4-V%=x~q zO^?)iqUez+ZpSN$54)BHAUQ9T-Y*OcH)C&gZ=L=5fDLOYVn9a5MaU{GA$e9DuhArJ z5{SzK`lDr)EkqB@R2!qWt$*8E2P?7yKiLYe{=Dzh7YKcVb0r2P0OUIY{`}q^gR`Gp zJRcF9bSc;UqS)ktbEl3+|fbsK&ebQ7^T6ynRD-B z6I4wU26gXz-W7P%DcZ4f>SQIyg1nmY0LI4xY2o!|}(Z-oMox ztjGGPr08|T=e1>+PF|q3#tS=4Cm`Avh@dXg*3r>4G8zP)yQH+#47{9h_U3c$kOdsM z6F_g`0JW-RFruzNp6%M5Lj<;y}2(*b=J z+67&Lg7fg@h-uJaPqKYnu&KjWYGbh`(vW)TGji% zTp9j<34=5$rj|Vtvyzdpqt56%^B>+v_}a2RYkdt-+WTh-1@nHrbnx2|>nxJlyjLV) z^bw+ly9aI$;ZSnjJaYX~Kee~*xq!S#or@OJZx*+X-SKa=o@y>#A1B@i_x@goqyvXnEY)ncxd6E32A@ zTKy2#*9k)n?@vJ(z&&e%ctwJuvA(Lg#$_i_tsRT(QBf8q5*K)9{Io^7n0WFcr z_ZwWV{eCGSTCEPZNncnzi?b-*>GF>M6WF{U)9w;%YcFbkxu{WlGGC&4H#p~bYX$Mw zzrzGV_|J00&0A~J=CPLT|A(dX4hQnz|9^>+RQ5_LJ0c@9ltM;iudEP~WUq`uh>)EX zQQ3qPAt{m_krb({tPe7>{hs&v{{A@E^|{V<&L_F=`~7~6=kxIlBujJaR*{@!B_%lq zR|GR)1Sqpa9y^hf-6fQjtNX@?pm#)VdoKUi--@EqYklS}mLTFVzTYnUD}w!*_kVX8 zy#gk*aq5zKI1ELR+y`O^D!t@BGrve9+fHrfeRL>Y5Q!!D-GA@{B&VuCcK#a-IC8RP zjsy}SXpDbu6|d~5qN ztW=JGM`SaJ5iS;fe%mo-QKQPa!#~XC#6@i`=D;5fSf(%pfW$>jcv1eBC_GR(RZB$0 zMWvr!Sk$?MbS$ZWWe5BoRS2FC!CBYH@Rz2&lzi6N`80Qicrwx9ST%VNd}1uRq*h1f zYqq!kXM>quriA9aa_yu+xkWt*kyx>NKWhWt{Qt4~9(gn5Jo4e&_rvV$l`F@xqYtTT zc!(3|J2)h+fX)nP16!>|Czzj|ij1ZWf9YEZC&%h!!AZiUwu-nD^n{+38{w_U3;>2V z0!}!*ls^|b>nz5j(h1@mPlivp?IZsXu2}&hq~N17bHLHJoo8+maKM0ilBkIQN{yT# z;xPgyISdO(P?faY0kB z$-Eq|)dYj8|6Wg#?+%2|6tm5T;x|~j?Cu+w-`XG4)sWyiCWHqIG(<$*_VBEij3Y$^ zh4*B0j@QW0_U|Q$vA;cWmtcTJ;?ka*Z-k>?F`oGOda5J`_sQCtPjU5s(m(uf9osk7 zX}dD_3r0$nk{^Cv|Mic1!J=8U(ZcTx+cdaB#%z9zT6pM9XuR_V%p37_dyT|g@R}G+ zszOdEPV8Sx`Lj!)aAnmk#qV z1&-sNOKS#L;T`IrH!y3))Wh(luatqQAAOf${O6qXjy6#n5sSBHKioeY)Hn4Q^9bBP zv~%lS$|;HMTh>daz8zKLgyR5w~as9_W zId=MVDA9SK0!R|=l#cV52{(Fey%#1lo2HKR0pvB92(6QNOb-wto-0j5+Z~{JN&?N< z+}de15&Lw<#%gE47`|DHPe1QmM50N!AN~I>BMfS)7Q59}ry>KcB09vdkO8@igqEiP z&Yk|{sS-Ly`YR$#lEe3tH_&feO_cp>Zf<`Jjmv=BLc58_#8VUJ(F7CsUOnO#xn5G8 zi06DDLeHIhY1J2lwon{e8T+Rs<^V}`eZPbz#2`mihUF*^e<*=>x$O9cth&9!e9MKN z|IEq`qr|GEm*2fcd;6aE-^fon+^fMv_HyF|T-hR=kpw&mBhqr)_TL!@frVl1PgnUK z;&dU35-F*E;=$Ma`(gRp8JIL>clG^e90n9lv2aP8j0U&j!R4FP^d(KOte@k!_Q}2i zF80F?tE;yEy_s?(@jpXwYdvo5&Q#)#qgU*u3L{y*Pf4mZLU3sTba+-=9Mx;?TMyfU zh7ePOoe=xA-uEk5A|Uh*ZFJB`R(s)*iyrtQHwQtl046|t zriVURF@jOpX4+8NW3mBEjE}hAJ`j$Sc^=kII4UCQ?M47>mFG|y@c%yJTe`N7C>e{_ zTJ`R;xSzQGrFaxRyQ>Dxj%>Gn3%_*e4-|N>gwBx2sih)+Zr{tNX@Rr-A`&;Iifa#K zbT(nag}Cbhmnnflld3@cAmP_bY7_DDNNsQQLXV%|8yRrzeE&;x^S({HbcEmNsKeLF7`XEQR7Z8RZwItUTy*5! z)t8REyfLH%`|X)C(F?ZbZ^6CQfbo_}@gDxRv6$Dod@-`c@P}IA4&ngV$w~T5T=$2w z6RwOo9BcER$1=R8yPCej3Js~&6q&?~mB#=nFP?iiR>ch z;mCle&odt+9PxjP$0OzbI}bp&z&kt82NI9_)sWf$-X*+KVOejX(^E0?mpiyIypynC zKD(Hnl@g;uV-uHb_b%y!P5FG+g#zM7#HIYsP6Q|yVix;5F9!~YDI#R9wWKl-Xn_5F zrlF0NqxE}^SYyVW<};b6+oy}+DN^ky%mZo<%s_C-URiDAWf$dl8_dfha_Sw+aid&4J^zAFhkw8ozFe-5RHQi4TH#c7UohC}4 zUru2-5|Zssv|yy$g1^pN+^5gK-yl3=zGe2X-kof6D5at#tC~D%KsZMU4(H?~YyObI zsgNHQ2#M8MS^V6o{Vls@!xslR@iO5fkOrlcQ?M1iEs@Shd`z}&GP`o#*8_Er_1Wl= zgEDWKZPVm2?30`-EC*xa;I{A2o$z<%p9wsX{docihI3lKdG|j45}&6diGRn+_n~bK zRN=(61^_2<><~fM{{A~92ApxVcpH} z{ku0ji&(q3;4*e?ujr$qAOq=3$EjKt{S;6?XNVEA(=Rf{PXJhRsyxZcA~KooH0P*~ zEl!B?3J8p*doq;eJPH1nSkqKJTeB-Xi~E<}frpP8Ew#|55gwP;#0;;EdpwoD!};xo z9?MpUiM2^?y6wPrKoy7tCd!v~TA&GuS4YAHy8}9UkU}j{Z2E#I1pWjIx-i`9WGCzX ziilN+5z*u@hj_uoh@%-_D}K4OHX*A5D{qeXR8LKykCGVh;z%;}IgWB(iulSJ8)uwu z$+lm7PO8^?TR&Sb{X1CyNigX*|WWR6ueZJUL7sIy|_jot|ik2Z7yci15Q8j^)ZL(Ymci) zDy3wW2`1JLC^0m(wCWK9fqeOcjN7Z0HEai0?~6QTqjFw8zTZ{j8Hi+%SR(0~SPXu5 z#Zs$RP@0L-uJ$H9b|wPVy<8PXfSd&=bPoM=Oh!^DJHNwF zX^_B~o4;lQ)(`RPpUBD2kHw(jH!%YMum+L$X0VfdD)2lBjLRk2RZ^rq;Y}9+c>F%# z8s*j25@Nl*zEx{#SU!H5(hazW6pp``Ccwn@Kp|#p*IGI5Nkmt)!l%}W#X$#-2PY|0 z!I8d;T^D?}`fZUpYioCk#pmCIqLGml(Ov`T&I(T(hM;jyybY)K4gCTG z$a0CXptQdyO$z^4RtcX|t%)bq9IlY5zuN}b%ERr?*BB?xbDRwF*;-r;{uvp88@?hU z({P3BBPR|wPmLRJxH#*p8-0613> zz3+&g1Q5b{+DkGju$CVF_KM)%Uz8lBvq^X7Mh|nU-r_bsgTJFm1xA~rNi`eYt?>y- z&_s~meV-ys3lpP*Kui_t??V*@_uO5X)t)%a#a??%sZRW;^#y5&ul|Va+q1ry zSAzi*mP2#(wC7~7*-b2?y5T`4PBJ0t4<=Df_xMFzPCRmvLn^=?bKfng3ipaU*&E`i zD~oZLuq8Zbi170}7CT@KprXx%b%K9ulLU*ME%8I zTp8H1xlX=&7YM2!(!JQ3m+!|fBLd;qf{*w0Eng7ZT3+6$(|(XqvU|@CqOvl*aTtdt z)^TXP@oXmbr#$kqZeM(xU{ZzWqIml99LmyiJ*>X1n*6m6L@rD{NP!e z>hHHskVViR+(Eb&Hd_u`^A`Y8zKPm07WKkd#>+X3&*QMa0TDa`y$Sn08lUt>duh4n zCTueZXoq49jQQg*l3D^7l7?vm9LZ{nlf4m>lYBqChWG#Qxz5IQYxzNj`PsKe&cXu< z0E_P1XwWmjd~>=b`VnWIQsdd{4CAT}QIb-0kEpxMWiGfJ_ z$-Z#734x%2O;e+P`HSC9&Hw#y=q33<(w?JZH&ABlg~=pe3RZ2SQS!Mg;54wx#A3LlA` z3^Ti|hDlwF6R8TF&MD;+&z|iKYqGrkRWipq$mY;Ll}xOVUX8U|K}#@=k&XmUj>_X_ zZECIcThVX+I}gDvCbWA3V=bg(sSlB!V$n-yff$TygPQn>(D`}Ys{ek%bWP4E*@##^ z%Tr+%5%3u=Z+f-cmCpxLF*~HfFb&DnLl#KHgO14BXe>e_y_m3LSps1oZaQu&|1LbnzFu?+%BY||H&PJkTb{CqgV*`X;wv`kV#oi+N9 zw&Vpy!P-S_!P;qU+!zELqpC^@e-<3+2sD5R6|rof<`(JiI(|>?l#C$zmEQSfw4P7= zLYGYfHkar|7@?_t{$2EAd;78XjyyaP7u<_ItD9Teeq+!#JbArXORuww$qH&<$jcx1 zA_-#2e(^|ABFbS%-gX1~unz<(hTkh3&=zB1626fj- z+7vzUUE;fC%LQa1YrGN&5iOx1n5XLQ;rnzeP*!y za6g$q0HE-q3@yl-AUTaG;7l3u@uW*&9KYQDGSc8*8G;W?J8l=2dIq^R(-9&W?U>UqKVmi05rcdmrgRyWs`+`#>zeiPLWev7JH)B5fxzy5=y)F$k$Ars=hnD&;8(d;9~_LfAQ<933WdC`@*%sU_} zr3}=_;JABtduub#v%0i@ddF8$5vS@}f>gKd_pclQfi0@7A6r`36X!OCQ!gUv-~|2Q znVbx9SFm)5*E~B~R!Nfe!;!hqrROi``_+Uqf6j4jOmIb5hv5zVgnCitu8H1Hg)z@r z9*h z_#K-Fx@o^d{qQ`(oQOfSQO!ziaAeh4zgnzZ*FKfpJfp!P5mi&;3q&FK6hMz$DK!-4 zZsRuE6$WV*UR4T; zi08(IO#Ab{T%hABOx#8J#4Lw`8S6HX{GX~NmLWGjezmE;yu$`F1^qLKpfj@v&p4C+$03Goq6 zZs&&B$o;?`OsZu7bV>7VA;l}(m79MY7sO-@*N490c?%&H!zvemau&n@kITzdrl9bt z)#yF2j|4`hXBbfv6IM0Bs^CKu?=&r$^20HB!lWj!I65ncerJqP^v zjHlfagc}ZfT@g`{kFBj>Mx4h#yb^bj`N?) z_B^>%J2CI|XRv?~%*3&F3B8cKOS5t~s9gF6Jw^T2+=#uz0NX*HM_%luc67M6ckWt4 z2g}PZpiaG#mq_}tv(>i!TSc1p7b7A(h+nWS5dBH{Op^J>=JwIEz9+S7!Z@#i!WB$= zPDoSthQ5X%^D-p}WWBZQCh9GxY$c5?O8h0B zFhjE*`bm=2`UD#XTM{$jQM5^q9CT~~&%dMJ7CbwsSg-c-91+D#7}JK2*bCI7S?u(~ zn0h!`Hl%6h*Tz0UIHEN*H9ztc=nK0~>Jlj7#w3$Au|5+bz3d%|=oUqRJbPj8zP9h=2`rlM+Nlk3h3V>r-MZF+QHM z=R^1ZdJ?CBQ$G;5PYN-j%z5980Z#p(=TH!*>c-}5z;GwyrjdK*=P4#pn-kC`00yWH zDc}?9gP89gG%qx3e}wt5&jc6Ia6BxvBGi(oHAy4}eMRLmlFq&b#7|bzvBz0C_Mubo zY}3Z-zCS&s9O#zDSH~YgaeW)wfTpP>;mNO7MlT(t(AW|(29abb$3;5|ihX0*-DZfBdAICgytHzsHtk?XV)BvkdpnoAu?>JJ54Q>E{fu&%QYyui)mh3&RxO=YMxOR*mYTczzRKUe!5u ziF!|XmZq6m2OwhJYson)eRW%n==BKllA%mg=lFT528DV=7DH9 z@aSAgz-Fp&c9Rp1lY%XM3IYRMv;H4DLHzjPH*QKm)9|;8zJ2@5&kIz#EN1=L-B;RRP_TS&m0J*nW zoqV7(abxI;{9>2`#=@Vuc^uKoduO)XlYWOCG3>6jbX|huej@3*07{qM*j||;s z5exrYCStNA*t>Ox?%A6+kXyDw;dVCkz+vyTi5zA*2T4c=vt5%qEmS@-MOvqP8Qn+!{}=ClwP(?)d9y>zTeMj;gBh^d_KJ`$06pC^hkB z-F6hGE^uQ5ik6|J<-c5^BotXL8BV8N_9uB$#gxYQHRo!Mf`h3iWX7lUM zN_&jl{5J#$P?IQFF<;-9_6o9{wx>guxq(>j-aXeK7m;DPK=Y(W#(%zXoVbm@J66+! zwZ7G&N~-0)9Nm;1mUa96FNKmmQEWLpIML?%9fpQZEcqTSK2l%`3f<@zxwg# zu^O?PY|tqXv4SHT3mpbxAG>(R(*xKP6&o=fFui|7BI>oY*z`)qmfJC6Hv?ql_~zAF zyedQ$j-7A=vfYJ})qzJ6oh|b2>G?7@!IR;H<*0U5o%a6jpKjdmRD%Guuscy@ixm#J=lOEG?7EN~!)fV(Pc`SGXXA~TI$8^YC*gJftk6fSo!GEkWl;XJ_TR{eW)>p1Gh*Uxrm3>|!n zC*Azg++Y6Yr{Q@Y^4;e#^#}*{2Y*%|**?Q+4*^0-NxZWqHGf6UY^-o#q>ukkvXN~& z2Km&HoSTL$w<+28Y-Jx%C_e&4cD>k7k!)|?fm=j zY0sJ0L$e1_F3x$$LRgf97wVuEo%gT1jJyJaYG#gdS)A|&_WU{PegGObvyA8%4yB(< zUMmyh8Q{9Uvn<8@SU-ZkR_l`KjdVV5Dg`<+N~QDcFD&R(?3wnMsv9%i3!Y!yAX??g zG!6gFtB3lf`k}GydV1a9wfjs9p;_MV}5qmr)ti}7W6BX1?Cqksf&Jg*$`U%8P z?;t|F1es~7#7%CAlM-DeyKYH83VRp-;!?%;>6HyR=qiBBguW3#1628dazgwU$ERA zAFg;IvkuScF*-8bKm}i?m*Z`Uo@d@x<_vGQX+*x4lk~iIEk<`C2M)JjdZdaP3#)eG zmk8EBWP13vnm{lYPj&9?%kG;XSb?ZWq0%u~89!4xR#5VFZJ`q#5jJ9LK8z3;ATMKX zs^oiKaj~}5`_rtfpX&&AGEScdF)?>K$2qWKg-oM>)Pp?eke0DSA4WS#e5PH}W@e}V!TT9lhzT)YU0lZmZ=W?!Dj#}{j$e%8~t{CkO|J!5uV#V~Y>jHcTS6Z&=RJwE|44_QF{QWx^ z?~q%$?fskz;m9c#0fqvy)ZP}Xbz8BwBzV`58Kcs6C1m&WUWQoTSFNt<8en!VHSSbD zy-+prER|Xz=0@E{;Gj)T=JOp9ijiaSXcSR2zC&>aG;*D<9If6!d2!Y1% z7NIh_0$v{}i9dcJ=t4h?=3gKn*ME4A_h)~({_>|=u5k;FP8qp_yTLtYm#I{W)sA_K zbPC{hYge|aXPug`L8LoVwv2hOXH{e-{aMTzYY{>bJ>V#@k!cm!MBH^>vKaq2a&su8=V6kE%bp8 zP(7z%5kQ|h7{Y1av!GaDg(z);Z;T)y{0tHC59>z0MKy~YxNvwT{CiAtf}*z(GfSk> z{#~rm!%J&_zSJJ#E>DJ2TW3~4nA%>Fiu@BM$!tC=SFq2Q$C2~(jn$saH6hu*A33l} z>yR9#yvVH+adBs^+)?0LiNWaC8S$|WKaLm)jt_sjtR4)DU^)w$GY$F5ab8~QPnq(C zL(lJ|_Z3$6w~(zC7%MjmY$mX$jF|vqSC`ydRSx5LktmKeV_C8GrS>=Qw)KW zb}yvikOd&Dyzzc)CjF_b_?E+*s_b$l(aMr-&e<1+i%S4?4ge7IahKi7=ZtNIZL78N z#m%Rvk431-@xkp>OjOx@AgXcrPlbK=yD!dj4o5DWru8AllDJ-&A7JP5qZBLp^<8@p z*{H-4csqoc{&9nB_m8{u#!fvwzHm$Si}}>)W|vN|=$ubiW!F#LuR^D{P{e&TZhnJI z{$hhUqwz&$vM1#7ubQ&gObmIMwCqtv)J(7;^ zDL_q(-_T3a$AfNXc+3L{M3^a8kNV1{Jjwz~_7Bw>Ici+i`$$RbD!%EyG*Z+8x;4+* zU*($WQ7Yx+fNNqb7rFK943wcW^v>gli%WosHcnt-<=(5Yy4P}(k zGjQ#-V)uF2ImN*cZKc+h3Jmz{!3U|SL4hnvBJUs7AE)IezH2_d7JLlwL3ig|f~#lp zBoS}yi?Iv@5Lvt~?2P9r>xQB^RSb*11aDI#sCM^|I!s6qR+Es}6wpU<<{itRy}Ik( zswmT9&zBy{QbWYv=5byLJPMPf-a<$)^MghQZkov7OFs8nMZ^pCr)#8=8}VD7V$#0; z1J8-_E;(5cprD*8n8tG2T&~ZU3lK?d2L%mse6x~0WH>!T^b2n28oc)t8Ja>0HqR!5=nsV6Yx@1LjT@l4$4(EY+b-Ie zJM9EL{@v`q1&B(tjEz-g1YtZtc#R6RN3G94n@_iQ5;&WAFV6MUdxXv z$A>K;)nBuqy13Z#MZy`F0P*i#Z&2NlLOaz|WDlsN7oKgrDp zBc~%+av?L6{cQSybp43F*-Oc$T>q}S9F&kf2s(gV$-!E)q>rna_G`FHVJ!Qh7M?Z6 zLqV#0CT%2HD|Jq#^t+yG^GNmMu2oCn?1o;a?>^Z(g`IyeQ|`tComzUJOshwdwlc*$I`_n>G^60q9#*L%-^RHw0rTf`&^6>?(aAqgC?kpnF1 ze7TQvuiM)T|8d8XJAaElhMcWr&(334Rgd#iU|?n;bi2z=pM#UTk=ZsVRGTFEd#m=(rt> zA1wbfk$HLcI%U|ANTq_5f2~hM?ovdS#Y+s?QK<3@8Vbm(Z1Nw?icy#xbnNvOJAIm7 zKr!a(fLV#^j_VcnWn79&!sNS2Q18*5kMn&5PlO~*eQsaDNv)^e3`5rXhDM3BT|)7? zuXLMk?JQCX!V(!PT}Ma9g3uy+GwmSGe2>Ob!@@hm{`;(cSd8>gpB9R0<~3EZY#(3X zdZZSqQv7)er^Vjg=Z>R~uaFK|(JXQ&qoU)wWAl0L zP4m2Wp3&>Shg!m8=j6}$7f;s7@lWM#@bL(iWUd`ISc;JAl8kQ|Aepb_SE1RG41#k| zuOKTkRzZssvo%ALV7W&{(#MbAi;s^FURM}RL#%Gol-{xH!^sI#uRHjemGPbFdw5*E zKxBlUR@EnuHbrXN2ym^>I1{}idUuvm-1g}l`9_ezx72aVcZvd?`zkt-4iv9T;6^LzK) zRj_QMt<(C)%-LZ3;-%c3%&aHJCyg|}A9gS7h%>*ulIpk7Ug{KNq1m<5`Ue&$fI-q!#j`T@o!NW#NwB583&G`xP`pRnR zTvA+I^ZrMvG&f7Lh4r4R`7We{x|s$xm3B96nIF0rsC>6CvU932vo9_-CFMC~75@O- zxUnvLB+2{MM8m(zTBkU${ZU3yzpw1{XBOHB7*~IH=|E@?W)=4t`DEX6ToV52o*iRScpF6wc%{ayIN0Zqg zFEO(<-}2MFT$UiCo6V}?nLdYjvhv?O+woCY?4z_OzuIOM+tZ8_tzj%NN>Wnu?vHKK zqu4^f?sCqx%chR&@**j+;VOKw*Oh+%^&9a{0tHLW&cja}m!B0&6~CC&7dYMuN-XoN z?fclLhiRn`3F@AD;nq7I_{k}uXMW~z=cGiTVnwK4o&5ft#fdNT-(0?KPUC5DHChMuOF7*A~<|4^|QvzM4+ zissa}qKx25p-(>I1a&GS-jm9mkk`NRvA7FNj&=N!UzZxv`j&Q$SE65Y9*q^FrqwxmDNT65v>;1 ztAK%HD0BRzC$QIh1gjm${p+Hk_#jc~es0&r_~h9Wm&HF?a;uB9)<*RDIJD>}X%1Wv z;z_1m@Y-;AZ6eqqwa!Xx-QTQD@(Wh}^i>U411}r9?lRj>vqukQ58WzD@9x$vW)lwl zezQ8V==;mA!fQJ0zsN7_i-xX#51caYi2|nQZ1fp4LgRiJ9JGZ4Qa9cJx=UJe%Ph{n z%a?DK-}o*LuNUG-v%B6x^#&A8d}2IJq~gSv8JRcR{`5q)HWJb9$wg!-2vNa@7!z+@ z{3}-P#$AGs+;dhjNS@x2Y)NE)=sP=i76ti+LyHR~r)M!x?6gJD*olp4(ZI)4&}OiC zM%B{hv;x^(+w`M${kdW6@1-MFdC-5gr-%(FrrzY1;<~#8i8)p#$5hzyK%Swo9Br)& z=WxE3o11Gfmnjcx?iea_W3v2NDNa>u~^OhRCBIMgPK_ZB)A7nb=S>$CIYM*@V9p+kh59XKr4fYoCwR7=Vtsl=A+@M$;et}Mk9>n_RP z4r)bu^9j1R;6=*heLlC)siLA$T<Hu2Ni~{MKJLk=F~a-NSfv)co%I$hLnFeQ!|u zP>)}8u&n3PR*&D`yXmsg2~Vu z>g&7;_EvS5YH~C>-f4Rt+E2TjU*5m}ame5pk6MnITJM{lFBv+fqpAtp8TJ`0W@|ki zaRI8fn$J5v1?FD7=(l z(K*EeZk6RFeJi?rUT<1X3KH0$+Wg!FfdH}k`Bon1V&g789pv{lS*o0i?b)$~siINQ zW!EQ}w}FxS2}yZIz-^;)8!a5lj&a-yzojS_ckfzV-2-ct-U-u}@ll7Rxn0}}SFi3j zE0Nflm&+>>%NrAupmMi5Fpz_cRmP26GryX;v57HCLDx_{Re&v8dwnv7llnq3$D;-I zyCR{x$bgepsA^SoDFZ?}v~RX4Q{p)yesFNVFJ@BWWwV}flQvqNQT17xRK%JAgZip6 ziTRzHu)00J@}B9|csgftyUf;#+m?G@9S$YJmj=n{@1=bIK8Oh)pBJ=k897ckv1UrI z4G51Nz{c|7zj>Kyk3-=!!(cRKdX|{sgzL`LIsi%G324FohY*t4ez#yEVzGlkjb`7O z%gRum_P@Dth;TfZ5Ba~a`;n$86vj(YXk0;2q|y4IHT35~-baH``cZH?NVT%A_c=l? zwD0D?&6+p0`l(#Q3bSd3JeLh7XlK{f+#B{6jrO#N7Z#^bx)0ts6}*%ucRJ_GP)N0R z+JSuQ*sUDn!tZ>Zf9v|zrX?f&O=tVK$qlxQsw4B?`1e*UOx_w_8Jct)wq9<}`!8pE zFZZf~uDigO$1m5;eD0)`mGWop9g^D2mgKmd_IR5mKmONLwF`^K$QRSP6HPR7{m_Nl zjCUM%7^u02ZNY@Bv3qni>AP)qlF;^=a zk|6sBulmkM|0f%!Xw{Y~aj>8_u*>de%5^2-)vgOwqluUQcyE!JbZ^t3Bc}h4$XmGx zA)W9ay(a|_Q4)EmJCFj_2;jy$`-(Z7_j!NRlsG6Tv}Mk;+}Ye?xY)=DeCF>3Z=g(U z=T`j)O=b9Bic`R}Wohc_ev}>lQ0lnp1EvyO10~L^dX8KulN*Ga6Umalp?iDwq>|KCTl=?~qLmi(${6Clz`w!c#&`7hxHZG{ z9Oi_jl#Gm+qdOngW6XOST$s0?7$Q?tL;l@XbZc0#zs zcsgWXiF#D0!-l?te%W*V0E}88^%f(6`vfyhQ^hecjwNQD2-A(hi9CGPUk(?7G(xA& zWp`x~8S?w_5!hgthxm5_HpMaug>-L4jjTZ&zD3(A9PWP_PW1mkW#l(ubx4v^*jwW-s;dzf9&kE#ouoOBY93P zqqDb_c|mp-*GQtx`FxL81vwF#+QzEY%QHqR{pTns4p?c3aFDZem*3l5KS&kX{B`(Z z3D7z{x}bQJ;O=W3!TRyDP`OzjUA;` zoTX5lKUMt3Sg(Qk)$OfKhi_%cQm$qMT7$2Q(YaLzX=4uDqN1W~*;(v0n1$e*`ot~t zO;4MyH)CK{C+04Re;tLuah+W^+A{mfJ!j3n6s{_|OT|55v5IKS0>gt_J*8nx;PS)8 zkJ)K{%l*(_9aCi|3q45eO9lUU%i$u-dC`Me0|)fVJMS_@G=oEijJK|9OMtqs7#CVU zi=N0J-3eJt)k;>x|3rBke;*ebR*@hP91$!3=O27-u@ig*WVeNC@_xkoS~(wb2$#Yu z>;Z{LEzqz4R-$(3e@zzMci!Hf@5c8RSD-Pzm@GpE1^SOnp6#FV8#~Wn%}7M+U1B_u z304u*8rfa;z{%`YIHg5X`hDi!!$T!~?_tdh_(ujKO0&*t?j^|~xY7kM0iA-M1IjOV%chih3x>f95xCjHG`DL!|+b)jgOO5;-~ z?F&D?(Wq@D9^(j}lN-9*o;SX&1#oAkSI)%w{dgdu@ig-DvgwrfN`~b54)UY6f9k@1 zGx{CQrD{@5Z>!fG3@R6Szi0*J9^TAr*Cq;$7WQ3|UE;(dgo7p0;#BUZpfOI-YR?&) z(O3MDD-jvf*(Y_ilUKNOKgKp1hvuo#%S(_g)M_3QLY7SX2?OjxAY z_IF>M{;?@hPegXy{yzM;vXUuC{>Y`T&)80fXPuLKgg1gnZ)Olk!6JSf!9qbVX20&d zbhr9jQhq~AQi0IB(v_R4tlv$j$iPDRg@{u9uh4s5e&z71(|wBAj%I6(XY$2rs@n5` zPcyQ1$We3f=fyFWDGr*b*z~x_thY7ZN9G4T?X^{NKHu#x9E&5=x{X{a(T(r{ zo=D_`_l;S4k;0}Ut??6oh=^?mE4pDAxp+#hu_49G0`^qIoTGSG_p#vyGsi@DnqoI4 zQ)aQm&UZ0Fqs1R&|0cK-n40OxnJk8bOM!LFBy-Kw_jj)o{@6A&!fH<{5_6jg+~AB zcGgX@s>>#@8T@yDhf7F@rtA}4`MUi1>Gc%(CNN-JXq~PfG;Bw1+a9H zY+*$O?Y})E;lI8q5rP*Fm#kLluONBAh%WNd-M6ie?BdO#ejj2AieSTpn}tdl&SOU4 z8-jSl`C2qtNlI0Qxrxf;<{P7Wtk1k1zMXoV(HDtZh2hor><_x+`HhH63JZW!irC)a z=z)pr$QdA< zX5!rX`xM89FWW9QTTW*o+lH4k1M!=f~ zcQgDx7}-{+ypSD#DkZbL;szOQWNPk^FQRrSkNAsCOiUn7F&(~%nAFrJ*n%}wjVU4+ zPL2gGbT*B;SQfCJcM*Z!+>mvx>aS-+uc{E!+nIT%iKj`JUp78XGNa&~^rYDBJ@J$g z|4Eu#-35F?xLO1}GN;dMF4E#fd6sLaF2{Hv)O$3cN(SmddyU}TS?s-vIhS`CGxpk& zNlMv1`!*ycENWPLa{uuF>4#%F8ABnbHPe-o2C4W}&;Ka+*MBXBJi(_xdZ|JuZY$>W zXJ=L~y^tmALieS8EWPR?Ino`wg@5tn)@aLX>KW!wRp?CFZklHwyHzW_TYskF%eQdN z^)uILNv|{dPX0GOktxb+NF42xrB^dT?CBLBKdB53<(5-`V)4)C90`;vz+q|WzdUlQ z%(VTJDc46ShKSflk2+BImAiiz$FiQ5E$y~`H$?pNjl=s%dn7|618$4}%tU}@Ogv}Y zaS=lcr}9kQ@4osWiTSB-6|e|FU2p*fLlue+R0=liuWmny2~)@dZdIhb?4g(|cfXO5 z3G|w$u75F6r((MIsj9Wg+U9Ea%Nv(*tIKOu`ZSP3EE?T8^?t@m8HZoAho zcO(wZT8hVq60WDRgMfzu6mz8REJZc$Gi$sM06=8-ATdP=@vdRP?<@HSP|HRVlrx~S z%CGkcW_6Lv1>An|;*@l{tN3@1>9bna7+HG5<@;!ZH;Wfxob^@dr#}}OiKd9(Q1#Fd zhYSndo!kyx(&NY`H8ay;kD+okOF55LgixRDsMieBwWOo%b z67v~RqdkS}6s~9&e7VB*J<3FO;n1w@XiGv`fd3`7bP?tveYLH6UsCyz`aL{Qrw7m} zUg>CceYo}24a$>fnZM+)_gpWo3VHH`6SBIi^!kQ0)O2?q7QW_}O4ERvZbt-zfaTfk zoi$tErhxzQnElb%T2j-NKdaw`2zv(qmk$ia-n2mw77Q%jWKW)9kvJ^G4#;5K|CTF> zQh|z;P-2OtdMNFV?(|B7i$APx>)YE>~mEVu*BR_iTxK=F=OHC#?>?NqnP`_3~rzt|j>I^489pE49@8q&L#U{MymZQ~xledLMpcs|7*|C+l~ zi>5`rMYTMacqFWv_Ko&3Ppy%?e?37O+I;3o9DU(+zv7;)&Tl?ubz$2X%;U)l!M1<4 zujfAsbDA5$&)hb*zTs?;8uyv;kzMNXvyyaA)Kgz#$?uGr;JaT!IDrW-%UJtmg0k{_ za$WBUC(kYth_~Q7(=pM7#B(R~zT+Revcm$;dgzP0nmsyy<(Jk)ZZWGH08iqRv-}8U z==N~*-jH)R%;RE`RSIxWzjBm~RpMlc>_YbGx0g3sQ*XYxCv|-9je&GKObyb87JOg- z`_q9%-UK8_IyoA{!_njO{qH(I*}ouaW+Ejx>o>cgvN2neL(yy0w)RR9&j=2cA0BWlluZuVATv>2MdnZ4t5w81h^pi#ic;kax3 z6^$~@|6F1c6F(gG9C-Zt)Nu;^h`VI_7@m(MiJ`5^-MLhM^mtw#q&%c>b?w*4(W$uj zbq3&GMr@h;iU+@f`;V~{WNxISS=3^s)=&4D!Tn`*i>PwAo;7G%id;RgXXbkakC?#S zp@8jOMfw+z#NttH>Rw&mPk#3tCO}=eT2}9VJxEa;e01@e(Qz!75Z}rvDw-a&8VX_7 zymx|Lu(V>-o*xB1CCZ$!4{i0Bbbpgyr3fz!qzhvy{i60FKlWt@sjK^6-W-?5kETy= zC#zK(7g0D*O$S8nl@?pO)Hb`#5b=Xb`iGL{q3Z?)2V!-${g-OJrS%otr8WK;ubU5q zlIZSLl)0M!Z$m1GNQ!%u7^PIeJ+j~>V>e9sVh4O2)>0vTQx_7g zzRolQVK2$VNq2xc@&lYeO)du-m{*=~iczQ2*s_tyx6e~WDuI{YK#V@M2cO*+ zqpi?8P}1EpvakvbCEoDIz4n@PNOb3Qp;YO~wbSxRPq`<{@>Jx*d%8;`*F)Ab$6a{itf(7<0NB->z0}_UrLjYy7~o+I=guD>_2d? zN;!?ln*+R1rSh-z+K zcb_8bNeY!G-h}z z|L+>!U%3w@{`O%aS--?2(vGi;pZKw@c!yNunR&0;)}{M7|A}vPws_^7bB`Q8BmBl* zZOfWv#WsD_d{dXF-fUxAm6(UPh2(3x@B5h9eCr?HprYOKboZ3lfsdwoYpt97+HbLPX%&?UV#gG%6?Al@hm4UI!Z5|HO?w#6CodB>GW{R9A zy{`JySKD=Zeu?kw0H1-UlI5`;JE~-K#QN>Z z>3hNrmrlx)vEK{s-Ox*znpfy~K1;nl>3(8b6Q`0d3)urvVPXUk74P+_l2zyAJ&cbb z-^PX(a{=}yOKcbrE-S>MZf$LI^d5WhUl$WZBJiS8jl$%KiBj9xIOUU^!Rub-SOv^Z ze>nwDPZvrr7u@E|TS}}p!GG)HBW+BKg!@{bQHu6vd=6o6dV^ z9DqagZbYv=2zDG)l(BK?MNy{rLPaCk*-3aaV@^r7w9+I-EiSq(bu%ggCWZw(V(d60 z?G?46tU~yTB=bj=4(niCVw@h;9ZN&suiA|XNAd3CO_ugj^ADeMAS~D}9-}O)SKsye zvh3(0IjPA>dHG!*#3V;)6a+O~@tOXpTQOyZe>Y&X6~@@-D<&cvGb=ZjN+tUkhwNcZ z(Xv~=RpublH%d-cxSG6ppAwEYuEs|2x2YQK8E;jT@Z_pcaH+hrtoXg)Q(!{6R9NTp z2-2?3Sss>1h|TXcT2}-Z_2=vH|7^0aAmRIYhc}Ih=ZXDI*e!L;?i7CHL!!i!v(-S;;ITmAxX7nNc=bQ3=^AdykNrQK%#<2}zO)$qr@9*8h6nzyE#Cea?OE zQ{vwL8+j$3eIcqFSq&!>CB z`iXV*wd3B;B&tMOO0?JNc!S)fjAY-KDjz+lb*;&;#0i?YolgyJL5iWSJAUB`zY6yO z)LYd9tSsT>;s;7*YT*U0ete0Wh6?erg3SBerKIJbtZxQqJ?I@d!V(@C9gWb<{B;rA zs}@-qlwp=J$_lZIt2A6eBU@iNq6DB(!qiVwT|MJ1W6$3^w)#a`>^MOnQ+#mlIHhqk zSI`wc!l`Qcta)1MvM^n-#_W8kYSe~xxIDGVx*W5tw^nb6OuE>{77DK3k7Ug2 zuxv!)1LD3lHKT2Y3hOujyE?xtR1*30FH&3am*%`A!C=L&thKduKKrTXwI2WA7ix#c zR(*qxW?pAZ?%2)Z`#S#(JMO023S{s(hbf-7n7dR=qH1`3!Ba9J^zDObo3USNf0I+x zS7}H|>UWSFP1OJP=%pddki#rAq|3jRRh7Bvskmm`{pvH0D!n!Tt$TRr0%Kq!Z6Ix_ zkK$*pcRfn6kwX;H+4+%IgyY1kc93f1IF2Yj@H1gi-Gd$SU0G#i!Aa}M2S-gV28SHb z{X05pj-Te(v6GD>ZY?o`xzXljG$D%CNxk$6_db1!7JucjRa>-H$=|h>;8gLPF0aq< zh)TNjmav{jz=x0jSfsXBM`C>$41myT8FDc)^w5747yGf)*w?Z%Nu~Q$E+(x0mdBzxQdsJ^O|? z7f<~}(<+{-T4``=_|CFAt+>l7&5hd<$!!5tLvxwuA?HJ;E^1~X@H{h~+5!#UyI>o~ z{P&kJT0|W;k#cAM=^Gt6n6ljbQOTqqcgAUn93~-)pBeX9+GQ??vjkr?e={;qLWvlp zh!%W*|G`v~%p`0@gFiL)=H9MRw)Q-w6;@&-%&o$_*!#9l_DJ|ghNCod*dQ9n#_cK^=sz8sBy61l=U-Je(YA1lSB7FtYi9JIAd_--QDe7;1wtn-e8?UUo~FF zi%olbK8x;!EJEPcdu<8Vsdq5r`J+1C^+aP}m{UVOy9eXh5oQ6M>xHkVF(w;w5dV-< z`28is9giN-v#9b!Ur7vAe>3JpPy=&SliyGE6wa@`a@0G^ke~6|Q$dcMe!p|&(?|mG z@yu2a6SLbevc#4f6f6W|q7OJs4Ac_~0|h!1DhA>#iqSdZMb3;08Xv8+ca|@dhrq;g z!-AHI46tGdmJBfs6Sn^mo$kBF1eK+3;Sv5_2dGE^6@+x@fj`H=z`tS5rVwK4IG-Y? zKgb|77k!UlD1h=Y>{L@d6fo`a^B%>+tao2dUWLj8r_KDY>AgKeS_wLHbVmH7YLxF9LY93~0#LkxhLLtWjSjE6b zGBTIR{={TERsgKy@xc*58p2FG<293s&WFglI1#Xa>|GZJJ;R{_XZj%Ivvq0>{e6mU zLeqDO!1EAS0op&=S9?w=BZ?d~A`wZi z=Ujg|RWwY4LrK#>x1nU<0};T)tN` zT%my!0oP5=ssve2lDfk$ft4R(Y1>=tW$yEIl?F&kIi~7 z{^b6jJ?O~-$OOn4=dpU};oC|NIxxJS`ffi#J zPC97t?y|F64@+3?#08uRsU2+eiv+7@WrLPf?qtYaz81*D2=?*s!Mb#$j6&zD>HQ(A zj1eCvZpWNeu?Ts9%#NU?A>Tc+mRXRVbDX1)CMB8xyC zDbTW1Gm45rzg1}8%YOCh{vl)csJCIv7*X72O@y!)B*i_m3&a^j5+Gm;&eIJ$5Dxy>A}uaS&$-Wf8m&RgvZtZ{tnT z%x;^Y|0YN{T1<%~hV|ZS0Es<-@`W*^BVcg0WqLlV%gTK{5x*bXbJ%S~t1te%EuP{s z)1OE!y^dD21-llf*;T?`I10Fy#r$xyCFPjOSNarC`Z!NqK~MV7+Bk1#Ajw9_vg1N8=moaZ=%J=|}}j6=U|L zKeq*aH|OEE3m6@xmwhv_{0STGn{FF|pNsnqP3P^oDUVuMJf02`-!a;Bh1b@4!JCwly1!<#> zkt*N8W_TMI^AWp*TYxSoGA-_HyM6^|ARHG#Tf1*mYt{FBUlu#!gpldAGphWPW^~M- zuQ4%TZRwC%Xf#YLL;qP6=lYVCi$Y(6Jg^~@`3zlNqa1$Yw+Ef6BP7>(QRegDK|2w! zuW@8vV5O%96$UM`NE$j1f-*wf^VZ+Wxsu#7UiMBa(7zetZM3W4dO$@MYyA>ORyFuA z$knjP@V!WB|7w1BjY`tTYDY8atE)`AQ`3mz125L~GS2x=De;U>-)K;6x4NB04uKii zcZ^}XdnA}*ptdr3!?nw5V2aOYR z9XhMMz&vb@HkXU$+`M;}nZx`Uyl1qeg4@NgRgY;5m8fUNG@2ar`O8$}Q3K)Ho`^m_ zmXN6U_%=M)M0Ixj_;EOn3%FV*v@#QKiN0?;@!9hlKYfOy9Y2)16uTwqsiKn7-bS|Z zo3+IiJ4TZDUl2d%skqyyjvD1mmlY?M2pNc%2unbPWyR^bWn0ON6Y&9{z;t2AO9U-Q z*t(zbR=?>93KGOaC_l35&k^L053$0{Mpyr5J~FL}V%F30tec{+7BtXWvf~x6BcIc7w0W?8=N|-I+r^PwW!F>YbP|mR3 zUQV#L{9N3y{!)}^lRED)zi~&{_QxYUr7eFHKjoi!>}_-k-``Q}S`hrl$J{vb_hXU1 zmT4I;M9e_qAZawD)x06?CXS(uca%PLZ0Wt1fGn4;Lug9K(Kejo0?uD9V^^K>piN_6 z6}Ox$y*#j4JFK5PsWu#YS04J~I+XV8Z8kQB$o^fIS?7S{J&gxw#D%ydkLGJ zPU%HH-9rCS^RsF)i}Xejfr4f&LKg3}u}>xt-+p_Hg9m5L2G(7Xz%auk2Hve+;I#1_ zx5r8JLGso;1tRD>C0Fmhzk#t{RunH$<#s8@y!+7lRzp`23sU_`D_V@2@*oz)_9a`4 zk%$z-M<;VIB=&8M3Ve>jcKr$1y(go~1VXLj(y7-E6r&@}C<*G@V2Pm|1SUZ`i;VfI zT~<`7s+SaDKMKB+^4}kKy^8NQM!qg~-?Ek*x$hdVZJR%3lC{IM|4)mCEpqm zZQiq%Xe|&fKp+V}{OM1W6?oWco`rZTNg-=Gyx(GKl7h*@!=^L;iG|0W&;wE;V(Cxc z{}Q!-2b-9i=brv+Y}R@~@4&7&3?M?@(#Ri4>(D0>h81YY0;ubPzo|*q69&{rN<19F zBr@Bp9KM^|d(OK3W=Fo?qeo2u&K}tLsmtoYqtDuen|87}{#SoZ#nj8=1 z?Vqd|c6h$6Q)aO>FWqTn;I^4iBB82!{qNqO&qf%QaYM@}j?O0cSN)I#Gw ztozSQ{0L=aT-=9FnXPt+weAkiP5?}jr5t1U{8rzG>gu111ZWZ~Xkq3f2Seru3hdVw zBV^X_o$K_bF1b*KgkYv~xw`waWsUB&pYkdDc_2QH$yVB{|H9=MV2k~3eYb?|OB$Hc zZySl_s`;jL9`gBc$=N8&d4eB2Wg)Zi%iHVIrvi*!8CJsCA|PDCm!1In(hP8IaFJBr z2`1hi^ojsOXR&!A9ZgYdTiaNQ^M|3JZ))f_@d2qx&ofGWm<_6EZ!P*(v5IX!LAS5Q zS&hbvZ*@t_DCu+{us3t~)H9=#<-We>P9-m1!{-5}J%s`daC68VIDb1kiT}vB95xy|~|O=2c8vAeA!t3F6oB zGxL0t>D1$aYGpI5Ju8Lc3BHlo96G=ancZb&^}Ujb*e`)C$PJoi@zv+oEBe-MBF!9M z6`}}tl*z;nih*GaY{oFGk)&zu9oIg#>Cbn7pR;$e`=uE1G_X?&ci3Vn*FFXUqB&aN z&==#(KL57F+-APYp-h317r_;?WOD4MPQ}MhylMv;2S=#0`&>PRxy9IA3tQcML&&Ki zr!(JbZ4deF-mg6%{_MDxFMjC`J#&StkPLi5-NPq!kIl!hgKO7r0=XR-m-ZIqXyg;* z2{7(^6nmx`(P_s{9vdfQJ(!4M%IYw2Mg4=>f_ME&Mb0br1xMYdl9)bFXccb7;4v8G6 zhB+3{F4JGa&^cLlBq<5SZ^?f# zJZju0&L$UMty)-Jnq&-BWc3)O{r=3p6J1?rb29%hexpZd4mq8`7A7V-y5aLNEoGHU zTw-GJ&6f_I_WE+u$bDV`4K_hRAfP&catuddFMjf5@!M}KRJ&_NqjwfbC@;X%$>;9#D? zWMOnp7ox%(&!kc~#3k^>#Kl1(Q!w=Wv4K<8qu4l&aR-pql+`YcR6VN`yfq~Z%5(!D zaWoJ8q8~IKnc3{EV?1j~bGi!Z!VWOf_`aFj+ff&Rc5|Ae4DKxrpgq}sZ9kjm$Ay-Ohkl8VE z8^ufuMlOtP-+(Gr{reLfJ-5z=B@4@CcM(Zr(Cd*BpRH55vsF(4mzv}}Cwj^^jWOKw zzeZbyJK9Dv16fVCHzUfv-9!{z%g9hlMW^NH!@@O_WopTl&ApeCgpaYZ(H06(7ep-E z+xmhRl4x#ePJG>iCyg!(bDy?YFeii(H(Q^ZCDxCWP!ATZ2H>g6>YeVIbOm9cq}r4h zC$j0c_%?uHIE?w$@AFu8<~qi_?RV|H*1oZ7HkuInEv0Ej%4>B+?;kfkWlUmhYp?OC z3FIAWYgAJ0mUZ3v;?I}9uSiXVuipadV03%jn-9(AFZX$-2ESc`ZO0j^BTe_pNSxNM zxBgzm%Q-n%Cj-$2FF`m#{e|swRAOT5Tw#gn_s`bHN9`_BjxjJm%?nE2RGikt`aOiD zHk)SJ*ZFt@?~~6@Da3YXi571tIaR0x!-xLz)^U1z#c<}Z<5bKrjsa&&%=W4bIhzmFS ztD~^IpF=uQSBc712+J@+A9`gsQHJki2WCyU)dcH`^cx?wMmRO@>+OEwYU}o|Ho4yK z)UKV#|B)AG$$UD;JNtTte}kIk=N9#k3B35bNQ*#W%x}lD1fHp(W3#%ZmS!~qRNWzi zhj1$%HnaJhoZPN*g}qKr<%vu0I77J)^cvm%k3aqZI(CuX(BU`tyr173IA<3l3H!|2 z#_Ry{KPJ+VCui`?t{FNIB6YavrrqnYgFdU6xeBgmbI`#&kzPW5%Ln|*S%i> z2Lzx55hy7yXFfEscg>=MS=4Sd zZ@3ISzdeS6Je~ahUEM;3JdF{n9WD}5`vd(-23~aKC?AmC_ObQ^$)hvGSExSD{O@upQRS%O9NPq07 zIb!_x_IUnjZ6$G1v7*)vsvISkatU^7{S^R+Zdo>pc%}m%ovs{E?jf47;7MBvm>{&4;)u8=$ zP7YaCXXsnjolSW)OIiCxwj3g!r6QcJMuMppZ;z;MI@6(jWKpZxde)F%vrm-R8Wkg z@tcTn?woAbX)HBG-9SYDEls)Z#JY|GRolekJjN=(E{eZ!JxdJ^PVLC7MNfea5R-Yw zk6-6)B2r6|pWpHr4&wRt@-~}gMI!J*=rnx)UAj`Ua8*RskRI{j<`CCNE`2@CG-ampQ*OGvPR*A{LFc0)p6v zR>l$_+cXfpV12YgGewqWq{7Am%OHs4K5g#*`Ck*DNY3$JvnC8`kgKiA8y zJiStc1;)?CHOOt)S@!jcZ?vVVVwcebDul<+`aOxOAJ1P-77at)qRASuudv>c*GZD1 zSeo?_YxAQVgE|p|TMbEx(JAe4tp*|}6Nz{e@1jWL<#IB9lH<7?>Ja-e3ZqvJ>YoQ3 z8qaV3pt?KAb{#JbRu@m~+6=CFr^QM*>kK&5goR#xth$ggPEX$$t0lFy3I{H%|%L2znvAW^2oKG zn8&83>{~JMCL-i)5pn7;VTTP^j>M?RSTyzMorcpBI(oRGq_87^g{cLGCX8Nxq8#Hs zeIh0_)P;bR+f@(kHXP&UQ*PPR;!-|JLylF7@+Gw!ctLkygp&?7uH%sZ9rD#XNJ7BA z1bU8l!S?}KMnGGgsNHDzS-Tc1fsffYr~~#LRs=d3ayK)Cb{k5YldB{N55W~;vN~4? z_u~%WW)UnUq8fj?jK3gY{mVgbBTa+%T)PnSDF8l}W_h{x9N|PRzq28l=Dl#tXKPsx z#5Uk*_ZtIziW6CX?i&eSsp6~8-cC9#Z0})vy57=51PoT3b%m2sQ(OB%((71g;}(VkHNo6BI#r(HsS!g!+hK zpQhI5-(%PWOca*J;p-?L+&dI$qu++p1y(9#s)Ngkdl=i#wZDC_s7#QLbhYZ%7@TL8 zKu#0R9n@I@|3=m@ab~Ulw-aH2dk^`*xMe>3Lkbtzm(spB@LkZ70^VJ;?!#~xa?tO_ zz6u&Yiz*1RQ#v5<_o29cgdzl2h#d0RCqG#bKj@&(RfL%DBB;fz16~AGOjaRS>rMk% zl9;_J#K79=+mXTM2gyktV_#bJKL0O~iHZFFo2tPQ^G0LedHDA-#TXAb^4v%t2!baP zcpq~i$E9}~xL}WoJ3XlTw+;B`T~)9Ty6EQC$dwu-B_~Y)Xq4Q~CLxbRBHUbMo6rA> z5ujFV-9N7$Df?uT`$D;9z^;VpOfbG(VsHRmWE|&SsI^an_&?@HErYQE3C4_<9W`i) zu&!2`t%Wf#F4KV~2>woZy)b-6vV{+=Ui8DqC?LL#&EB8*bQzplo9ES-nT7 z_wfK5!z+(PqLL&W1F#EoF^3V6js&+JL2yJcHwu5O zKMA*8u1djhmPK0}ERDf3|5p9LsYByu&g%7B5M3SJFJlPU#@bH(3p2_a!46vz4Wgmj zHw(gc2In8AG78Nnn>BX-5p%uz>kqH|(@gqOQKk>wiwp(DAFY2AyDX`^|5!64w?d z$~LDlsVI+~k$QRB{)Z$Eu^Hc=JCDCVf$Su;g8CbKywF@$_iEgTzY+ zr-aj@6Q<2firIqvp}oJ2T&HEZ+6hpnEFbrQxx#rx#h4(_zJGWxs@T085d~Fwe$VEw zB?FlJaXcVwA{B7~l?MBEXBQNOC^mpGd$f!EKCiAbUEw7@AQp?-FTV!=Ofj6Xp77-d zb^e)0aJ<%^p~<|yozsNhW;-Nbl#?S&H)L>@hlLVjbza~VKxIR4&wI3Y2$+~|egEDR zZ|Z|{CO;sZ1)Neb=4}|U96z-5;x1Au1@T#7@J3MVu?^%x7mEq^$8w8H->YV2FJ7eV z&Q%>5PqrV&g))Q~Hxrc1jxP)$lKA1fa)-#^z+VI|d%XP8smFtNeK2y0fk2Em7-TtS z#ZZ=3soN1|zY_!r1-swc9>_yC_|}i5QRJh*{rMdq z6^y;*6fJjSXvlfepi-8etGv8|a0CM%BW(ZeK88Jzr964^Wb>(~2({D_jZ!$v5k_($ zcLHrSNq7k1P{7%PqvBE=f1{P~!lrX)SAoTe9hh4s&b`@%nQJEm2oO{V@aS9hSJN^v zC61ckh)Il+^E6<_-#J1t&4QkX|L_=WSltRJL9R?&+;UXpG=0;hEd#sUCMKbU95@%2sI^rCR{?nlw$af zgR*bv1Ssk9^sq@`3Tf2*LgwEn<|~X#$4TvVbwxe>qL!9~H8r)6FS51qxBFl8PU!F) z;5K_GpUoofa5uElrR3GZ#Sdpz3`J;sRtE1Dv|@CB0>GI!^mUQz%d-(ZHYQzJr!J_e zDMeZAXAytn4;nwlmIOtaAo&wrPf^iL;+{tDfC6hDjKWPY-=MuGdYKYNH!_H05v$#0 zJ_@cscJRHBP}@t*#PU(;e_d!3_F$c2Es$1ERY|;1-jIN6qL2 z+Z)p|&iA=kRlGS=!YCT*z}Z0X&2=rgab!b11j5^KyO)35{}T^cH;RoPAn!%jb&p+3?2@6`ftTMf zct`!ym4CdS>h+Q)&a|4j2I=2@FRAbT{(Tcq!z9xrOhp>HngQ4o6b$KMTZx>mRHf~E zyI3R>)Z$C75fU-tt<}oA3^zTMilluv2;l_g2)QOm9Ni6`Dv`0;eCO1785k_hrY6I`&zp+z@BLrFv51S@!rgpwX z2meCg^yJGM2Zl)7!R;-i8v(Eu(mTzCg9k%1%Z^iZbJI!2)&nm@klaLMRe(od?le}_ zl%+w8h_KYZrXdIET9-{F%lppK@#DI!=P|#;UlJh_|N8M8>0d5=$tdM1eD?Nl_S)bL zO`T|Aus&ok9kJ35vsM;L#$|X6z#kaQ_`=)R34RrkPyj9jqvx7Bie4o5ut<9+z@4># z6IcU@xX9Q*D(cy(k&#tLhH z{3pmUjW5XIdl>r-Q(53&s40%y`Er5oaCAuE(BH|%KUD)Cz7KvWH*49?f7po$7aMR@ zfLZ*H+0cS4XyA_s&+ZN)KO9W;#**sjgY3;Ms%_(L)VyRrd?s!o%}| zE5-kiFLSu0?%v*wrTU2#=fTzaO1pt11Rq8nJb85i_Wqr7@B8(5dBrb#dM@w!H1gMB zn{1*R@K(`VKic==7{F{eQNr~&@y^u#?`zC|JWpLVoLuU${v@xp)mk2<2&`*>?GMdE zMkImFnjNBw+K9CSM$$x#Aq-IFFi5n6!@j-kC5Fb8n|^=uGw(3jyC8~z&(ci92f7%X zw6=ai{Gl|Tsbkzyk`#9l@|J?5C;_D=8>o#&dD+T#c1On_!yZfAb*(XZb3dB`Vaz^l zeDYr~3O*nG3fL0du@Jig@(a;BV}(bgMn7y7B&JH`7HuD`DwigmS&0`2j#q9LO1+Ty zC!bAg4EeEx|6dwB?!mGveDY?3rcR->93^iptDL}hxh~ASHXL%weRg2Q>RT2WUby;+ zc_MsL(vG^cZeGHyC3L6UPkgsOd!N~Y2~c#U>^|m#i(P?i^FXH06FByXx*7A4Toqg` zoTpAvKMxz^1!0xI)r zc|7@7rRU$7J|(_`BN*HerI9YJsoU?PpcDjLI}-{ce*ak?$zxBRz|cVY+wEn|-|xit z;*BPt$hd0%U3<{Zu}N9oiWtOL?bosdT->Wr$|47Y3PCi)V(>g!APs%bdmT+p4?xOBj6K5I1fv6Kp#(xN&IHad> zL`a?+d8^`%E&Mx~gw6!P+eC=Xxl9_OWNpn$%Xymr@Cx5sY`74$HHOFsOf%*`^x2<= zqK-(x0QYI{K|Xdau2|~`6)q_$zZAu=1W6AeVi%Td&)ku3s|XB>$L9QGyU(!l--!w5 z(F^yILVG5L-bHc1$YA6#W7uT|mx2%EU%RvM*9;^un&}Ba+{U5o)+T$~-uqek=en8?Ars9VaiheVc-4Ko#bxSi zP_NOQeag`UM?KOau`tj$tl7o{o6^OUV)Q2D?dQ|yY7^ot(KDS%?kHNyvO)=V;(nw!Kwgr znE3nm4if*t1jqHW`w+k31+cvJpZ?UMCnk25DD{bLS*4miG20(%+(V(%AhJ%+M%88Y z2--n>5J#0YcP;pT{?DdFeIi>c_jC|>=f{7&+8nedv6p|Lp!ia3Ac#41rGyj#Dhi}# zYp|)vZ~=i-s&zW_#Kl)dyCZr7!HW((Kg0dKl*G4C zd-5tLT_!@NR8u7ooAqG`t?H#q**TSuP!KLSEw0**7(dK8Z~ z?zgKtPXCUwnXB2|+vtVsWN`Q7_1Patf3+-lH>cw_&%nj{nI9QXg2^gSTlU*givp(2 zG34m=)Swv4Ye`l$mgNg7qcb(_NI zmklbNi(b(vd(nX-@8A+k8*~TiybsPCJ4So;=c2oYI;4`jHvZ1GXlcj+Uc$xo7!?zJ zOlwp4pR>pzW(-q=u1}rb9|H<(_2xY#iEZvP+g|LaSc?wsHI85vG zO*OZu@&U}zKs*7>cnHE?cS)mm%L8Y=AsgC-98qAA@o{m)J|7JUn~8j z;RW=|0QAJ2G??4mdkS;smNSU&MRz2}aWN-1Z($)NzROAkGG5JrJ&r3WFpWT+6N*Jk zWf5PRY*^De1F6Un%fv>x>531U*F^vRK5}wK(fMZTam?%6xeq|%WYH{kf)_qvVdjyB z%g3-z1P7jITa$$Ch=R<&9&`OKy$vl$Kj>jt{oydVU|?X=f9ujw7D~DxMS;Y2G%QYos~ZH?5^|Ni%A7}6 z?P~pr|AJkn>+>?YveE^Lglb&nHK;=g+0xEflgn@bwG+v(m^NVecG`Q@p`>Qq0INl~ zvq#aDK#e@&BJuRwpGuo=!KlEp-U(_B$Em__1fJJ*0Srv(jw?uh?JnqgXHj+u} z)XJKR8i4qLf#NXwi*`w(XVB=(hYqcR@)<%Fg0lePuPjAQkY{(+*s7_%9dR3R5yu20 z#p^>2i-h+>2mvv8M6q|g=(Z*u==MGlYqVtd_WyVGDK?^p{5;26j~Pmtiq}Eiv$4X) ze(V^+-Zpyql630L6DFcd06n5^P$=41u`b1>r2U@ zLkjm$(h`FZ{5tqnZ|;oFACH>)RI^u5AFZe8^@?wE9<}JDzu*4#Ca&WuB-II*Ee)RA zv|`kPr6*g)4vFHp^@N#`C(omC;Rjf_V`WM8eE+M8MYjDQDoF$ZON z-k}KcKIK{aI^eo`qA-&EJ|QR_jg#-ct=l9bb4Ys~#XMtm3ZQ`HE$Xi~zf9R(pAV-b zk#C?qTE@$)taa&X&*E!nOWiY=tPyBh!eRo5^ISIpz! zkdo?JDd1G5lTK%=YPnV%dKbwBp~fF6C5nyqk;=naa)6SwYsxi<-AiAWoYebWIh_(+ z44rLdZ!B~!f2YSr~Q+zNzqd_$3z#*6ZwnwELj!N`dS+&^0y~+iY@OaT=gSH{JwH9=nuu`PlU+GZtVZVglP{EK7Dq?LuhJ5SwRCMN77>5 z!njzAqg}iJAdn6RpVJvSIJbu);PYYwnXE;>=}&Za%%cntRwEfDqG*~jW-n>uqyg)@Wm*x-mT6-_>{rD4wHXkz=!z;K@N zQD`U?-eS2YRNQAp_a6%{J{4?gxXZ0QOTF*w;oU8_voxPc$sCNyY#Ln^_y1nzWa|-Q zaJ7hX{4n)EVf)P0piPED+J5(T;HI~sKHjA;9IBfos;|rBpdm}2W7avvI8zp=px=I=%VY71I~%Pu9N{Zr^vhsdpjTh6}g(k&`F?tCh20=2Si}z?kv$ zDNSTy)I$T4=iuavTNTr0uH@V{NuSyX%Kv-WVPrOgYjtEgNWPI);bSO*0a*8_q5wFNrx};3@Yxk(7xrD zulvW^DtkYjg3d=cf^hntGK{ zQNVE~*8WOun3-2;0mG^c0tKqn59=+aXI~f4MkVE~xZ}+E8|2|tcd3$I-I)EsB%Lmk znffHByOH%CXAr615%^`x`Ld(5tp=|Ppq2XXsliDC1&TZrc%Rsa2BS#h*&>M;Wpa2q{efz=GZ zZ`k(KhRycc%_PNW-cpA`-fQ!t2G-UEC*&QK1v4|4KPJb;g|}M4TpVRymeFNZAP^;D zTe{i&$IVPA>Eo0udGP%rUKnX%JEi4{3au0l?V0;yYgqi*Mc^iHD)2d>nP;zUyprfS z$@hS_#3`S}XP@J!j9x7p4c zEc?51*i-tmLY~&IC$c-Vx=gV@L``nRQyWuh%F0387exo+OF6@PBe}Kld28U(e zbgPr1=G$sjWTTg9)|udTY;x7LIMh?eW&7GUYu~TG`Bri(1HNo-R}ppmXDmidJO2}_ z)QfHa88Dy@ud$|cl4l^ddo{E>z{ux}ZSVF)2#mU>_g&$$;U0FK7Hb+fbea846bD(q zXWt*Hn%_ciL;nlG9Y;mVhZ z5ITOd$xmEJACDncVxJ#rV>zkNtUyH7ggf0JBOVo##*!wO-5fs3B4-WlOL&jv$ge04 z@1-uKCUgDe_(MAK$p}a2uq+&o({`4wl|H57l>BpL?LcC;f(AQtECuN%RHsXdex6Oo z^Z6aScr9aSOM6@NZ+&-|twv666UJwa*fKVy-BhAiVCdpw(H#+*iVVf!timjo3AIy` zd7>3(L;7^@ksvzdIL>muL>6i7yY(3_)DbJ~*sUVw2w}MsCYcAqm_QF#;e#csCLf)tCy2^>&2~lK`r}h^dZ?7AH974t=5ZhM!;6>m3a0x zKhgGMe{y4WZV31S?#mtUVRYi@l+PB#=t`wh;=h+}Gs6Lg?S6u>@b#vf>=719tfFhi z#*^2UdyS7B<9|Ek;(-0nTl_Cv$W)X?QXk!=lVM$9 z2?d&@ofvOqT2wtW(S)xeIwvzHC!w(9Rd8OKcQABs4lVyxEn?q+N&OG@-uS7F#}Yl8 zx6fZ$PFnt=cjMOj=KGcD=TSX|Jp2Br-I`FiGjU$UEpO=76W`K^TO~%@O?>Jdx2O0u zR-7N-JNZTAZ$JnQPxsr_vaZ+@84IfU#o^y=N?z<|7Sd~(VacTctqh2rT z6L1u--n6Uk7;EY*jHu-=xaFg!#K!zdv2kTa@JD7zn()m*-O>&7za~8!&!(h{e0^f& zCj*L$Ms7^W+}c~9`ag2F@p%%G0?pc~A>+XS38N}6Q^2vfV`{;bL=B65qVd8?F_x=6;_7@axcZ7e2h6c<49uZ z%AdZLV1^S>W`d&j-`k-eB-AkwTcPxLvbJkSn4*fe%jho+pY=7KS4M2i%A@!-Dr9## zu>0caWWdl8pj6CI2n_x5bZ-opv{>2n=qGDGK#A?nBM~^S zn{DduA- z2_YxU{vG&76d`D!qo{hl?RkcQ0hMzUZV_Gz0z!coe;!ZRr{~uo^lx*hNs(OqT~V|; zei&dPHZ?_^Sg?U6y&R1pJE)#2I8g&NpQA*oq3c*IRgE_}f_u$3S2wD+!u#gewys1+ z=R3UgSn6u!TQ{X8!cwu$CpO6IhfDEQI|Vj^z5!D%EOGJxn|CCBg8#WJvB?DEt;T6J z)*E{tNr-c(SiO(!S)?wgQBrN_EqNaO?AlMBwx220KVJOPS$Sp{Z#pD@Q*I!%zV(p| zZO@g8XVRsuAA6fawc2J~mkWmuMjyyKQh1Cx%|};Je8!%~G``@gow(@BY5WR(7F>G5>saI-=OK8v%lmw zov$Ri0tl2I?0sM0o{nd}0h^ay~Bv#+<}Fk-BLgbrFWWD629h+yy*@|7myu|Ejy z*+b20zVN7($UVoR=z+Ksp|UD*IyAw@R#?w%d7B(@GV`@%pZCe%*47-6Ty2!Ym3O5gNrk7cFb0he@ek4NeM(3bcHJ*lQ z1Qb-ye7F7f%HlLCEFyH5g8K4LOumQT9=5ywF0zeL{6fdbb-M_ld;ZQ6Mnhu9Qe?0!C7K*&?6d9fpMKIDMHpm!-Jjb(_1(V7bNcjNdVfe$Gom=b z`R^dC2I4G)F`%cgj!1zp`85{bU2^R#20ie~jp3Ad42VK#wI}JG#YGPkNnfjOpJMZ# z+lfr;;9xtyH_+XQ|JzI>wo3#};C{vnKZTKe`4-bcQg!;HQ4g8Djn@j<^WUB9dH+SR z=VWBwxqTihpTCQHJkKa$-nLHEI_6a_8W8ZT$3f%qk&`KW9PZ`C!cAJQXXuuvQ`Pot z=p>6fZQd@lc_PkalH+q$bxTb(Kvj{Y@RrJ#$3m-YU6p&EZ7wNqOL^E~$J1(KGJKwT9`oq~lw^X;DilZL^K>!~ znU!B=&v7Ns|uCSvCkRFu(igH<8hK{D%FBkoxl3Eex- zef<-=YEPdQT5}t_NZ5H46(yjw##lZUONDZ%bAGqlC|b+WV<<4v7$H5Jdi%!_4vt+R z&4@sHBJJwbE^yveE-O3aE*S;ctZqI%e_A_V53VRw(cS?q3@xs-a?+^lX-d}DyjRQw>Z{)Wg zKB;_=a{r->uAJ)4;H-z77vC)-Dds@?+b5Nd<=-bpt~oLfXE`6eV{y%*(00~V z^ZZvotx3H*uBm)IW0YEYb>e?0=$E>d@2;-!YN47**NKnL_{3P0Gh zU!>CwiLLkB(_O7$23XqJ=d&4|{)avRmJMAnxV-OMtb~ZR>=S%=D2P5Fq#AMI!Zx2D zVmQS-CX8ggunU~$Oicb~{e$MF+4LC~Y4OOhgCUvS(@>^heZjC{6kgCeN}u@Q+6yPS z(!DrGN&VzLb)SaXiUFcqIGnP2rVgAri+uZw5ouDNbBvFOGGDM4*TgMp0N1olJ~|y)x&4i*N@%pGB<5p-$&@eZA)LKAue(kI-3hw|W;3U1SLtS7|6|Qm2S!95ZUMCD zX8125YsmKZM+=E~5ggQ%sNzNIuSYrv_h(#sH7+eFmDL)Q8dSZ>wbCz|d5aYsw4P-=8hB z?e0B4V!YPWsj9oD^z*WM{>#_vf3=J6*HcKi+B~oQ$FjxqNQ5W5DgSS>)8=em)$RXZ zKHt~edS7tuSN^3-SN}z2*@V+yY`a*qXngxqrT*!Xy6xSpDdMym^#7;2V}i6n{^(GX zmlK~NDg_u~FndUV{%gMJfSQ2qI3IMc&`SiBD_fqv`u|uu?|3Z#|LsSiRHRS{l}!}N z9+_F$BfDfoWv?hIp^RiFdxxZw5RzoCtoq2xN|dZ*b06pT{{7pduC8&O@AEZ|<9UcU z*@C)EpyVAEP6ed&X|*cHoY~mu*1A>U$^pBaLT+ytcyU4d7(U`T{wweTJxWAVR5p1@#?5T&PgWbgFP3THrHfQwgoYLX@=&d z`;{KokJbbQSY6uHPj!~g))7jS{|Pb7-yyn8l28!BivyZM{fh6bL3EuG8@q9o3f2h6 zoO_ohcB!0uVwmyd+l(h6I;#)&pZ_-SwG1&e1d@uujY1*VLX-Qb6)eARu67<(jk;Lk zK!dlsUChG@!#LxLX++^6xE|m=y$A zKXz+L$2!{=qu^O0FDaq~BHp6NXmHn9LK|Jrrw9jQX|(|d-Qh?1;Z3x6g3@`DpG+4` ztQcxle!XUC>S5|VZS{Pb&)+IrWzhZg#P*)(-UB%s-lB=ouTGq?e&KL!O#j)7Srf}@ z%@=iR*A16Sbtc6PudV0GY_0vNJ~SA<{V9CveyiiXX;xE(W&Ne;X}i>d^IK=a9QZ9; zBDi$VoOw3HOU)z`(VHh@qn zKYf`L7ZOG7hR(BscpJ$cnZ>>rlfi7?WtP7`I|GOjo!{n~pv$PD%-S5=`wdqH#=@#PaGl(@5j(tr(tj8oJN|i{a6J_vuQu}L{2;{Wp!BH8Ma+ti#Q!lS%c!96h|s&}O&eUR3P3OTE1i}BHIEeEdNo={+$B_dP zg)9M0cTN-}lqeuq>vaj&S4r_TJEzoJw;N<-2WP$-O!{XeZ(CQDM28&uo9Z}g-Qd3V zBhB%A;`X?dQQ`WEr$cd4@*lsX#;Rv;MYi+QbrtG$Qu{3w{w(R)FZ%yVj?R91qi0j; z(Xx9%ibLtvf}K!$>i>2{Hu9`zycdRU``WHl`JMUpbmT&At&?6G_iyrDnAN{N$<`u- z5FkLZlrxXR84%fXp-ZJ~RCTXjlpZ%fpL*FS7X3)g=n0#0sn_qi>TIO{eQ^!Kd+n0; zbElsP)!JsN?zg2^!ff@=f-Br37{rI17+YF_E%(!C%_q;qWCR2QAeP%58I|#t*bKMk zG|T`5=JAL&wp>F^DEet!4kg2U>npqs<3|9%XniQ#!`*oQ9?11N*hL-OmtQl3fB`w} z{=MWxrev7%P9z7MWW+VWwZ^6z^)2UICADiZ$xp6~W|#XPkN<`DxKJSSw@&zF9eD6n z828Z9_3<60DzNb|k+0#kA;A_0AB0T@B&=B~%aq@1MB|ahWjW>QB8FDzShDH|~JqNmW%9 z#tTPlR6O$XZ^^#u96fAmaEX<}!tgo83>SSU$D7|JIukN_v}CHZIy;OX9p7y=!!5s* ze87M@a;$mJrz#U`G9?nZFw3skzDA|v>GRq#&GVw;V)EVU{81po8elD(e+LsW*zJt79l%lvt7uR)Y$%>o8j(SP2H+Y%e&=c6rJ)gu9 zyN62a-!1w(Po7&aWN`QXwCT02D?7lqn>5GU`KChuv$lU6EwR4mp2t0SUgJhLo5a04 z|0UyYD+Jf2QT{aes&Xqn-GYn@{nEG2{S9TeO{hQ)C04dZ#$jqRm1kSzPcL5UM#M2K0wmwMp3Gtz}xAIJxlB( z%j*Xx&#^Nq(UK{Su$tT%^|eSgefZ?40&v@MVZ$7`PG$rdI`tHcQgTsZGFQ?_xdjnJfv-u|Lp*?Ee257`#yqCA)E}y4ThGi}XsqZBf2#liH|M-nb#uVK^G2J0CP9-9Oo&dGjcj!vCEM zhnX9gvEj%51w+AF+u~PH7@>mfycp9Jr3?2oc|b~Q>&Y{qzf5IeGL7;JVIEO2_hk>H zy?OKc`CVjF?l=>rsA|6_(m5t6>GwN{`|J-5@ zU@M18b%$J_@6yfQlfM}Y>BUphE8*qAmJy^nZR=Q7y$(@>2l-(E+#X_RTpnbc!->sV zyhq89o1dp41B+WHClD1rO9_ZHA{-y1?Nwrpm3Z~a8d@q|p zy}rlwnjMCrfp{j{JbHbf?)esd&(gC-Av|$0=n|ot`L$R_c3WLH_N?>4I8r%&R=snN zn#RvG9|wkUalKdiNGJjpaTF^SeFg5dXOnj~Mq>c$>|P;t&@k+JCDXH1sHpl;Plx0l zV+T0*5Abu203yBPvGuFRktswDC>@={5Gco5z|*soG6mPt9JcC>`~SesMwghEJw3EI zO*L~5sp@UMqf>nN*wSMer5Lws*dZ62*e@+HZ_c2&6^P#(p7Y}@%ZC3Z!I zF3G8(>{g-XgWA?1q#2`rinIqP*0`IscahOj+#9_let>RrEM<2{$h~{Mr;{}L3bJoV zbcy(;EJs9V*+1>0A2J%=kV%z~aw~G{xHkCJrW6lURZPkHioK{s3fd&!ck;*%o3qBZ zgtyr$6&glob&ve>hW>E;$9Pxi?9@h;ohVgBt*jI2#gayk-K12xhhlY=Y%Ms+6-s91 zK6&;|7HxBrZ!j^x(yNkBb3cEeGKI1P6I>PFSAF+2a}{!Q?=~DdcQZJj?N*hL+=t)2 zAK+*jb@9P4Zb&@g;E#Os?q^;!ssU!&pD8N_@Af*(Q}R^Kp4{y{Kt$s-HK|$@`rP-` zvKYB=;|9N~s)W<`!6OYBs;$9y`mB^QzW~d4-q@IjGB9d-+F^Qn`rI9?N$pwZQqHmz z)y7sv`o-oVh!qx+8n^QIkvi{#AVO*zNa! zg3m>mT>E4fC6;B2d-t#(pxkOxT&sq_`M4d`WrTRc%8Gg9nSDVHM?EQoZCft^C}8G2 zq69G&FJA%Jr~CwSG=kskfqZ&e&RhEUmO4zPnt}@qUA9&y^$a*}aN~V!u2FwqV|sz^ zord>VoCc>htbYxUm~X<3OoRZDlJ@rYCUyOyW%0d!yd_Q=c&EKk&PHY!QG6(^VU_fW zfro?ezQE-tlBlS0^b}9}to`lsEA{)5G${nU?!1)SmC9H}ylaJ^myoPK!Q#-h{Ag(3 z$Ii?Kv0u-Np8&Z1Oh1YzDqI4xCI1k zv1&yBrG}0y;QZf9uS_1duq)Sm-*5QyN{qED15j6@n`_INId9`6q9fVO4-$AJr1*~h z9;wZHo=^GvF8OV6dFRUp35*O-WYZ469=A+DXygnFf$L&h>7_GYbj+_1UJQ=V!^L>J z46gMI*g;-;F5H>vOHI&sSxn{*xF#H}s6CusIscIR1mY)}iSPC7S2Km1H^oH?_80Vq zbdO}^em}4YF*i&4y>u*1`EH?9jvQShhvZ%bQ(3t!`R0 zAH^PI&`bV9W^M6v2gKM&A{*3FU{(<;G3h~}k}PsdAwS?qTg^HG13a|g!3Arl%+ zq9FhYPrsQsT)UP4D_DWy?MB#Wo)r{;*PI0V_RG;#y2{lrrb#~2_JqX_xR=PiNMOSd z5Dm`e?XhbC9UD5F#J|W+^SDMyN%`#g^G^W;Hy~Nk>jxYKKd^z%^euHfDr?aU<^VXN zf{3%6#CB*DTV!HnMmW}x5g!epeLTiku(+oMbJxW;SC8lZomPtd+VZ5$vDP}O zzA*n?2n}2N<%JlYo}M1)WI_Jc-7YihVF({u7hPhhzb+!FmK_CRBsOM;^8_lG6We~w zSlXw=#>eXRWbf6IT4e{T0=tMJo|Hq0y+M7}|2in-9v_K&Ct+VLoT!0uK1?51Dna|- z$0GG4q*-lOLKt$ce3H7Nxl+87dSXCZE%2AQc%!RA)#P}MzyGthFQ`iU51&##aWx`7 z#<_*R6cjz$=9VS+La{QzS0CJcSA(-*h9fAMii^IWw`0fJlb7$_NmPqxb(DDDFlROl zB_k=?NlDuMrHC%Rzv4yZi>%FGy;kWjUTC_W=WpC-P=jonI1n}r#;`cSN-i2(ahJ+* zG0j=VxTalZ0;2U2nZx5WA4VQ)$b?%=(%#ePf}+HeidCi+D65W74-bi3_#`DdzKaBu zLD*&VLQn6Pn_o*czERv-4~@4+qt~_JERiR8#955p_A>}4rl!A!JIGEQGC+o2UT#V= zFn3k0514XWrUos4i~Xk3)$0aJkQ3_Vm|rc~>$XBQPVXF`oufE1^h8c1F+4*+sd0ib zOgY-Am`b97{ccq4pAWXBSI#6b{yv2$0-;V1iC-OQ2b{|Xo`i9-fFHKy)@So`iB8|p zpcB$$OhWuOW@EQI5l#JfjS|&CckXcec~<{V24nTaZPaufc9J=4|GE(Jg(}G8Q%ruB z?|T9N9}M-6nAIIj7#k3=-CH1vWb@1INl1|VdF7eD!?!n%ZD)-v70k_BED`A1A#@i_7IFAFp#L3-+(g3llBn_M;bm zwzSl~ZOcs-JhCq;!RxspxwF+wYo*HRV|zA!9rKa?RW>LfK~L|BCr6HERrvn~#lmj0 zsV!Nm(S#YmZBpjL2a=tq6T+pIdi1zy)k5C6kG%Jt&CbscAtDB$hLNc5q=lZ>Hf4j3KFd?JN#bs&kL)u8@@yZINDO2_ z>T=uT3*=E&G)>Q2E}cG&6Me{cnc1;wMnt!}3qN>N507|411xD-=&sB|BJ{*uj%_3@V(9t z>0bLcG3Ye}q(I<4##=$-7XMwh-P%|sd`(!V#ggr1$$+KVkF%)e2`Scq{f-skI`QU& z!&jSoLf^o!>xH=~2q!wYNXQk=Y6gb}vs#>s&p3fyNITG>YG}`BcERJ&@dQXR7}$PB zx%)jd=S~iK)vi+NI4Cw2CcLutD;!scNvS;|nvenE>X0fkKK|qK=_4yX}& zmz%DO`-(l>SjmR0(4>U0m+1S>9`60}C0(DFhnqXit|R5|%vb8@CPjSttE;O7l|Fyk z!Rh-shTb3ta;hWeQe|$hIMkBkkx4>QsHvX&DEagk)%(gQ*-4{fsO8SZXVp%4Vn}*C zLTceOem?p5nEOrFN8Y@@JvC4+Y(Hhs&cUT;WE9LTZ(u-&1|%DohrXfVh-jriDgnN~V_%3ELwIx&v{Nc0)}t&WpLv9atXrVucU3&bD;ERk-7EYJ9|? zEdYE&nO82Krmc@W@E;5TFrBOolumOz=v*wz2WU?vZiAmjEJ8Olx0dluDIF zhtgqlMLD@^U=@Ow$PsSo;~)VdP9~nrfK|ZL3WqNPyE^5 z!!P~LvE4tHCN?Hzw~upiC4#}A!;6?9HnxB-H@<0`n+J4HL9xUa^pug9iq*ZJEG568)vHe~lq9KcDxF34fLEQoZ#V+(T74mqkn_N=GZ*I>sg~ z{m9RyYh!`|sI;(VE^P}phj<{>jO3*f<0cjLOs&+R#&Orb6BAiE*_~DAsZq!}i~SZm zs_F1+`lm-T5B^xkz<@_^$sqRsav`Rt!@80MDxeZlo0Yl!nS(<|PcL-Gk^F+D_V%3M zQZv@#OF|3;iq|gXCMT1KgVFYgN)!;%kJQ$G30k19czX zxuct&_3WAv>_7mwL-KxXH*DuY&`$|Ee?K=MAbL$^^*&g(u7AWQeG>Rin`%H~zxMY} zfLdWr{%gu|c{#xKvMv=09L`PlWCSI;=gXH-_j0kqS$l3;%Gq~Ocew?Pt~7*azkBzz zHkfM6Rs5y#4U`{w+y<1*OA|nLfR}guU?{|t6SaaHmB)Ui>xzBRe+2fe)8nse?`Q2bVt(ACSm!{I^7|f+Ah0I zsws^_-*?&kz%F!T#;>gf=|ky)Yily<_P+Y|Tq8v)q2DRBeYSeD=68iHQMH|<7dG|V zj71IS246QM#*9xm*4+Q3x2LE$mk+{Ujq_g)7*#_&!J_nZAlXGLo!oBXxjAJnfA!Ptn@oE~H9yXT-QQzKx-GFXOS;=N zNr0ci*sS^_|g72;yUH}B~@8v>|N+8sD_jq}8!+8B&!ai)$n)@KOLDvSSy zi$L{;D?-d4A~)jK7fW%UzefnacS%VJKt65=Uo9$Se|WdKy@^v?Jb{AcNdsOVhsO?m zBcsJ0y%qv`Qf%8H&uR4CRrQs?^r@>M+;nPDQSMT!pQ4F?p@Eg*ZP&#tRSUX`u=UBI zj?cwG8J!N1P1ZeMdb)DVv2p49gt(0+{?Y+?=%scb`F8?hxaS++ck&kGH#W1hkJg0} znBxSI8Be(D91gJCkAzGbI(L6!LiUO*t_z&Q52=k_jN;QPW*Q#fEkvtEmtdT53;&2n zgAP05LbbYD=q$l;Z@{Vc)NX{oy45o%JdwUeA0a!x3kM1Abi>5(z`ci~lA@ZuhW&%h za?WKMVg^z}!6*BDuyl9=Z48L!V}Om2&Z&>lC~o^PJ(E22Sc47qFM(4oTRDO+JlCLr z6mOAHbE@x9K^wu;Ns#rIp#TIGsPyPJLLMGO5swosa?e-G4mg-~8Yga{BP9Nm;9x!x~quvB{W~9}G8C2a!X3B1Z4Y@&_q1M^Tf)M-;3+k0JEBT-ry#k&UbUqz`co zefsoJlr6gHVzF&_YK_TjZ$ZuYP)zw>`7E8^5yp5n1kZe`K>3O)VS9+9(!ub3L_{Y$^gT<|IwNC0=*W{b25ja0#ac_Z_{9hFP%l2TRI8M<>WjKK>8 ztsd|>pMF>Tu^Jw#DjN_Wg0kfN`2JK)vLM@|{O8Bei)es}9-5;e!tLLKVziKooY5|I zKl$b!u%z7lXN(770}$m5{!89~21F=ljy#h5a|vI#)M+Oc1@hFpbq+rQ4_gp9pc;vu zl4yd6iVeqS5E-?s{BXBs-DNLl*!*@l1N0xAknm7Go%oF=$|nuAjf`6UG6;*gH68$k z?jzyLe>Br?{O@n~_{1d0R+`~8AN&P2sV#3W(KSTGqf51OhJSk0U7Y8)b|azai`J3} zth2IRe|rN0nMIfYUtaCBLil`bx7Ml5YhNAzyKYEmitdr^WuGK9OysoB->U?pB(BiZdvu=bbNnv` zm-gK`9Zw$BT~Ak!KM{A*94OApmdC;!)@w7R(SV#=`ft{OVuvyk%!3?_?Vls2&X9>F5r48DRIDjn+9#LzYeHayfz+#eOF+b2Ob{5V5%b2^V})p-IGt4TAI;LuCd!bf?r>?%zjPg84^zrc*7`!Q+h z@Z1!i5CqH#;yB0KC#X@r%QF+{6s+nVISyPLcjejr3{v4g1Lf|&zJ*I%VUCbqI|9h@B~Zs`J*Mu}j-;Jm z{xje^ycxqH+m2KoML!D`JKS?HgwPS;o4r@ibrSPrtty!XIQcU1yRo8+pdb<&V& zc|bV$w(l~KWM#v@dgoyeu|_+T)Kzhqy7e}))AgaVZylick)d1gMCP_cVBk!`nJIcO zj2gSOWW#%T6J}ObjIrW0G^8hnFQ}gh$PB(R7hIjM!3pQ(<0Ck{1n(F7tN{cGAb>qQ zYYFCMH0rjCas0Z@|NXw+rK=aY$CY4)(^8UY$tz)?$vN zit`aHP_R33vauzfs@@0z5)Z`m!v_zUng(OsDG(hzLMH`1JV9Y!{EnDDkSU7o7j0eL zTsL?~7x&0jzu9QZuUENx?%>L@`~e#26GG+tN=z2Mg@^bRFYp>PNarayU!;G6S#DZ26I##j0~mlc9tHsxXmZK)KA&AL2Lh$En@DD=$1m& zV8t5`OhsPaT(ku(Rh59=o4frXDA6@Do{Mv}5pxr3r-14GaIG&uE&J1i^V~v95&l>< zt=nEFU;pPD6XkuZH5U#!S+zYqOHfRZtuc@PR8LSBuV5ysO2OQyWRFC;%GnR@$$LfT- zu&L5wx323Itd(XV8=HRP|pWf*lX6EpkfAiDKdT+MEK8=@!HoAao+NskJ#P zKR=3Sa?EYM_dC5O(HX}RGsCFEBJBOGGXPl&D&-su>gFh&oCT+FhFR2kf#%IVstp#{k=K!QZvE8NahEMkoinKxk2K@y3b6t0S5 zyN-H?64!Z-c;p*2_&b%|B|ux>6{uYnKRpCsu@hc}9>)EpLq2?(!O<>P8qjA1G+iCd zpEr61Q2f@`y3@C}Hv(!deFxv>e{6Mh1c0qHV%P$e05liQ{e`6q5W{wrn2P4}{_h~d z5Kr*R3;1rd4NaQWPgUQqv$6q29xVcpK4;&r{$L^=qi_11pct;Oho86qty3m0o0G<` zcSZLpVqivJoG?<`fh-k4Vk&ZLy-l=n7XxqXIHv>$2i=YmH6=9U#1#Vb{QS`R?CNY* zl4~=MRK*4Zh#Q~wi*CfDR1Q&B6Pt)hsoeT@ZAeUv)n%#I$Zc(Evutg;0G5~GBd0Iz z$l%sBFs?)Wirt$v>>fj#C&S?YfQDK(PImK*=&)4e$B$H%+e5y9#KfS)f{88!;y$pE z(KNlfIwDVKpE08AbU33?ImHPSKJY=8Hdf||Fa>x}a0f6J*0b*A#=oF8^!tFHMtr8H z8)G`LBq|87riKb7sb$1cBVJwh%}^;fsKr8%<2#>~4W zkkC3^ykThVDvsOz3Z^4wzB486vqJ$WGEyL3neVf^#&C0MwGvbcjbtf0#mI8ES-Z_O zk1%H8Ah&kuc&J~xVGwAS^kRo2VluP0V5)DCR*?ozd(8{scN>6XZ_?uw_MSV39fIhP zJKM1TGCy>1udQ`qj{^7_63UNwO0H~{5&>Rf<_O!>J>v4O4@K+Y9b4;;AW0n(9d1k)coCVjQDy#~EuOpXFGE zb(bIl4G@EI<;(8`Gw;qo_t{S7#NOtj>dodwz7)I01UsR%g%)w|xno-VruAE(mrVs8 zHvZS6=dDQ1Tht&M{B>tq$FFBCFI?fn@RaDKlk_m!50ou7qRGm1R#xYJ&)p7m*x=;q7_I zfMK~KXW#!9tUaWOfu)hI%d5jpN8-hyz$VU-U|!+1_;K$kOkidXbJpLBPTQeMOi_Km z7qcxF9!MRqPF`)Dws>Z7Jtf) zH~Sc)8eN8{3qqLPj#`!4B=fJ_O^^EOrjn)#xad1s{9Tron>lskqi{%6a1Jw4Nw-m!);&+^8`#yO{_ zr>ZuDFdc*mcOG}Q3)Xphzp9@7u3+anVfE^p^E)#l0T>sIu-liDLDa08cw8Rh(!`LM zzGCpW_`W$Gh3PLQuCk22psg^>E2Mc z4yGpJtpVwUILiU&+WklJ_tyvqX`kGE?_8h-l)~oNLVib25}8E(K>NzG%P?7$T;=BkjcsZk4xF7{$x z5_bt^Hs!EOIBbx;hs6t_#~fT9Caz>6@C*(5X)vEb5l03BUG>I>aE0e=4lG%K?!NMz zF$oC`C7@sU?=U8^*UZi#9#~9H?|JHUU-dEEuDc4oHrS}_($dV~hAlXxS8$_UAF524 z>3`daL~EIq?>jN9`BSxQ;oxxX;_65+q;mM*T*R1vf2#_`6}8sJ<+t+3m$)u)UkCM1 z^2R0_&N0thO# z*eZ;x=XiWmC5ywYWfp5}UWk_M{pR|AuWnNSC^H^o?YBB%CvttDaXLaK1709J8+8nf zAqB`4cK&<}T)y4sATO-G{*3_7Vk)q|A%gXMq2ZXjID@3;Nnn1h@H~N8I4W8XLj=|p zM0s4?>^zU~fKDhJ@ts1S2~Z13ao;|b!|v7VTfPK~^8C=7!u~fTG}w>BPd5SkL>BZU z;mW_fCT@6%ZG7u(4xvQDsSuuCZ|Uf&DDK7o0xg zumY>y3S5T7m!kTP{f)zZvO?4D0xzd(^Y@ws6OAKmAUi%Hyd&t~q9DoebCj`sVXEcg zB53Ki#6cNIT5fiCaAdB6Ow=c!=g0WCwt+z%zH$sHx7(2x9d1&Xlk?>R=wBCKTkk>h zE}yAG6pMc<-8k{&w)=30WN4jU0K*XmfcY$M%?EDFm^6H(y0tvx1uevB-+$MMI5<46 zTao#=3OW$%SbXr15oaeZ#^8T5#KRLgzR!B1nA~rdv*ZpoVWs|~u`~17J>BCCBaaa} zmn2|~P@CKvFH&8gDn@6UK`CCq{0NfnFf_*Ahp5GfKFqOvmHxSfFn_Y9tBq4h(EG|6 z8l@ODj;5g}`tR6zRtn_>sKzCXN)UA*M8!A^1k%hd5Tqs1hmaA|5op1Qb#|ID9^UV!6g5nK%(tlOHqL~7$ zbtPZT&tt5^Eb#`Dve)S5h|-WaDjZR1fYR+UK6e?L#I~v1TDfUybi{OCyj&bz(qoar zer+^sX>tMh{6pR>`{UpAU02HOMx~gFnP2<0qW8+FLshT=5(DmTP3c%eGP0zh(o`w{3Id*M4NcX82OOn9h_=tB&ss5{ERTi`ReYlZ><|`yD zGzD_k>dn>0U3aUuhidUT6mg}NIShzB_vBk&o<)X9;;$FgDd^7$?lQQXWGeAI6aWKZ z`c61qDxW9e7+_5{Y?f8og8ulR&0YY<@uNR(tAUm9- zwftwW@BI}?sCfvF=ilFb(eq&?W2r~(6V zAHOj<+b7<)gCN>R#eD<;cM9JqQD&i6dE@jo1^q;HWQv^U+fz3l4evu9X$#iWx61vb zZ{A#mY*5eCwB>*Ks)z=BeU-+4Yl+M9#!3kMrY=h>^Mn#4mi3lOFjXLGG+C19Xr#(IG+@Q&7+W)KiCU9`-7f7(+dmbi3uJ39<&dE`J zr#M_iapT+D2_)Lveom_)rgab^1f@zRIlbSCi}-r0b(AA?N}Iba7bBBz_!C!#Fq zRmQidqIzKjoQWUU7T7h%n!!zi^e)g2{x-uu-k95G~J7eDt&>HOfe$ZkgTw5p_z~SLv98|b;82qO zNqz@nd*k(dmgEb+ATZBOu8fMhDO*I^39jAKNi zL}nz(PV!^%E#mGgkGG{siOUaV>BsQx%~M_jE)>`(*&CZ zkGdy5PSCY4{pm(hZhu#WmQ%IlFXHnr)fe@ZxN*f>`0P=Rj;tJz!mQVOFM;vk|RS$y_+9KYSuIeK56EU>$&SU;g)>xwuKh@V? zOsQVS|9ceeynR$Gr=qjq2MH*hZ(h+wq_Tn}B8v(?^0z zKx{4+(EH5)%#4`-?_&eRLsEag2mJS21g5DPSS|pDBP`Ps+3^;mpbw8D#0Q`u9V1h9 zV|yNK0JC4de0g`T2~A}6)<(1v^ni#pf{k@?Qu+aACIuLcuZ2mL&E4C(`m5+h<;rMi zl&?;%zS632uy4~2vGr9XdzR+V!e0y!dy(WJ$M?T~eu zxxhH3IA?Lf5;hXZ?Wqs12$`*3e}pK z?FM0mZ%yJ+T_%QVh{Cy}mYqQuDCcU_u;HebT{zwE_51sjjh0(S)$-|U|4zcjT2tJT z8rid9M?JZ=M^sx|`xu$kKEVh~5sAVDebr(kTg2nIr$H?f%CypE2yxn>QMTnH#S%%( zey6v$&SP`!y;<@#?^(~{%9#2q6Mm`Ho@io@ip^df22t(MJ~bh3BJ|W3!Al=@5Sh8e zk_wr0lNi<8;%{JPqycMHQ&T$uFV97l_|?LHKN1!oA<=-6MoYE|wC%V*cL2BMJ1XP; z0Q1Rc+;*?5n+{HgDdT6^^8xK^6C_z>-|y{R!HbCB`gt0+8pkQw)MZ)k2$U^NW7L

F$Z0n)=tU9oTx2hXC%D9~l$d)dDT=Nupo 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 diff --git a/.backup/en/public/chrome-512.png b/.backup/en/public/chrome-512.png deleted file mode 100644 index 2641bfb4ee60f0165b52965fe4786d7772666b08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11998 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 qcA#}Pwe`|O^c3x@mbgM-VSJ`3ohh7TJoUE4I7 zZ{7HNZT~2elU}5n;v8~zbdb~Gpti;;YA8A-bkU{FQSht2@wXPf?7a>A#t+`e+g_(8 za|Ll>9XiAr&K-7Yt7RG?G*2ki31KAMz&2I}igtdzZ>HH46E)U=|1H-;X5A! z!k_p8)yNk_-yC-|@zK6Ibi>)6=A?!>8W&CFr;~&qm`gk#gdJ|_*MI!rdcq8`r%TLkU)|a!ipr8+BJ=fpF%HjZ?bca7eff=_$3y(IROv*& zwU>^e&s*%zZNRY##6rWt_eh)aa;LuM=X5*Xq>BCTf*-H?wt?=tqw5gYY~(;(G937; zj}QFEKbdrgJ|NqTR&qKWA_t3ggxjycj`&n_Ig{FNG?T-2gPO|nsC@g}Tku7bA!7rz z)EdZ+cFl$>h&4;R@o_xZxqz(oW?`%Ezlu|Gyn8HVKj_!`2R6~4P9TSqsJ+!f6?-|p zVScK8;#{A(iR{SBo~1f}*G>G~9`VLnQ|Z}+8cT9WlQ_epFU*5aU!%tFHKJYB*<_MA zpQ~e`4#fWYtR&=J66*%`zbAef+%e?tA*kn7>aU!<$uq{kZz;95SiRb`n6;$!_=PTqR#CIH$&Jn4-cH7o8*jD)E;;pes;y9R2sh+4NKN4p z#4?Xq06lg4mr+}bMd9l45Bq>Wld0lVJowW=cIJ=Bj~@JiPvwLd(q+a1UlBD_mXI+! zP0VNSI0PL&e-H8HTku0m=E4(VP3sxsu?Hbj{%1nBwno@0ef~`wxHuFDe>4Z)ryH%! z)PePiDdQs_n=n6lY&qo>vDV;N?v35`K7;*$IK>>76Q%y^1qZ}Db~@Ur`kPeP2zy8`E_mSVj5{1hu_mawn&lO59Y`(~#C$_0VwT7U zU2`x{{EV|~+gz&F6be5(Y^_wCw}Z6FFLugPZ{9Fp6uZ}6IfdBh#6G8kEM^^-U}WNyh%o zAC7m1TfNrj_*FePkBWK7^H&wuJnk_k_Yim8V|e6W*Zafko1-L1IP^QO>KM#5+yD6A zCgz3>2zm79!-qm`?{YZWSBj|M&7|kKq0J@L@v+N+rN=Or9#?{zkP;FxO>x zcZP40Yk*&TPsO^Iv9tUE(iqIx`5WGq4UzhlGsjFMDuN1s zU#)s>6<`m-cXffE55TO-eKNX*;_~N5-BXNjMxJHHQzKOTT>k{J4q^-v zJ)C7*qDsJvwR2sSj{!cYH|Gkp|32U+Vd=T R2s@W^jbA>raUCv6{{yx2ON;;j diff --git a/.backup/en/public/langbot-logo-0.5x.png b/.backup/en/public/langbot-logo-0.5x.png deleted file mode 100644 index d5526e0a5b0e9efc887a8bc8011db0f01bbd4be2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11949 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 R0{U~1ilPRxO5QBw{{hn4Ngn_J diff --git a/.backup/en/public/langbot-logo-block.png b/.backup/en/public/langbot-logo-block.png deleted file mode 100644 index b2caca7b6b950b6fb2b79d71d1bbe20576c2c906..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14953 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 WM;=@(*}%7>bk-Jj=8dMlDgOfo2LuHG diff --git a/.backup/en/public/langbot-logo-final.png b/.backup/en/public/langbot-logo-final.png deleted file mode 100644 index 9c99c5f04efb7a3261669a756ad45ad7e03ed335..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14431 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 diff --git a/.backup/en/public/langbot-logo.png b/.backup/en/public/langbot-logo.png deleted file mode 100644 index 567a2e4fcfd12d8ed6ca25ded209e56f1c51040c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24667 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 KQ5D)Z9{dk=rMwUT diff --git a/.backup/en/public/social.png b/.backup/en/public/social.png deleted file mode 100644 index 7e351174747750c040626e06bb695282f08e53bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160443 zcmeFZXH-*L*C@Qff)&oOfJ(CjsWwEUsfhGqp+f@FYoZ)QK}117q$vmpNRbwL zq9~CrM2HZQfb^2k6GBLGcY=C|&-;z>{`$ta<34NTcy`!(%|7RvbFP*B=(2(K)=hgh z0RXU7=i>RR03ZNfuJhZl9{iuE${`{E>;-hrpSk9jG>Lb5O!BuTQro(ni4c77L~RXq zUZ$ma{@p(}j@6~@eyqOsTw&hDCr=A^ocl-TuSf5`G=?6!s(10-Nr|}QXMJwdb*^7{ zcI(vjhg%Hb5=v`DcG<$m5>isgIGh!oU=hoQa6TElgiOg8zt%Z%P&AoEV}-DayMh;M zi3WX+W6j-j%{UhxkgDH9#J5U&x|Dxb-E+%^?FkN;MS9X_VmVCt_XF$;ZM4h}oReDC zp%+)rUKnnXfW>IrsT9I|xgKh1QF)#mb{KinmT>0bmm9)VsJnIM8bdnqDJ*97gD4#Z z65wMkIsb$%a<6Z@f$NtWyGEwSMp7_pXZVV|v8Db5|EPxv zp1kKX$M&WXGwHb${S$j2^dQVI@#yLaN2B;p=p|F06LV_xQ?p3aed{&)KZWYmz1vSv zqBl`QypF0xPVkS@R?h+WJ4)7cn&~YiD~u-l1O@#?PdIn)Y`tRBsBhedsW-3PF*Qs# zE)_Hr&gj$g0-uC#x(9Ok&&6kDnWBwl-4-PdNqxS(7t0$;F}3`wQNT4lb{WAUx*R<8 zG3v#HCXv4mc6_>_g>)Ohhi>`@M^2=au6`Vl`Yh4!jBydzKb_p=A~J8y|7gRrBgGqF z4x$VL7ii3a5`EcX&FUF1dKI!p%eK1X*4BpYT(kLZ)W$3OwKCOtd|$RhF%NyCkNWje zJ3gIZB+fyFSI;}Sfp6!tCvz%xSiNmHrLDneya{zlzssRCYK_FU+uzSTKw_SRptpvi z{WaYv&2;2@e;91_eAEZkO=m_G_IE4IkR)DDy}N02kxMna`7kD7bO)W#ic{DC5#DBaV7(x(`1Z4PF-|ETiAwELCHIIP z_Xcx<-Kfn!%vVo^r$eIqyppE9qx*b1_WJEagU7J6w|<XEK%k@xdiCTMAAm4kuu0j$`eHSe;qlktX2^amYpO6IC{)0A*Aq4Zd=sa_7!gMYdrq|jjsL& zIq<2*4vQ5vTr&4|(TZwnRkYH43-e^UaS4U5|JBDfC%$*y9@B5yr=|x$ttr^#HhA07 z>&>8*Ve8M|(