diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml new file mode 100644 index 000000000..7816d4393 --- /dev/null +++ b/.github/workflows/build-docs.yml @@ -0,0 +1,43 @@ +name: release + +on: + push: + branches: ["master"] + paths: + - "docs/**" + +jobs: + build: + runs-on: ubuntu-latest # 运行环境 + steps: + - name: checkout + uses: actions/checkout@master + - name: nodejs installation + uses: actions/setup-node@v2 + with: + node-version: "18" + - name: npm install + run: npm add -D vitepress + working-directory: './docs' # working-directory 指定 shell 命令运行目录 + - name: npm run build + run: npm run docs:build + working-directory: './docs' + - name: scp + uses: appleboy/scp-action@master + with: + host: ${{ secrets.HOST_NEKO }} + username: ${{ secrets.USERNAME }} + password: ${{ secrets.PASSWORDNEKO }} + source: 'docs/.vitepress/dist/*' + target: '/tmp/' + - name: script + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.HOST_NEKO }} + username: ${{ secrets.USERNAME }} + password: ${{ secrets.PASSWORDNEKO }} + script: | + mkdir -p /root/docker_data/caddy/caddy_data/static_site/abv4/ + rm -rf /root/docker_data/caddy/caddy_data/static_site/abv4/* + mv /tmp/docs/.vitepress/dist/* /root/docker_data/caddy/caddy_data/static_site/abv4/ + rm -rf /tmp/docs/ diff --git a/.github/workflows/sync-wiki.yml b/.github/workflows/sync-wiki.yml new file mode 100644 index 000000000..67797135e --- /dev/null +++ b/.github/workflows/sync-wiki.yml @@ -0,0 +1,68 @@ +name: sync wiki + +on: + workflow_dispatch: + push: + branches: + - master + paths: + - '.github/workflows/sync-wiki.yml' + - 'docs/scripts/sync_docs_to_wiki.py' + - 'docs/tests/test_sync_docs_to_wiki.py' + - 'docs/zh/**' + - 'docs/en/**' + +concurrency: + group: sync-wiki-${{ github.ref }} + cancel-in-progress: true + +jobs: + sync: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Validate manual ref + if: github.event_name == 'workflow_dispatch' && github.ref != 'refs/heads/master' + run: | + echo "This workflow only publishes from refs/heads/master. Re-run it from the master branch." + exit 1 + + - name: Check out docs repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Run sync unit tests + working-directory: docs + run: python -m unittest discover -s tests -p 'test_sync_docs_to_wiki.py' -v + + - name: Validate internal doc links + run: python docs/scripts/sync_docs_to_wiki.py --source-root docs --check-links-only + + - name: Clone AstrBot wiki + env: + WIKI_TOKEN: ${{ secrets.ASTRBOT_WIKI_TOKEN }} + run: | + test -n "$WIKI_TOKEN" + git clone "https://x-access-token:${WIKI_TOKEN}@github.com/AstrBotDevs/AstrBot.wiki.git" wiki + + - name: Generate wiki pages + run: python docs/scripts/sync_docs_to_wiki.py --source-root docs --wiki-root wiki + + - name: Commit and push wiki changes + working-directory: wiki + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add . + if git diff --cached --quiet; then + echo "No wiki changes to push" + exit 0 + fi + git commit -m "docs: sync wiki from AstrBot-1/docs" + git push diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..3562259c0 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,6 @@ +__pycache__/ +venv/ +.DS_Store +node_modules/ +.vitepress/cache +*dist diff --git a/docs/.vitepress/config.mjs b/docs/.vitepress/config.mjs new file mode 100644 index 000000000..2d4895048 --- /dev/null +++ b/docs/.vitepress/config.mjs @@ -0,0 +1,530 @@ +import { defineConfig } from "vitepress"; +import { head } from "./config/head"; + +// https://vitepress.dev/reference/site-config +export default defineConfig({ + title: "AstrBot", + description: "AstrBot", + head: head, + + rewrites: { + 'zh/:rest*': ':rest*' + }, + + sitemap: { + hostname: "https://docs.astrbot.app", + }, + + lastUpdated: true, + ignoreDeadLinks: true, + + locales: { + root: { + label: "简体中文", + lang: "zh-Hans", + themeConfig: { + nav: [ + { text: "主页", link: "https://astrbot.app" }, + { text: "博客", link: "https://blog.astrbot.app" }, + { text: "路线图", link: "https://astrbot.featurebase.app/roadmap" }, + { text: "HTTP API", link: "https://docs.astrbot.app/scalar.html" }, + ], + sidebar: [ + { + text: "简介", + items: [ + { text: "关于 AstrBot", link: "/what-is-astrbot" }, + { text: "社区", link: "/community" }, + { text: "常见问题", link: "/faq" }, + ], + }, + { + text: "部署", + base: "/deploy", + collapsed: false, + items: [ + { text: "包管理器部署", link: "/astrbot/package" }, + { text: "雨云一键云部署", link: "/astrbot/rainyun" }, + { text: "桌面客户端部署", link: "/astrbot/desktop" }, + { text: "启动器一键部署", link: "/astrbot/launcher" }, + { text: "Docker 部署", link: "/astrbot/docker" }, + { text: "Kubernetes 部署", link: "/astrbot/kubernetes" }, + { text: "宝塔面板部署", link: "/astrbot/btpanel" }, + { text: "1Panel 部署", link: "/astrbot/1panel" }, + { text: "手动部署", link: "/astrbot/cli" }, + { + text: "其他部署方式", + link: "/astrbot/other-deployments", + collapsed: true, + items: [ + { text: "CasaOS 部署", link: "/astrbot/casaos" }, + { text: "优云智算 GPU 部署", link: "/astrbot/compshare" }, + { text: "社区提供的部署方式", link: "/astrbot/community-deployment" }, + ], + }, + { + text: "支持我们", + link: "/when-deployed", + }, + ], + }, + { + text: "接入消息平台", + base: "/platform", + items: [ + { + text: "快速接入指南", + link: "/start", + }, + { + text: "QQ 官方机器人", + link: "/qqofficial", + collapsed: true, + items: [ + { text: "Websockets 方式(推荐)", link: "/qqofficial/websockets" }, + { text: "Webhook 方式", link: "/qqofficial/webhook" }, + ], + }, + { + text: "OneBot v11", + base: "/platform/aiocqhttp", + collapsed: true, + items: [ + { text: "NapCat", link: "/napcat" }, + { text: "Lagrange", link: "/lagrange" }, + { text: "其他端", link: "/others" }, + ], + }, + { text: "企微应用", link: "/wecom" }, + { text: "企微智能机器人", link: "/wecom_ai_bot" }, + { text: "微信公众号", link: "/weixin-official-account" }, + { text: "飞书", link: "/lark" }, + { text: "钉钉", link: "/dingtalk" }, + { text: "Telegram", link: "/telegram" }, + { text: "LINE", link: "/line" }, + { text: "Slack", link: "/slack" }, + { text: "Misskey", link: "/misskey" }, + { text: "Discord", link: "/discord" }, + { text: "KOOK", link: "/kook" }, + { + text: "Satori", + base: "/platform/satori", + collapsed: true, + items: [ + { text: "使用 LLOneBot", link: "/llonebot" }, + { text: "使用 server-satori", link: "/server-satori" }, + ], + }, + { + text: "社区提供", + collapsed: false, + items: [ + { text: "Matrix", link: "/matrix" }, + { text: "VoceChat", link: "/vocechat" }, + ], + }, + ], + }, + { + text: "接入 AI", + base: "/providers", + items: [ + { + text: "✨ 接入模型服务", + link: "/start", + collapsed: true, + items: [ + { text: "NewAPI", link: "/newapi" }, + { text: "AIHubMix", link: "/aihubmix" }, + { text: "PPIO 派欧云", link: "/ppio" }, + { text: "硅基流动", link: "/siliconflow" }, + { text: "小马算力", link: "/tokenpony" }, + { text: "302.AI", link: "/302ai" }, + { text: "Ollama", link: "/provider-ollama" }, + { text: "LMStudio", link: "/provider-lmstudio" }, + ] + }, + { + text: "⚙️ Agent 执行器", + link: "/agent-runners", + collapsed: false, + items: [ + { text: "内置 Agent 执行器", link: "/agent-runners/astrbot-agent-runner" }, + { text: "Dify", link: "/agent-runners/dify" }, + { text: "扣子 Coze", link: "/agent-runners/coze" }, + { text: "阿里云百炼应用", link: "/agent-runners/dashscope" }, + { text: "DeerFlow", link: "/agent-runners/deerflow" }, + ] + }, + ], + }, + { + text: "使用", + base: "/use", + items: [ + { text: "WebUI", link: "/webui" }, + { text: "插件", link: "/plugin" }, + { text: "内置指令", link: "/command" }, + { text: "工具使用 Tools", link: "/function-calling" }, + { text: "技能 Skills", link: "/skills" }, + { text: "SubAgent 编排", link: "/subagent" }, + { text: "主动型 Agent 能力", link: "/proactive-agent" }, + { text: "MCP", link: "/mcp" }, + { text: "网页搜索", link: "/websearch" }, + { text: "知识库", link: "/knowledge-base" }, + { text: "自定义规则", link: "/custom-rules" }, + { text: "Agent 执行器", link: "/agent-runner" }, + { text: "统一 Webhook 模式", link: "/unified-webhook" }, + { text: "自动上下文压缩", link: "/context-compress" }, + { text: "Agent 沙箱环境", link: "/astrbot-agent-sandbox" }, + ], + }, + { + text: "开发", + base: "/dev", + collapsed: true, + items: [ + { + text: "插件开发", + base: "/dev/star", + collapsed: true, + items: [ + { text: "🌠 从这里开始", link: "/plugin-new" }, + { text: "最小实例", link: "/guides/simple" }, + { text: "接收消息事件", link: "/guides/listen-message-event" }, + { text: "发送消息", link: "/guides/send-message" }, + { text: "插件配置", link: "/guides/plugin-config" }, + { text: "调用 AI", link: "/guides/ai" }, + { text: "存储", link: "/guides/storage" }, + { text: "文转图", link: "/guides/html-to-pic" }, + { text: "会话控制器", link: "/guides/session-control" }, + { text: "杂项", link: "/guides/other" }, + { text: "发布插件", link: "/plugin-publish" }, + { text: "插件指南(旧)", link: "/plugin" }, + ], + }, + { + text: "接入平台适配器", + link: "/plugin-platform-adapter", + }, + { + text: "AstrBot HTTP API", + link: "/openapi", + }, + { + text: "AstrBot 配置文件", + link: "/astrbot-config", + }, + ], + }, + { + text: "其他", + base: "/others", + collapsed: true, + items: [ + { text: "自部署文转图", link: "/self-host-t2i" }, + { text: "插件下载不了?试试自建 GitHub 加速服务", link: "/github-proxy" }, + ], + }, + { + text: "开源之夏", + base: "/ospp", + collapsed: true, + items: [{ text: "OSPP 2025", link: "/2025" }], + }, + ], + outline: { + level: 'deep', + label: '目录', + }, + darkModeSwitchLabel: '切换日光/暗黑模式', + sidebarMenuLabel: '文章', + returnToTopLabel: '返回顶部', + docFooter: { + prev: '上一篇', + next: '下一篇' + }, + editLink: { + pattern: 'https://github.com/AstrBotdevs/AstrBot-docs/edit/v4/:path', + text: '发现文档有问题?在 GitHub 上编辑此页', + }, + logo: '/logo_prod.png', + socialLinks: [ + { icon: "github", link: "https://github.com/AstrBotDevs/AstrBot" }, + ], + footer: { + message: 'Deployed on ' + + '' + + 'Rainyun Logo' + + '', + } + } + }, + en: { + label: "English", + lang: "en-US", + themeConfig: { + nav: [ + { text: "Home", link: "https://astrbot.app" }, + { text: "Blog", link: "https://blog.astrbot.app" }, + { text: "Roadmap", link: "https://astrbot.featurebase.app/roadmap" }, + { text: "HTTP API", link: "https://docs.astrbot.app/scalar.html" }, + ], + sidebar: [ + { + text: "Introduction", + items: [ + { text: "What is AstrBot", link: "/en/what-is-astrbot" }, + { text: "Community", link: "/en/community" }, + { text: "FAQ", link: "/en/faq" }, + ], + }, + { + text: "Deployment", + base: "/en/deploy", + collapsed: false, + items: [ + { text: "Package Manager", link: "/astrbot/package" }, + { text: "One-click Launcher", link: "/astrbot/launcher" }, + { text: "Docker", link: "/astrbot/docker" }, + { text: "Kubernetes", link: "/astrbot/kubernetes" }, + { text: "BT Panel", link: "/astrbot/btpanel" }, + { text: "1Panel", link: "/astrbot/1panel" }, + { text: "Manual", link: "/astrbot/cli" }, + { + text: "Other Deployments", + link: "/astrbot/other-deployments", + collapsed: true, + items: [ + { text: "CasaOS", link: "/astrbot/casaos" }, + { text: "Compshare GPU", link: "/astrbot/compshare" }, + { text: "Community-provided Deployment", link: "/astrbot/community-deployment" }, + ], + }, + { + text: "Support Us", + link: "/when-deployed", + }, + ], + }, + { + text: "Messaging Platforms", + base: "/en/platform", + collapsed: false, + items: [ + { + text: "Quick Start", + link: "/start", + }, + { + text: "QQ Official Bot", + link: "/qqofficial", + collapsed: true, + items: [ + { text: "Websockets", link: "/qqofficial/websockets" }, + { text: "Webhook", link: "/qqofficial/webhook" }, + ], + }, + { + text: "OneBot v11", + base: "/en/platform/aiocqhttp", + collapsed: true, + items: [ + { text: "NapCat", link: "/napcat" }, + { text: "Lagrange", link: "/lagrange" }, + { text: "Other Clients", link: "/others" }, + ], + }, + { text: "WeCom Application", link: "/wecom" }, + { text: "WeCom AI Bot", link: "/wecom_ai_bot" }, + { text: "WeChat Official Account", link: "/weixin-official-account" }, + { text: "Lark", link: "/lark" }, + { text: "DingTalk", link: "/dingtalk" }, + { text: "Telegram", link: "/telegram" }, + { text: "LINE", link: "/line" }, + { text: "Slack", link: "/slack" }, + { text: "Misskey", link: "/misskey" }, + { text: "Discord", link: "/discord" }, + { + text: "Satori", + base: "/en/platform/satori", + collapsed: true, + items: [ + { text: "Using LLOneBot", link: "/llonebot" }, + { text: "Using server-satori", link: "/server-satori" }, + ], + }, + { + text: "Community-provided", + collapsed: false, + items: [ + { text: "Matrix", link: "/matrix" }, + { text: "KOOK", link: "/kook" }, + { text: "VoceChat", link: "/vocechat" }, + ], + }, + ], + }, + { + text: "AI Integration", + base: "/en/providers", + collapsed: false, + items: [ + { + text: "✨ Model Providers", + link: "/start", + collapsed: true, + items: [ + { text: "NewAPI", link: "/newapi" }, + { text: "AIHubMix", link: "/aihubmix" }, + { text: "PPIO Cloud", link: "/ppio" }, + { text: "SiliconFlow", link: "/siliconflow" }, + { text: "TokenPony", link: "/tokenpony" }, + { text: "302.AI", link: "/302ai" }, + { text: "Ollama", link: "/provider-ollama" }, + { text: "LMStudio", link: "/provider-lmstudio" }, + ], + }, + { + text: "⚙️ Agent Runners", + link: "/agent-runners", + collapsed: false, + items: [ + { text: "Built-in Agent Runner", link: "/agent-runners/astrbot-agent-runner" }, + { text: "Dify", link: "/agent-runners/dify" }, + { text: "Coze", link: "/agent-runners/coze" }, + { text: "Alibaba Bailian", link: "/agent-runners/dashscope" }, + { text: "DeerFlow", link: "/agent-runners/deerflow" }, + ], + }, + ], + }, + { + text: "Usage", + base: "/en/use", + collapsed: true, + items: [ + { text: "WebUI", link: "/webui" }, + { text: "Plugins", link: "/plugin" }, + { text: "Built-in Commands", link: "/command" }, + { text: "Tool Use", link: "/function-calling" }, + { text: "Anthropic Skills", link: "/skills" }, + { text: "SubAgent Orchestration", link: "/subagent" }, + { text: "Proactive Tasks", link: "/proactive-agent" }, + { text: "MCP", link: "/mcp" }, + { text: "Web Search", link: "/websearch" }, + { text: "Knowledge Base", link: "/knowledge-base" }, + { text: "Custom Rules", link: "/custom-rules" }, + { text: "Agent Runner", link: "/agent-runner" }, + { text: "Unified Webhook Mode", link: "/unified-webhook" }, + { text: "Auto Context Compression", link: "/context-compress" }, + { text: "Agent Sandbox", link: "/astrbot-agent-sandbox" }, + ], + }, + { + text: "Development", + base: "/en/dev", + collapsed: true, + items: [ + { + text: "Plugin Development", + base: "/en/dev/star", + collapsed: true, + items: [ + { text: "🌠 Getting Started", link: "/plugin-new" }, + { text: "Minimal Example", link: "/guides/simple" }, + { text: "Listen to Message Events", link: "/guides/listen-message-event" }, + { text: "Send Messages", link: "/guides/send-message" }, + { text: "Plugin Configuration", link: "/guides/plugin-config" }, + { text: "AI", link: "/guides/ai" }, + { text: "Storage", link: "/guides/storage" }, + { text: "HTML to Image", link: "/guides/html-to-pic" }, + { text: "Session Control", link: "/guides/session-control" }, + { text: "Publish Plugin", link: "/plugin-publish" }, + ], + }, + { + text: "Platform Adapter Integration", + link: "/plugin-platform-adapter", + }, + { + text: "AstrBot HTTP API", + link: "/openapi", + }, + { + text: "AstrBot Configuration File", + link: "/astrbot-config", + }, + ], + }, + { + text: "Others", + base: "/en/others", + collapsed: true, + items: [ + { text: "Self-hosted HTML to Image", link: "/self-host-t2i" }, + ], + }, + { + text: "Open Source Summer", + base: "/en/ospp", + collapsed: true, + items: [{ text: "OSPP 2025", link: "/2025" }], + }, + ], + outline: { + level: 'deep', + label: 'On this page', + }, + darkModeSwitchLabel: 'Toggle dark mode', + sidebarMenuLabel: 'Menu', + returnToTopLabel: 'Return to top', + docFooter: { + prev: 'Previous', + next: 'Next' + }, + editLink: { + pattern: 'https://github.com/AstrBotdevs/AstrBot-docs/edit/v4/:path', + text: 'Edit this page on GitHub', + }, + logo: '/logo_prod.png', + socialLinks: [ + { icon: "github", link: "https://github.com/AstrBotDevs/AstrBot" }, + ], + footer: { + message: 'Deployed on ' + + '' + + 'Rainyun Logo' + + '', + } + } + }, + }, + + themeConfig: { + search: { + provider: "local", + options: { + locales: { + root: { + translations: { + button: { + buttonText: "搜索文档", + buttonAriaLabel: "搜索文档", + }, + modal: { + noResultsText: "无法找到相关结果", + resetButtonTitle: "清除查询条件", + footer: { + selectText: "选择", + navigateText: "切换", + closeText: "关闭", + }, + }, + }, + }, + }, + }, + }, + } +}); diff --git a/docs/.vitepress/config/head.ts b/docs/.vitepress/config/head.ts new file mode 100644 index 000000000..efa645e1a --- /dev/null +++ b/docs/.vitepress/config/head.ts @@ -0,0 +1,47 @@ +import type { HeadConfig } from "vitepress"; + +export const head: HeadConfig[] = [ + // --- Google Fonts --- + ["link", { rel: "preconnect", href: "https://fonts.googleapis.cn", crossorigin: "" }], + ["link", { rel: "dns-prefetch", href: "https://fonts.googleapis.cn" }], + ["link", { rel: "preconnect", href: "https://fonts.gstatic.cn", crossorigin: "" }], + ["link", { rel: "dns-prefetch", href: "https://fonts.gstatic.cn" }], + ["link", { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap" }], + + // --- 基础和SEO元数据 --- + ["link", { rel: "icon", href: "/logo.png" }], + ["meta", { name: "description", content: "AstrBot" }], + [ + "meta", + { name: "viewport", content: "width=device-width, initial-scale=1.0" }, + ], + + /* // --- Open Graph (OG) 协议元数据 (用于社交媒体分享) --- + ["meta", { property: "og:type", content: "website" }], + ["meta", { property: "og:locale", content: "zh_CN" }], + ["meta", { property: "og:title", content: "AstrBot" }], + ["meta", { property: "og:description", content: "AstrBot" }], + ["meta", { property: "og:url", content: "https://docs.astrbot.app" }], + ["meta", { property: "og:site_name", content: "AstrBot" }], + [ + "meta", + { + property: "og:image", + content: "/", + }, + ], + [ + "meta", + { property: "og:image:alt", content: "AstrBot" }, + ], + ["meta", { property: "og:image:width", content: "1200" }], + ["meta", { property: "og:image:height", content: "630" }], + ["meta", { property: "og:image:type", content: "image/png" }], + + // --- Twitter Card 元数据 --- + ["meta", { name: "twitter:card", content: "summary_large_image" }], + ["meta", { name: "twitter:site", content: "@AstrBot" }],*/ + + // --- Umami Analytics --- + ["script", { defer: "", src: "https://cloud.umami.is/script.js", "data-website-id": "9c3f777e-9f4a-4b79-a5c3-ff94f5dca8f9" }], +]; \ No newline at end of file diff --git a/docs/.vitepress/theme/components/ArticleShare.vue b/docs/.vitepress/theme/components/ArticleShare.vue new file mode 100644 index 000000000..35115e5a6 --- /dev/null +++ b/docs/.vitepress/theme/components/ArticleShare.vue @@ -0,0 +1,194 @@ + + + + + \ No newline at end of file diff --git a/docs/.vitepress/theme/components/HomeFeaturesAfter.vue b/docs/.vitepress/theme/components/HomeFeaturesAfter.vue new file mode 100644 index 000000000..2033a7257 --- /dev/null +++ b/docs/.vitepress/theme/components/HomeFeaturesAfter.vue @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/.vitepress/theme/components/Layout.vue b/docs/.vitepress/theme/components/Layout.vue new file mode 100644 index 000000000..e8ea0cb2d --- /dev/null +++ b/docs/.vitepress/theme/components/Layout.vue @@ -0,0 +1,131 @@ + + + + + diff --git a/docs/.vitepress/theme/components/NotFound.vue b/docs/.vitepress/theme/components/NotFound.vue new file mode 100644 index 000000000..f3b01e24c --- /dev/null +++ b/docs/.vitepress/theme/components/NotFound.vue @@ -0,0 +1,73 @@ + + + + + \ No newline at end of file diff --git a/docs/.vitepress/theme/components/SectionTabs.vue b/docs/.vitepress/theme/components/SectionTabs.vue new file mode 100644 index 000000000..80b998808 --- /dev/null +++ b/docs/.vitepress/theme/components/SectionTabs.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/docs/.vitepress/theme/index.js b/docs/.vitepress/theme/index.js new file mode 100644 index 000000000..02131c6c8 --- /dev/null +++ b/docs/.vitepress/theme/index.js @@ -0,0 +1,21 @@ +// https://vitepress.dev/guide/custom-theme +import { h } from 'vue' +import DefaultTheme from 'vitepress/theme' +import './styles/style.css' +import './styles/custom-block.css' +import './styles/font.css' +import Layout from './components/Layout.vue' +import ArticleShare from './components/ArticleShare.vue' +import NotFound from './components/NotFound.vue' + +/** @type {import('vitepress').Theme} */ +export default { + extends: DefaultTheme, + Layout() { + return h(Layout, null, { + // https://vitepress.dev/guide/extending-default-theme#layout-slots + 'aside-outline-after': () => h(ArticleShare), + 'not-found': () => h(NotFound) + }) + } +} diff --git a/docs/.vitepress/theme/styles/custom-block.css b/docs/.vitepress/theme/styles/custom-block.css new file mode 100644 index 000000000..951f1d0c6 --- /dev/null +++ b/docs/.vitepress/theme/styles/custom-block.css @@ -0,0 +1,185 @@ +/* .vitepress/theme/style/custom-block.css */ +/* 深浅色卡 */ +:root { + --custom-block-info-left: #cccccc; + --custom-block-info-bg: #fafafa; + + --custom-block-tip-left: #009400; + --custom-block-tip-bg: #b6dcc7; + + --custom-block-warning-left: #e6a700; + --custom-block-warning-bg: #ffe69d; + + --custom-block-danger-left: #e13238; + --custom-block-danger-bg: #ffebec; + + --custom-block-note-left: #4cb3d4; + --custom-block-note-bg: #d6eff7; + + --custom-block-important-left: #a371f7; + --custom-block-important-bg: #f4eefe; + + --custom-block-caution-left: #e0575b; + --custom-block-caution-bg: #fde4e8; +} + +.dark { + --custom-block-info-left: #cccccc; + --custom-block-info-bg: #474748; + + --custom-block-tip-left: #009400; + --custom-block-tip-bg: #003100; + + --custom-block-warning-left: #e6a700; + --custom-block-warning-bg: #4d3800; + + --custom-block-danger-left: #e13238; + --custom-block-danger-bg: #4b1113; + + --custom-block-note-left: #4cb3d4; + --custom-block-note-bg: #193c47; + + --custom-block-important-left: #a371f7; + --custom-block-important-bg: #230555; + + --custom-block-caution-left: #e0575b; + --custom-block-caution-bg: #391c22; +} + + +/* 标题字体大小 */ +.custom-block-title { + font-size: 16px; +} + +/* info容器:背景色、左侧 */ +.custom-block.info { + background-color: var(--custom-block-info-bg); +} + +/* info容器:svg图 */ +.custom-block.info [class*="custom-block-title"]::before { + content: ''; + background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-11v6h2v-6h-2zm0-4v2h2V7h-2z' fill='%23ccc'/%3E%3C/svg%3E"); + width: 20px; + height: 20px; + display: inline-block; + vertical-align: middle; + position: relative; + margin-right: 4px; + left: -5px; + top: -1px; +} + +/* 提示容器:边框色、背景色、左侧 */ +.custom-block.tip { + background-color: var(--custom-block-tip-bg); +} + +/* 提示容器:svg图 */ +.custom-block.tip [class*="custom-block-title"]::before { + content: ''; + background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23009400' d='M7.941 18c-.297-1.273-1.637-2.314-2.187-3a8 8 0 1 1 12.49.002c-.55.685-1.888 1.726-2.185 2.998H7.94zM16 20v1a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-1h8zm-3-9.995V6l-4.5 6.005H11v4l4.5-6H13z'/%3E%3C/svg%3E"); + width: 20px; + height: 20px; + display: inline-block; + vertical-align: middle; + position: relative; + margin-right: 4px; + left: -5px; + top: -2px; +} + +/* 警告容器:背景色、左侧 */ +.custom-block.warning { + background-color: var(--custom-block-warning-bg); +} + +/* 警告容器:svg图 */ +.custom-block.warning [class*="custom-block-title"]::before { + content: ''; + background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Cpath d='M576.286 752.57v-95.425q0-7.031-4.771-11.802t-11.3-4.772h-96.43q-6.528 0-11.3 4.772t-4.77 11.802v95.424q0 7.031 4.77 11.803t11.3 4.77h96.43q6.528 0 11.3-4.77t4.77-11.803zm-1.005-187.836 9.04-230.524q0-6.027-5.022-9.543-6.529-5.524-12.053-5.524H456.754q-5.524 0-12.053 5.524-5.022 3.516-5.022 10.547l8.538 229.52q0 5.023 5.022 8.287t12.053 3.265h92.913q7.032 0 11.803-3.265t5.273-8.287zM568.25 95.65l385.714 707.142q17.578 31.641-1.004 63.282-8.538 14.564-23.354 23.102t-31.892 8.538H126.286q-17.076 0-31.892-8.538T71.04 866.074q-18.582-31.641-1.004-63.282L455.75 95.65q8.538-15.57 23.605-24.61T512 62t32.645 9.04 23.605 24.61z' fill='%23e6a700'/%3E%3C/svg%3E"); + width: 20px; + height: 20px; + display: inline-block; + vertical-align: middle; + position: relative; + margin-right: 4px; + left: -5px; +} + +/* 危险容器:背景色、左侧 */ +.custom-block.danger { + background-color: var(--custom-block-danger-bg); +} + +/* 危险容器:svg图 */ +.custom-block.danger [class*="custom-block-title"]::before { + content: ''; + background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2c5.523 0 10 4.477 10 10v3.764a2 2 0 0 1-1.106 1.789L18 19v1a3 3 0 0 1-2.824 2.995L14.95 23a2.5 2.5 0 0 0 .044-.33L15 22.5V22a2 2 0 0 0-1.85-1.995L13 20h-2a2 2 0 0 0-1.995 1.85L9 22v.5c0 .171.017.339.05.5H9a3 3 0 0 1-3-3v-1l-2.894-1.447A2 2 0 0 1 2 15.763V12C2 6.477 6.477 2 12 2zm-4 9a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm8 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4z' fill='%23e13238'/%3E%3C/svg%3E"); + width: 20px; + height: 20px; + display: inline-block; + vertical-align: middle; + position: relative; + margin-right: 4px; + left: -5px; + top: -1px; +} + +/* 提醒容器:背景色、左侧 */ +.custom-block.note { + background-color: var(--custom-block-note-bg); +} + +/* 提醒容器:svg图 */ +.custom-block.note [class*="custom-block-title"]::before { + content: ''; + background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-11v6h2v-6h-2zm0-4v2h2V7h-2z' fill='%234cb3d4'/%3E%3C/svg%3E"); + width: 20px; + height: 20px; + display: inline-block; + vertical-align: middle; + position: relative; + margin-right: 4px; + left: -5px; + top: -1px; +} + +/* 重要容器:背景色、左侧 */ +.custom-block.important { + background-color: var(--custom-block-important-bg); +} + +/* 重要容器:svg图 */ +.custom-block.important [class*="custom-block-title"]::before { + content: ''; + background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Cpath d='M512 981.333a84.992 84.992 0 0 1-84.907-84.906h169.814A84.992 84.992 0 0 1 512 981.333zm384-128H128v-42.666l85.333-85.334v-256A298.325 298.325 0 0 1 448 177.92V128a64 64 0 0 1 128 0v49.92a298.325 298.325 0 0 1 234.667 291.413v256L896 810.667v42.666zm-426.667-256v85.334h85.334v-85.334h-85.334zm0-256V512h85.334V341.333h-85.334z' fill='%23a371f7'/%3E%3C/svg%3E"); + width: 20px; + height: 20px; + display: inline-block; + vertical-align: middle; + position: relative; + margin-right: 4px; + left: -5px; + top: -1px; +} + +/* 注意容器:背景色、左侧 */ +.custom-block.caution { + background-color: var(--custom-block-caution-bg); +} + +/* 注意容器:svg图 */ +.custom-block.caution [class*="custom-block-title"]::before { + content: ''; + background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2c5.523 0 10 4.477 10 10v3.764a2 2 0 0 1-1.106 1.789L18 19v1a3 3 0 0 1-2.824 2.995L14.95 23a2.5 2.5 0 0 0 .044-.33L15 22.5V22a2 2 0 0 0-1.85-1.995L13 20h-2a2 2 0 0 0-1.995 1.85L9 22v.5c0 .171.017.339.05.5H9a3 3 0 0 1-3-3v-1l-2.894-1.447A2 2 0 0 1 2 15.763V12C2 6.477 6.477 2 12 2zm-4 9a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm8 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4z' fill='%23e13238'/%3E%3C/svg%3E"); + width: 20px; + height: 20px; + display: inline-block; + vertical-align: middle; + position: relative; + margin-right: 4px; + left: -5px; + top: -1px; +} \ No newline at end of file diff --git a/docs/.vitepress/theme/styles/font.css b/docs/.vitepress/theme/styles/font.css new file mode 100644 index 000000000..9b37c44df --- /dev/null +++ b/docs/.vitepress/theme/styles/font.css @@ -0,0 +1,5 @@ +/* Keep only the top-left navbar title in Outfit; use VitePress defaults elsewhere. */ +.VPNavBarTitle .title, +.VPNavBarTitle .title .text { + font-family: "Outfit", sans-serif !important; +} diff --git a/docs/.vitepress/theme/styles/style.css b/docs/.vitepress/theme/styles/style.css new file mode 100644 index 000000000..6c1e1559e --- /dev/null +++ b/docs/.vitepress/theme/styles/style.css @@ -0,0 +1,358 @@ +/** + * Customize default theme styling by overriding CSS variables: + * https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css + */ + +/** + * Colors + * + * Each colors have exact same color scale system with 3 levels of solid + * colors with different brightness, and 1 soft color. + * + * - `XXX-1`: The most solid color used mainly for colored text. It must + * satisfy the contrast ratio against when used on top of `XXX-soft`. + * + * - `XXX-2`: The color used mainly for hover state of the button. + * + * - `XXX-3`: The color for solid background, such as bg color of the button. + * It must satisfy the contrast ratio with pure white (#ffffff) text on + * top of it. + * + * - `XXX-soft`: The color used for subtle background such as custom container + * or badges. It must satisfy the contrast ratio when putting `XXX-1` colors + * on top of it. + * + * The soft color must be semi transparent alpha channel. This is crucial + * because it allows adding multiple "soft" colors on top of each other + * to create a accent, such as when having inline code block inside + * custom containers. + * + * - `default`: The color used purely for subtle indication without any + * special meanings attached to it such as bg color for menu hover state. + * + * - `brand`: Used for primary brand colors, such as link text, button with + * brand theme, etc. + * + * - `tip`: Used to indicate useful information. The default theme uses the + * brand color for this by default. + * + * - `warning`: Used to indicate warning to the users. Used in custom + * container, badges, etc. + * + * - `danger`: Used to show error, or dangerous message to the users. Used + * in custom container, badges, etc. + * -------------------------------------------------------------------------- */ + +:root { + --vp-c-default-1: var(--vp-c-gray-1); + --vp-c-default-2: var(--vp-c-gray-2); + --vp-c-default-3: var(--vp-c-gray-3); + --vp-c-default-soft: var(--vp-c-gray-soft); + + --vp-c-brand-1: var(--vp-c-indigo-1); + --vp-c-brand-2: var(--vp-c-indigo-2); + --vp-c-brand-3: var(--vp-c-indigo-3); + --vp-c-brand-soft: var(--vp-c-indigo-soft); + + --vp-c-tip-1: var(--vp-c-brand-1); + --vp-c-tip-2: var(--vp-c-brand-2); + --vp-c-tip-3: var(--vp-c-brand-3); + --vp-c-tip-soft: var(--vp-c-brand-soft); + + --vp-c-warning-1: var(--vp-c-yellow-1); + --vp-c-warning-2: var(--vp-c-yellow-2); + --vp-c-warning-3: var(--vp-c-yellow-3); + --vp-c-warning-soft: var(--vp-c-yellow-soft); + + --vp-c-danger-1: var(--vp-c-red-1); + --vp-c-danger-2: var(--vp-c-red-2); + --vp-c-danger-3: var(--vp-c-red-3); + --vp-c-danger-soft: var(--vp-c-red-soft); +} + +/** + * Component: Button + * -------------------------------------------------------------------------- */ + +:root { + --vp-button-brand-border: transparent; + --vp-button-brand-text: var(--vp-c-white); + --vp-button-brand-bg: var(--vp-c-brand-3); + --vp-button-brand-hover-border: transparent; + --vp-button-brand-hover-text: var(--vp-c-white); + --vp-button-brand-hover-bg: var(--vp-c-brand-2); + --vp-button-brand-active-border: transparent; + --vp-button-brand-active-text: var(--vp-c-white); + --vp-button-brand-active-bg: var(--vp-c-brand-1); +} + +/** + * Component: Home + * -------------------------------------------------------------------------- */ + +:root { + --vp-home-hero-name-color: transparent; + --vp-home-hero-name-background: -webkit-linear-gradient( + 120deg, + #bd34fe 30%, + #41d1ff + ); + + --vp-home-hero-image-background-image: linear-gradient( + -45deg, + #bd34fe 50%, + #47caff 50% + ); + --vp-home-hero-image-filter: blur(44px); +} + +@media (min-width: 640px) { + :root { + --vp-home-hero-image-filter: blur(56px); + } +} + +@media (min-width: 960px) { + :root { + --vp-home-hero-image-filter: blur(68px); + } +} + +/** + * Component: Custom Block + * -------------------------------------------------------------------------- */ + +:root { + --vp-custom-block-tip-border: transparent; + --vp-custom-block-tip-text: var(--vp-c-text-1); + --vp-custom-block-tip-bg: var(--vp-c-brand-soft); + --vp-custom-block-tip-code-bg: var(--vp-c-brand-soft); +} + +/** + * Component: Sidebar + * -------------------------------------------------------------------------- */ + +:root { + --vp-sidebar-bg-color: transparent; + --vp-section-tabs-height: 44px; +} + +@media (max-width: 959px) { + :root { + --vp-sidebar-bg-color: var(--vp-c-bg-alt); + } + + .VPSidebar { + background-color: var(--vp-c-bg-alt) !important; + } +} + +.VPSidebarItem.is-link > .item > .link { + margin: 2px 0; + border-radius: 8px; + padding: 0 10px; + transition: none; +} + +.VPSidebarItem, +.VPSidebarItem > .item, +.VPSidebarItem > .item > .link { + border-bottom: none !important; +} + +.VPSidebar .group + .group { + border-top: none !important; +} + +.VPSidebar { + scrollbar-width: thin; + scrollbar-color: var(--vp-c-divider) transparent; +} + +.VPSidebar::-webkit-scrollbar { + width: 10px; +} + +.VPSidebar::-webkit-scrollbar-track { + background: transparent; +} + +.VPSidebar::-webkit-scrollbar-thumb { + border: 2px solid transparent; + border-radius: 999px; + background-clip: padding-box; + background-color: var(--vp-c-divider); +} + +.VPSidebar::-webkit-scrollbar-thumb:hover { + background-color: var(--vp-c-text-3); +} + +.VPSidebarItem.is-link > .item > .link:hover { + background-color: var(--vp-c-default-soft); +} + +.VPSidebarItem.is-link.is-active > .item > .link { + background-color: var(--vp-c-brand-soft); +} + +/** + * Component: Algolia + * -------------------------------------------------------------------------- */ + +.DocSearch { + --docsearch-primary-color: var(--vp-c-brand-1) !important; +} + +/** + * Component: Nav + * -------------------------------------------------------------------------- */ + +.VPNavBarTitle .logo { + width: 40px; + height: 40px; +} + +.VPNavBarTitle .title > span { + font-size: 26px; + color: var(--vp-c-text-1); +} + +@media (min-width: 960px) { + .VPNavBar.has-sidebar .wrapper { + padding: 0 32px !important; + background-color: var(--vp-nav-bg-color) !important; + } + + .VPNavBar.has-sidebar .container { + max-width: calc(var(--vp-layout-max-width) - 64px) !important; + justify-content: flex-start !important; + gap: 24px !important; + background-color: var(--vp-nav-bg-color) !important; + } + + .VPNavBar.has-sidebar .container > .title { + position: relative !important; + z-index: 3 !important; + padding: 0 !important; + width: auto !important; + max-width: none !important; + background-color: var(--vp-nav-bg-color) !important; + } + + .VPNavBar.has-sidebar .content { + padding-left: 0 !important; + padding-right: 0 !important; + } + + .VPNavBar.has-sidebar .content-body { + justify-content: flex-start !important; + } + + .VPNavBar.has-sidebar .menu { + margin-right: auto !important; + } + + .VPNavBar.has-sidebar .divider { + padding-left: 0 !important; + } + + .VPNavBar.has-sidebar .VPNavBarTitle .title { + border-bottom: none !important; + background-color: var(--vp-nav-bg-color); + } +} + +@media (min-width: 1440px) { + .VPNavBar.has-sidebar .container > .title { + padding-left: 0 !important; + width: auto !important; + } + + .VPNavBar.has-sidebar .content { + padding-right: 0 !important; + padding-left: 0 !important; + } + + .VPNavBar.has-sidebar .divider { + padding-left: 0 !important; + } +} + +/** + * Component: Local Nav + * -------------------------------------------------------------------------- */ + +@media (min-width: 960px) { + .VPLocalNav.has-sidebar { + border-bottom: none !important; + } + + .VPLocalNav.has-sidebar::after { + content: ""; + position: absolute; + left: var(--vp-sidebar-width); + right: 0; + bottom: 0; + height: 1px; + background-color: var(--vp-c-gutter); + } +} + +@media (min-width: 1440px) { + .VPLocalNav.has-sidebar::after { + left: calc((100vw - var(--vp-layout-max-width)) / 2 + var(--vp-sidebar-width)); + } +} + +.VPDocAsideOutline.has-outline .content { + border-left: none !important; +} + +@media (min-width: 1280px) { + .VPNavBar.has-sidebar .divider { + display: none !important; + } + + .VPSidebar { + padding-top: calc(var(--vp-nav-height) + var(--vp-section-tabs-height)) !important; + } + + .Layout.sidebar-scope-intro-deploy .VPSidebar .group, + .Layout.sidebar-scope-platform .VPSidebar .group, + .Layout.sidebar-scope-providers .VPSidebar .group, + .Layout.sidebar-scope-use .VPSidebar .group, + .Layout.sidebar-scope-dev .VPSidebar .group { + display: none; + } + + .Layout.sidebar-scope-intro-deploy .VPSidebar .group:nth-of-type(1), + .Layout.sidebar-scope-intro-deploy .VPSidebar .group:nth-of-type(2), + .Layout.sidebar-scope-intro-deploy .VPSidebar .group:nth-of-type(7), + .Layout.sidebar-scope-intro-deploy .VPSidebar .group:nth-of-type(8), + .Layout.sidebar-scope-platform .VPSidebar .group:nth-of-type(3), + .Layout.sidebar-scope-providers .VPSidebar .group:nth-of-type(4), + .Layout.sidebar-scope-use .VPSidebar .group:nth-of-type(5), + .Layout.sidebar-scope-dev .VPSidebar .group:nth-of-type(6) { + display: block; + } +} + +.VPHomeHero:not(.has-image) .container { + text-align: center; +} + +.VPHomeHero:not(.has-image) .heading { + align-items: center; +} + +.VPHomeHero:not(.has-image) .name, +.VPHomeHero:not(.has-image) .text, +.VPHomeHero:not(.has-image) .tagline { + margin: 0 auto; +} + +.VPHomeHero:not(.has-image) .actions { + justify-content: center; +} diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..10cc7acfe --- /dev/null +++ b/docs/README.md @@ -0,0 +1,10 @@ + +# AstrBot +_✨ 易上手的多平台 LLM 聊天机器人及开发框架(的官方文档) ✨_ + +[查看文档](https://docs.astrbot.app/) | [问题提交](https://github.com/AstrBotDevs/AstrBot/issues) + +[AstrBot](https://github.com/AstrBotDevs/AstrBot) 是一个松耦合、异步、支持多消息平台部署、具有易用的插件系统和完善的大语言模型(LLM)接入功能的聊天机器人及开发框架。 + +![image](https://github.com/user-attachments/assets/48f72a71-9456-4166-bbd2-f2a6c8cd740f) + diff --git a/docs/en/community.md b/docs/en/community.md new file mode 100644 index 000000000..116fa882d --- /dev/null +++ b/docs/en/community.md @@ -0,0 +1,34 @@ +# Community + +## Community Channels + +This documentation may not cover all features comprehensively. If you have any questions or suggestions regarding AstrBot or this documentation, please feel free to reach out to us through the community channels below. + +### Discord + + + +### GitHub + +Welcome to submit Issues or Pull Requests: + +- [AstrBotDevs/AstrBot](https://github.com/AstrBotDevs/AstrBot) + +- [AstrBotDevs/AstrBot-Docs](https://github.com/AstrBotDevs/AstrBot-docs) + +### Tencent QQ Groups + +> - All groups are available to join. If you find that the group size is below the limit, please feel free to join. + +- Group 1: 322154837 (2000-member group) +- Group 3: 630166526 (2000-member group) +- Group 4: 1077826412 (1000-member group) +- Group 5: 822130018 (2000-member group) +- Group 6: 753075035 (2000-member group) +- Group 7: 743746109 (500-member group) +- Group 8: 1030353265 (500-member group) +- **AstrBot Core Development Group: 975206796** (AstrBot development members are usually active here. Welcome to anyone interested in programming/AI technology~) + +## Become an AstrBot Organization Member + +We welcome you to join us! diff --git a/docs/en/config/model-config.md b/docs/en/config/model-config.md new file mode 100644 index 000000000..5bc3d57b0 --- /dev/null +++ b/docs/en/config/model-config.md @@ -0,0 +1,12 @@ + +# 配置自定义的模型参数 + +请手动修改位于 `data/cmd_config.json` 下的配置文件。 + +找到 `provider`,并找到你想要修改的提供商的模型配置: + +![alt text](https://files.astrbot.app/docs/source/images/model-config/image-2.png) + +然后在 `model_config` 中添加新的参数即可。 + +具体的参数请参看对应的提供商的文档。 diff --git a/docs/en/deploy/astrbot/1panel.md b/docs/en/deploy/astrbot/1panel.md new file mode 100644 index 000000000..9c0b6fa0a --- /dev/null +++ b/docs/en/deploy/astrbot/1panel.md @@ -0,0 +1,27 @@ +# Deploy AstrBot on 1Panel + +[1Panel](https://1panel.cn/) is an open-source next-generation Linux server operation and management panel. + +AstrBot has been published to the [1Panel App Store](https://apps.fit2cloud.com/1panel) by the 1Panel team, allowing users to quickly deploy and use it directly through 1Panel. + +## Install 1Panel + +If you haven't installed 1Panel yet, please refer to the [1Panel official website](https://1panel.cn/) for one-click installation. + +> International users can refer to the [1Panel official site](https://github.com/1Panel-dev/1Panel) for tutorials. + +## Install AstrBot + +Open the 1Panel panel, go to the 1Panel App Store, and search for `AstrBot`, as shown below. + +![image](https://files.astrbot.app/docs/source/images/1panel/image.png) + +Click `Install` and wait for the installation to complete. + +After successful installation, open the corresponding AstrBot port (default is 6185) in the 1Panel System-Firewall page. + +If you are using cloud servers from providers like AWS, Alibaba Cloud, Tencent Cloud, etc., make sure their security groups also allow port 6185. + +## Access AstrBot + +Visit `http://IP:6185` to access the AstrBot dashboard. diff --git a/docs/en/deploy/astrbot/btpanel.md b/docs/en/deploy/astrbot/btpanel.md new file mode 100644 index 000000000..750fd61db --- /dev/null +++ b/docs/en/deploy/astrbot/btpanel.md @@ -0,0 +1,48 @@ +# Deploy AstrBot on BT Panel + +[BT Panel](https://www.bt.cn/new/index.html) is a secure, efficient, and production-ready Linux/Windows server operation panel. + +AstrBot has been published to BT Panel's Docker App Store, supporting one-click installation. + +## Install BT Panel + +If you haven't installed BT Panel yet, please refer to [Install BT Products](https://www.bt.cn/new/download.html) for one-click installation. + +## Set Acceleration URL (For Users in Mainland China) + +After entering the BT Panel page, click `Docker` on the left sidebar, click Settings, and modify the `Acceleration URL`. + +![alt text](https://files.astrbot.app/docs/source/images/btpanel/image-1.png) + +## Install AstrBot + +Go to Docker's App Store and search for `AstrBot`, as shown below. + +![image](https://files.astrbot.app/docs/source/images/btpanel/image.png) + +Click Install and wait for the installation to complete. + +After successful installation, click `Security` on the left sidebar and open the corresponding AstrBot port (default is 6185). + +If you are using cloud servers from providers like AWS, Alibaba Cloud, Tencent Cloud, etc., make sure their security groups also allow the corresponding port. + +## Access AstrBot + +Visit `http://IP:6185` to access the AstrBot dashboard. + +> [!TIP] +> By default, the above method only opens port 6185. If you need to deploy messaging platforms, you need to additionally open the corresponding ports. Click `Container` in the top bar, find the AstrBot container, click `Manage`, click `Edit Container`, and add the corresponding ports. +> +> ![image](https://files.astrbot.app/docs/source/images/btpanel/image-2.png) +> +> For specific messaging platform port mappings, refer to the table below: +> +>| Port | Description | Type +>| -------- | ------- | ------- | +>| 6185 | AstrBot WebUI `default` port | Required | +>| 6195 | WeCom `default` port | Optional | +>| 6199 | QQ Personal Account(aiocqhttp) `default` port | Optional | +>| 6196 | QQ Official API(Webhook) `default` port | Optional | +> +> Platforms not listed do not require additional port opening. + diff --git a/docs/en/deploy/astrbot/casaos.md b/docs/en/deploy/astrbot/casaos.md new file mode 100644 index 000000000..dd02fc402 --- /dev/null +++ b/docs/en/deploy/astrbot/casaos.md @@ -0,0 +1,39 @@ +# Deploy AstrBot on CasaOS + +## Install CasaOS + +```bash +curl -fsSL https://get.casaos.io | sudo bash +``` + +## Add CasaOS-AppStore-Play App Store Source + +![image](https://files.astrbot.app/docs/source/images/casaos/image.png) + +Click `More Apps`, then enter: + +```txt +https://play.cuse.eu.org/Cp0204-AppStore-Play.zip +``` + +And add it, wait for the addition to complete. + +If your network environment is in mainland China, please search for and add `dkTurbo` first, otherwise you may not be able to pull the AstrBot image. + +![image](https://files.astrbot.app/docs/source/images/casaos/image-1.png) + +Enter `Astrbot` to find AstrBot. + +![image](https://files.astrbot.app/docs/source/images/casaos/image-2.png) + +Click the icon (not the install button), then hover over the `Install` button and click Custom Install. + +![image](https://files.astrbot.app/docs/source/images/casaos/image-3.png) + +In the Network section, select `host`. + +![image](https://files.astrbot.app/docs/source/images/casaos/image-4.png) + +Then click `Install` to start the installation. + +After installation is complete, the AstrBot APP will appear on the main interface. Click it to open the dashboard. \ No newline at end of file diff --git a/docs/en/deploy/astrbot/cli.md b/docs/en/deploy/astrbot/cli.md new file mode 100644 index 000000000..857e0d6a6 --- /dev/null +++ b/docs/en/deploy/astrbot/cli.md @@ -0,0 +1,92 @@ +# Deploy AstrBot from Source Code + +> [!WARNING] +> You are deploying this project directly from source code. This tutorial requires you to have some technical background. +> +> This tutorial assumes Python is already installed on your device with version `>=3.10` + + +## Download/Clone Repository + +If you have `git` installed on your computer, you can download the source code with the following command: + +```bash +git clone https://github.com/AstrBotDevs/AstrBot.git +# The above code will pull the latest commit of the source code, if you need to pull the latest stable release version of the source code, you can use the following command: +# git clone --depth=1 --branch $(git ls-remote --tags --sort='-v:refname' https://github.com/AstrBotDevs/AstrBot.git | head -n1 | awk -F/ '{print $3}') https://github.com/AstrBotDevs/AstrBot.git +cd AstrBot +``` + +If you don't have `git` installed, please download and install it first. + +Alternatively, download the source code directly from GitHub and extract it: + +![image](https://files.astrbot.app/docs/source/images/cli/image.png) + +## Install Dependencies and Run + +::: details 【🥳Recommended】Use `uv` to Manage Dependencies + +> If `uv` is not installed, please refer to [Installing uv](https://docs.astral.sh/uv/getting-started/installation/) for installation. + +2. Execute in terminal (in the AstrBot directory) +```bash +uv sync +uv run main.py +``` + +If you have installed some plugins, it is recommended to add the `--no-sync` parameter for subsequent startups to avoid reinstalling plugin dependencies. We are working on solving this issue, so stay tuned. + +```bash +uv run --no-sync main.py +``` +::: + +::: details Install Dependencies with Python Built-in venv + +In the AstrBot source code directory, run the following command in the terminal: + +> If on Windows and you downloaded and extracted the source code directly, please open the extracted folder and enter in the address bar: +> ![image](https://files.astrbot.app/docs/source/images/cli/image-1.png) + +```bash +python3 -m venv ./venv +``` + +> It might be `python` instead of `python3` + +The above steps will create and activate a virtual environment (to avoid disrupting your local Python environment). + +Next, install the dependencies with the following command, which may take some time: + +Execute on Mac/Linux/WSL: + +```bash +source venv/bin/activate +python -m pip install -r requirements.txt -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple +python main.py +``` + +Execute on Windows: + +```bash +venv\Scripts\activate +python -m pip install -r requirements.txt -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple +python main.py +``` +::: + + +## 🎉 All Done! + +If everything goes well, you will see logs printed by AstrBot. + +If there are no errors, you will see a log message similar to `🌈 Dashboard started, accessible at` with several links. Open one of the links to access the AstrBot dashboard. The link is `http://localhost:6185`. + +> [!TIP] +> If you are deploying AstrBot on a server, you need to replace `localhost` with your server's IP address. +> +> The default username and password are `astrbot` and `astrbot`. + + +Next, you need to deploy any messaging platform to use AstrBot on that platform. diff --git a/docs/en/deploy/astrbot/community-deployment.md b/docs/en/deploy/astrbot/community-deployment.md new file mode 100644 index 000000000..17602ce32 --- /dev/null +++ b/docs/en/deploy/astrbot/community-deployment.md @@ -0,0 +1,52 @@ +# Community-Provided Deployment Methods + +> [!WARNING] +> AstrBot official does not guarantee the security and stability of these deployment methods. + +## Linux One-Click Deployment Script + +Use `curl` to download the script and execute it using `bash`: + +```bash +bash <(curl -sSL https://raw.githubusercontent.com/zhende1113/Antlia/refs/heads/main/Script/AstrBot/Antlia.sh) +``` + +If your system does not have `curl`, you can use `wget`: + +```bash +wget -qO- https://raw.githubusercontent.com/zhende1113/Antlia/refs/heads/main/Script/AstrBot/Antlia.sh | bash +``` + +Repository Address: [zhende1113/Antlia](https://github.com/zhende1113/Antlia/) + +## Linux One-Click Deployment Script (Based on Docker) + +Supports AstrBot / NapCat. + +> [!TIP] +> Use `sudo` for elevated permissions if you have insufficient privileges. + +### Using `curl` + +```bash +curl -sSL https://raw.githubusercontent.com/railgun19457/AstrbotScript/main/AstrbotScript.sh -o AstrbotScript.sh +chmod +x AstrbotScript.sh +sudo ./AstrbotScript.sh +``` + +### Using `wget` + +```bash +wget -qO AstrbotScript.sh https://raw.githubusercontent.com/railgun19457/AstrbotScript/main/AstrbotScript.sh +chmod +x AstrbotScript.sh +sudo ./AstrbotScript.sh +``` + +> [!note] +> `sudo ./AstrbotScript.sh --no-color (Optional: disable color output)` + +__Repository Address: [railgun19457/AstrbotScript](https://github.com/railgun19457/AstrbotScript)__ + +## AstrBot Android Deployment + +Refer to [zz6zz666/AstrBot-Android-App](https://github.com/zz6zz666/AstrBot-Android-App) \ No newline at end of file diff --git a/docs/en/deploy/astrbot/compshare.md b/docs/en/deploy/astrbot/compshare.md new file mode 100644 index 000000000..c985830a6 --- /dev/null +++ b/docs/en/deploy/astrbot/compshare.md @@ -0,0 +1,92 @@ +# Deploy via Compshare + +Compshare is UCloud's GPU compute rental and LLM API platform, offering compute resources for AI, deep learning, and scientific workloads. + +AstrBot provides an Ollama + AstrBot one-click self-deployment image on Compshare, and also supports Compshare model APIs. + +## Use the Ollama + AstrBot One-Click Image + +> Default image spec: RTX 3090 24GB + Intel 16-core + 64GB RAM + 200GB system disk. Billing is pay-as-you-go, so please monitor your balance. + +1. Register a Compshare account via [this link](https://passport.compshare.cn/register?referral_code=FV7DcGowN4hB5UuXKgpE74). +2. Open the [AstrBot image page](https://www.compshare.cn/images/0oX7xoGrzfre) and create an instance. +3. After deployment, open `JupyterLab` from the [console](https://console.compshare.cn/light-gpu/console/resources). +4. In JupyterLab, create a new terminal and run: + +```bash +cd +./astrbot_booter.sh +``` + +If startup succeeds, you should see output similar to: + +```txt +(py312) root@f8396035c96d:/workspace# cd +./astrbot_booter.sh +Starting AstrBot... +Starting ollama... +Both services started in the background. +``` + +After startup, open `http://:6185` in your browser to access the AstrBot dashboard. +You can find the public IP in Console -> Basic Network (Public). + +> It may take around 30 seconds before the page becomes reachable. + +![WebUI](https://www-s.ucloud.cn/2025/07/7e9fc6edc1dfa916abc069f4cecc24cf_1753940381771.png) + +Login with username `astrbot` and password `astrbot`. + +After logging in, you can reset your password and continue setup. + +The instance imports `Ollama-DeepSeek-R1-32B` by default. + +## Use Other Models + +### Pull Models with Ollama + +The image includes Ollama. You can pull any model and host it locally on the instance. + +1. Choose a model from [Ollama Search](https://ollama.com/search). +2. Connect to the instance terminal via SSH (from Compshare Console -> Instance List -> Console Command and Password). +3. Run `ollama pull ` and wait for completion. +4. In AstrBot Dashboard -> Providers, edit `ollama_deepseek-r1`, update the model name, and save. + +![image](https://files.astrbot.app/docs/source/images/compshare/image-1.png) + +### Use Compshare Model API + +AstrBot supports direct access to model APIs provided by Compshare. + +1. Find the model you want at [Compshare Model Center](https://console.compshare.cn/light-gpu/model-center). +2. In AstrBot Dashboard -> Providers, click `+ Add Provider`, then choose Compshare. +If Compshare is not listed, choose OpenAI-compatible access and set API Base URL to `https://api.modelverse.cn/v1`. +Enter the model name in model configuration and save. + +### Test + +In AstrBot Dashboard, click `Chat` and run `/provider` to view and switch your active provider. + +Then send a normal message to test whether the model works. + +![image](https://files.astrbot.app/docs/source/images/compshare/image-2.png) + +## Connect to Messaging Platforms + +You can follow the latest platform integration guides in the [AstrBot Documentation](https://docs.astrbot.app/en/what-is-astrbot.html). +Open the docs and check the left sidebar under Messaging Platforms. + +- Lark: [Connect to Lark](https://docs.astrbot.app/en/platform/lark.html) +- LINE: [Connect to LINE](https://docs.astrbot.app/en/platform/line.html) +- DingTalk: [Connect to DingTalk](https://docs.astrbot.app/en/platform/dingtalk.html) +- WeCom: [Connect to WeCom](https://docs.astrbot.app/en/platform/wecom.html) +- WeChat Official Account: [Connect to WeChat Official Account](https://docs.astrbot.app/en/platform/weixin-official-account.html) +- QQ Official Bot: [Connect to QQ Official API](https://docs.astrbot.app/en/platform/qqofficial/webhook.html) +- KOOK: [Connect to KOOK](https://docs.astrbot.app/en/platform/kook.html) +- Slack: [Connect to Slack](https://docs.astrbot.app/en/platform/slack.html) +- Discord: [Connect to Discord](https://docs.astrbot.app/en/platform/discord.html) +- More methods: [AstrBot Documentation](https://docs.astrbot.app/en/what-is-astrbot.html) + +## More Features + +For more capabilities, see the [AstrBot Documentation](https://docs.astrbot.app/en/what-is-astrbot.html). diff --git a/docs/en/deploy/astrbot/docker.md b/docs/en/deploy/astrbot/docker.md new file mode 100644 index 000000000..7ce973a53 --- /dev/null +++ b/docs/en/deploy/astrbot/docker.md @@ -0,0 +1,91 @@ +# Deploy AstrBot with Docker + +> [!WARNING] +> Docker provides a convenient way to deploy AstrBot on Windows, Mac, and Linux. +> +> This tutorial assumes you have Docker installed in your environment. If not, please refer to the [Docker official documentation](https://docs.docker.com/get-docker/) for installation. + +## Deploy with Docker Compose + +::: details Deploy AstrBot Only (General Method) + +First, clone the AstrBot repository to your local machine: + +```bash +git clone https://github.com/AstrBotDevs/AstrBot +cd AstrBot +``` + +Then, run Compose: + +```bash +sudo docker compose up -d +``` + +> [!TIP] +> If your network environment is in mainland China, the above command will not pull properly. You may need to modify the compose.yml file and replace `image: soulter/astrbot:latest` with `image: m.daocloud.io/docker.io/soulter/astrbot:latest`. +::: + +::: details Deploy with Agent Sandbox Environment + +Supports native Python code execution, Shell code execution, and other features. + +Deployment method: + +```bash +git clone https://github.com/AstrBotDevs/AstrBot +cd AstrBot +# Modify the environment variable configuration in the compose-with-shipyard.yml file, such as Shipyard's access token, etc. +docker compose -f compose-with-shipyard.yml up -d +docker pull soulter/shipyard-ship:latest +``` + +For configuration and usage details, see the [Agent Sandbox Environment](/en/use/astrbot-agent-sandbox.md) documentation. +::: + + +## Deploy with Docker + +```bash +mkdir astrbot +cd astrbot +sudo docker run -itd -p 6185:6185 -p 6199:6199 -v $PWD/data:/AstrBot/data -v /etc/localtime:/etc/localtime:ro -v /etc/timezone:/etc/timezone:ro --name astrbot soulter/astrbot:latest +``` + +> [!TIP] +> If your network environment is in mainland China, the above command will not pull properly. Please use the following command to pull the image: +> +> ```bash +> sudo docker run -itd -p 6185:6185 -p 6199:6199 -v $PWD/data:/AstrBot/data -v /etc/localtime:/etc/localtime:ro -v /etc/timezone:/etc/timezone:ro --name astrbot m.daocloud.io/docker.io/soulter/astrbot:latest +> ``` +> +> (Thanks to DaoCloud ❤️) + +> No need to add sudo on Windows, same below +> Sync Host Time on Windows (requires WSL2) + +``` +-v \\wsl.localhost\(your-wsl-os)\etc\timezone:/etc/timezone:ro +-v \\wsl.localhost\(your-wsl-os)\etc\localtime:/etc/localtime:ro +``` + +View AstrBot logs with the following command: + +```bash +sudo docker logs -f astrbot +``` + +## 🎉 All Done + +If everything goes well, you will see logs printed by AstrBot. + +If there are no errors, you will see a log message similar to `🌈 Dashboard started, accessible at` with several links. Open one of the links to access the AstrBot dashboard. + +> [!TIP] +> Since Docker isolates the network environment, you cannot use `localhost` to access the dashboard. +> +> The default username and password are `astrbot` and `astrbot`. +> +> If deployed on a cloud server, you need to open ports `6180-6200` and `11451` in the cloud provider's console. + +Next, you need to deploy any messaging platform to use AstrBot on that platform. diff --git a/docs/en/deploy/astrbot/kubernetes.md b/docs/en/deploy/astrbot/kubernetes.md new file mode 100644 index 000000000..cb4d9f679 --- /dev/null +++ b/docs/en/deploy/astrbot/kubernetes.md @@ -0,0 +1,197 @@ +# Deploy AstrBot with Kubernetes + +> [!WARNING] +> You can deploy AstrBot in a high-availability setup using Kubernetes (K8s), allowing it to automatically recover from failures. +> +> Due to the current use of an SQLite database, this deployment does not support horizontal scaling with multiple replicas. Additionally, if using the Sidecar mode, pay special attention to the persistence of NapCat's login state. +> +> The following tutorial assumes that you have `kubectl` installed and configured, and that you can connect to your K8s cluster. + +## Prerequisites + +Before you begin, make sure your Kubernetes cluster meets the following conditions: + +1. **Default StorageClass**: Used to dynamically create `PersistentVolumeClaim` (PVC). You can check this with `kubectl get sc`. If you don't have one, you need to manually create a `PersistentVolume` (PV) or install a corresponding storage plugin (e.g., `nfs-client-provisioner`). +2. **Network Access**: Ensure that your cluster nodes can pull images from `docker.io` or your specified image repository. + +## Deployment Methods + +We offer two deployment options: + +* **Integrated Deployment (Sidecar Mode)**: Deploy AstrBot and NapCat in the same Pod. Recommended for personal QQ accounts. +* **Standalone Deployment**: Deploy only AstrBot. Suitable for other platforms or if you want to manage NapCat independently. + +--- + +### Method 1: Deploy with NapCatQQ (Sidecar) + +This method is located in the `k8s/astrbot_with_napcat` directory. + +#### 1. Deploy + +```bash +# 1. Create namespace +kubectl apply -f k8s/astrbot_with_napcat/00-namespace.yaml + +# 2. Create Persistent Volume Claim +# Note: astrbot-data-shared-pvc requires ReadWriteMany (RWX) access mode. +# If your cluster does not support RWX, you need to configure shared storage such as NFS and modify the storageClassName in 01-pvc.yaml. +kubectl apply -f k8s/astrbot_with_napcat/01-pvc.yaml + +# 3. Deploy the application +kubectl apply -f k8s/astrbot_with_napcat/02-deployment.yaml +``` + +#### 2. Expose Service (Choose one) + +* **Option A: NodePort** + + ```bash + kubectl apply -f k8s/astrbot_with_napcat/03-service-nodeport.yaml + ``` + + The service will be exposed via the node IP and a port automatically assigned by Kubernetes. You can find the port with the following command: + + ```bash + kubectl get svc -n astrbot-ns + ``` + + In the output, find the `PORT(S)` column for `astrbot-webui-svc` and `napcat-web-svc`. The format is `:/TCP`. For example, if you see `8080:30185/TCP`, the access address is `http://:30185`. + +* **Option B: LoadBalancer** + + If your cluster supports `LoadBalancer` type services (usually provided in K8s services from cloud providers), you can use this method. + + ```bash + kubectl apply -f k8s/astrbot_with_napcat/04-service-loadbalancer.yaml + ``` + + After execution, check the assigned external IP (EXTERNAL-IP) with `kubectl get svc -n astrbot-ns`. + +#### 3. Configure Connection + +Since AstrBot and NapCat are in the same Pod, they can communicate directly via `localhost`. + +1. **Add a message platform in AstrBot:** + * Go to the AstrBot WebUI, select `Settings` -> `Message Platform` -> `Add`. + * **Select Message Platform Category**: `aiocqhttp` + * **Bot Name**: `napcat` (or custom) + * **Reverse Websocket Host**: `0.0.0.0` + * **Reverse Websocket Port**: `6199` + * Save the configuration. + + +2. **Configure Websocket Client in NapCat:** + * Go to the NapCat WebUI, select `Settings` -> `Reverse WS` -> `Add`. + * **Enable**: On + * **URL**: `ws://localhost:6199/ws` + * **Message Format**: `Array` + * Save the configuration. + + +--- + +### Method 2: Deploy AstrBot Only (General Purpose) + +This method is located in the `k8s/astrbot` directory. + +#### 1. Deploy + +```bash +# 1. Create namespace +kubectl apply -f k8s/astrbot/00-namespace.yaml + +# 2. Create Persistent Volume Claim +kubectl apply -f k8s/astrbot/01-pvc.yaml + +# 3. Deploy the application +kubectl apply -f k8s/astrbot/02-deployment.yaml +``` + +#### 2. Expose Service (Choose one) + +* **Option A: NodePort** + + ```bash + kubectl apply -f k8s/astrbot/03-service-nodeport.yaml + ``` + + The service will be exposed via the node IP and a port automatically assigned by Kubernetes. You can find the port with the following command: + + ```bash + kubectl get svc -n astrbot-standalone-ns + ``` + + In the output, find the `PORT(S)` column for `astrbot-webui-svc`. The format is `:/TCP`. For example, if you see `8080:30185/TCP`, the access address is `http://:30185`. + +* **Option B: LoadBalancer** + + ```bash + kubectl apply -f k8s/astrbot/04-service-loadbalancer.yaml + ``` + + After execution, check the assigned external IP (EXTERNAL-IP) with `kubectl get svc -n astrbot-standalone-ns`. + +--- + +## Advanced Configuration + +### Image Mirror (for users in mainland China) + +If you have difficulty pulling the `soulter/astrbot:latest` or `mlikiowa/napcat-docker:latest` images, you can manually edit the corresponding `02-deployment.yaml` file and replace the `image` field with a domestic mirror address, for example: + +```yaml +# Example: +# image: soulter/astrbot:latest +# Replace with: +image: m.daocloud.io/docker.io/soulter/astrbot:latest +``` + +### Enable Docker Sandbox Code Executor + +If you need to use the sandbox code executor, you need to mount the Docker socket file into the Pod. + +Edit the `02-deployment.yaml` file and add `volumes` and `volumeMounts` under `spec.template.spec`: + +1. **Add the following to the `volumeMounts` list of the `astrbot` container:** + + ```yaml + - name: docker-sock + mountPath: /var/run/docker.sock + ``` + +2. **Add the following to the `spec.template.spec.volumes` list:** + + ```yaml + - name: docker-sock + hostPath: + path: /var/run/docker.sock + type: Socket + ``` + +> [!WARNING] +> Mounting the Docker socket into a Pod poses a security risk. Please ensure you understand the implications. + +## View Logs + +* **Sidecar Deployment Mode:** + + ```bash + # View AstrBot logs + kubectl logs -f -n astrbot-ns deployment/astrbot-stack -c astrbot + + # View NapCat logs + kubectl logs -f -n astrbot-ns deployment/astrbot-stack -c napcat + ``` + +* **Standalone Deployment Mode:** + + ```bash + kubectl logs -f -n astrbot-standalone-ns deployment/astrbot-standalone + ``` + +## 🎉 All Done! + +After deploying and exposing the service, you can access the AstrBot admin panel through the corresponding IP and port. + +> The default username and password are `astrbot` and `astrbot`. \ No newline at end of file diff --git a/docs/en/deploy/astrbot/launcher.md b/docs/en/deploy/astrbot/launcher.md new file mode 100644 index 000000000..87ef896a3 --- /dev/null +++ b/docs/en/deploy/astrbot/launcher.md @@ -0,0 +1,98 @@ +# Deploy AstrBot with AstrBot Launcher + +## Recommended Method 1: AstrBot One-Click Launcher + +AstrBot One-Click Launcher supports Windows, macOS, and Linux. + +0. Open [AstrBotDevs/astrbot-launcher](https://github.com/AstrBotDevs/astrbot-launcher) +1. **Optional but recommended**: give this project a [**Star ⭐**](https://github.com/AstrBotDevs/astrbot-launcher). Your support helps maintainers keep improving it. +2. Find **Releases** on the right, open the latest release, then download the installer for your system from **Assets**. + +For example: + +- Windows x86 users: `AstrBot.Launcher_0.2.1_x64-setup.exe` +- Windows on Arm users: `AstrBot.Launcher_0.2.1_arm64-setup.exe` +- macOS Apple Silicon users: `AstrBot.Launcher_0.2.1_aarch64.dmg` + +For macOS users, if you see "damaged and can't be opened", it is caused by macOS security restrictions on unsigned apps. Fix it with: + +1. Open Terminal. +2. Run: + `xattr -dr com.apple.quarantine /Applications/AstrBot\ Launcher.app` +3. Reopen AstrBot Launcher. + +## Method 2: Legacy Windows Installer + +We still recommend the One-Click Launcher above because it is simpler, more automated, and better for most users. + +The legacy installer is a `PowerShell` script, very small (<20KB). It requires `PowerShell` (usually built in on `Windows 10` and newer). + +> [!WARNING] +> `Python 3.10` or later must be installed, and environment variables must be configured. + +> [!TIP] +> If deployment fails, try Docker deployment or manual deployment instead. + +## Download the Legacy Installer + +Open + +Download `Source code (zip)` and extract it. + +## Run the Legacy Installer + +> The video may be outdated. Follow the steps here. + +After extraction, open the folder. + +Type `PowerShell` in the address bar and press Enter: + +![image](https://files.astrbot.app/docs/source/images/windows/image-4.png) + +Drag `launcher_astrbot_en.bat` into the PowerShell window and press Enter. + +> [!WARNING] +> - The script is safe. If you see `Windows protected your PC`, click `More info` and then `Run anyway`. +> - By default, it uses `python`. If you want to specify another interpreter path/command, edit `launcher_astrbot_en.bat`, find `set PYTHON_CMD=python`, and replace `python` with your own command/path. + +If Python is not detected, the script exits with a prompt. + +The script checks whether an `AstrBot` folder exists. If not, it downloads the latest AstrBot source from [GitHub](https://github.com/AstrBotDevs/AstrBot/releases/latest), installs dependencies, and runs it automatically. + +## Done + +If everything works, you will see AstrBot logs. + +Without errors, you should see a log like `🌈 Management panel started, accessible at` with several URLs. Open one URL to access AstrBot WebUI. + +> [!TIP] +> Default username and password: `astrbot` / `astrbot`. +> +> If WebUI returns 404: +> Download `dist.zip` from [release](https://github.com/AstrBotDevs/AstrBot/releases), extract it into `AstrBot/data`, then restart the computer if needed. + +Then deploy at least one messaging platform adapter to start using AstrBot in IM apps. + +## Error: Python is not installed + +If you still get this error after installing Python and restarting, your PATH is likely incorrect. + +**Method 1** + +Search for Python in Windows and open its file location: + +![image](https://files.astrbot.app/docs/source/images/windows/image.png) + +Right-click the shortcut below and open file location: + +![alt text](https://files.astrbot.app/docs/source/images/windows/image-1.png) + +Copy the file path: + +![image](https://files.astrbot.app/docs/source/images/windows/image-2.png) + +Edit `launcher_astrbot_en.bat` in Notepad, find `set PYTHON_CMD=python`, and replace `python` with your interpreter command/path. Keep quotes if your path contains spaces. + +**Method 2** + +Reinstall Python, check `Add Python to PATH` during installation, then restart your computer. diff --git a/docs/en/deploy/astrbot/other-deployments.md b/docs/en/deploy/astrbot/other-deployments.md new file mode 100644 index 000000000..c42d2c860 --- /dev/null +++ b/docs/en/deploy/astrbot/other-deployments.md @@ -0,0 +1,5 @@ +# Other Deployments + +- [CasaOS Deployment](./casaos.md) +- [Compshare GPU Deployment](./compshare.md) +- [Community Deployments](./community-deployment.md) diff --git a/docs/en/deploy/astrbot/package.md b/docs/en/deploy/astrbot/package.md new file mode 100644 index 000000000..d72961b95 --- /dev/null +++ b/docs/en/deploy/astrbot/package.md @@ -0,0 +1,17 @@ +# Package Manager Deployment (uv) + +Use `uv` to install and run AstrBot quickly. + +## Before You Start + +If `uv` is not installed, install it first by following the official guide: + + +`uv` supports Linux, Windows, and macOS. + +## Install and Start + +```bash +uv tool install astrbot +astrbot +``` diff --git a/docs/en/deploy/astrbot/sys-pm.md b/docs/en/deploy/astrbot/sys-pm.md new file mode 100644 index 000000000..b89080c93 --- /dev/null +++ b/docs/en/deploy/astrbot/sys-pm.md @@ -0,0 +1,41 @@ +# Installation via System Package Manager + +> [!WARNING] +> Currently, only the AUR version is provided. +> If you are a Windows/macOS user, it is recommended to install via `uv`. +> If you are a Linux user, it is highly recommended to install via a package manager. + +# Preparation + +## What is AUR? +AUR (Arch User Repository) allows users to install software from community-maintained software repositories. AUR packages are typically maintained by community members rather than official maintainers. +Common AUR helpers include `yay` and `paru`. +The following tutorial uses `paru` as an example; `yay` works similarly, just replace `paru` with `yay`. + +# Installation Process + +## AUR +```bash +paru -S astrbot-git +# Note: +# The review step will begin; press 'q' to exit review and continue installation. +# After installation, the data directory is fixed at: ~/.local/share/astrbot +``` + +# Starting +>[!TIP] +> You can directly use `astrbot init` (for the first run) to initialize. +> Use `astrbot run` to run the bot. +> However, it is highly recommended to use `systemctl` for starting, as it provides features like automatic restart and log rotation. + +```bash +systemctl --user start astrbot.service +``` + +# Auto-start on Boot +```bash +# For security reasons, it is designed to run as a user. +systemctl --user enable astrbot.service +# If you need to start it immediately, add --now +# systemctl --user enable --now astrbot.service +``` diff --git a/docs/en/deploy/when-deployed.md b/docs/en/deploy/when-deployed.md new file mode 100644 index 000000000..d30245052 --- /dev/null +++ b/docs/en/deploy/when-deployed.md @@ -0,0 +1,16 @@ +# Preface + +After successful deployment... of course, don't forget to give [AstrBot](https://github.com/AstrBotDevs/AstrBot) a Star! + +AstrBot Main Repository: [![wakatime](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018e705a-a1a7-409a-a849-3013485e6c8e.svg)](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018e705a-a1a7-409a-a849-3013485e6c8e) + +AstrBot Dashboard: [![wakatime](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018c440f-c177-45f8-8224-292cdf5926f3.svg)](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018c440f-c177-45f8-8224-292cdf5926f3) + +AstrBot Documentation: [![wakatime](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018c9619-e195-4b94-bd7b-2ca61679145b.svg)](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018c9619-e195-4b94-bd7b-2ca61679145b) + +❤️ Contributions to this project are warmly welcomed, including Issues and Pull Requests. + +## Next... + +If you're reading this, it means you have successfully deployed the messaging platform and sent/received your first command. Next, you can configure large language models or add plugins. Please refer to the `Configuration - Integrating LLM Services` section. + diff --git a/docs/en/dev/astrbot-config.md b/docs/en/dev/astrbot-config.md new file mode 100644 index 000000000..5c5d356f5 --- /dev/null +++ b/docs/en/dev/astrbot-config.md @@ -0,0 +1,557 @@ +--- +outline: deep +--- + +# AstrBot Configuration File + +## data/cmd_config.json + +AstrBot's configuration file is a JSON format file. AstrBot reads this file at startup and initializes based on the settings within. Its path is `data/cmd_config.json`. + +> Since AstrBot v4.0.0, we introduced the concept of [multiple configuration files](https://blog.astrbot.app/posts/what-is-changed-in-4.0.0/#%E5%A4%9A%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6). `data/cmd_config.json` serves as the default configuration `default`. Other configuration files you create in the WebUI are stored in the `data/config/` directory, starting with `abconf_`. + +The default AstrBot configuration is as follows: + +```jsonc +{ + "config_version": 2, + "platform_settings": { + "unique_session": False, + "rate_limit": { + "time": 60, + "count": 30, + "strategy": "stall", # stall, discard + }, + "reply_prefix": "", + "forward_threshold": 1500, + "enable_id_white_list": True, + "id_whitelist": [], + "id_whitelist_log": True, + "wl_ignore_admin_on_group": True, + "wl_ignore_admin_on_friend": True, + "reply_with_mention": False, + "reply_with_quote": False, + "path_mapping": [], + "segmented_reply": { + "enable": False, + "only_llm_result": True, + "interval_method": "random", + "interval": "1.5,3.5", + "log_base": 2.6, + "words_count_threshold": 150, + "regex": ".*?[。?!~…]+|.+$", + "content_cleanup_rule": "", + }, + "no_permission_reply": True, + "empty_mention_waiting": True, + "empty_mention_waiting_need_reply": True, + "friend_message_needs_wake_prefix": False, + "ignore_bot_self_message": False, + "ignore_at_all": False, + }, + "provider": [], + "provider_settings": { + "enable": True, + "default_provider_id": "", + "default_image_caption_provider_id": "", + "image_caption_prompt": "Please describe the image using Chinese.", + "provider_pool": ["*"], # "*" means use all available providers + "wake_prefix": "", + "web_search": False, + "websearch_provider": "default", + "websearch_tavily_key": [], + "web_search_link": False, + "display_reasoning_text": False, + "identifier": False, + "group_name_display": False, + "datetime_system_prompt": True, + "default_personality": "default", + "persona_pool": ["*"], + "prompt_prefix": "{{prompt}}", + "max_context_length": -1, + "dequeue_context_length": 1, + "streaming_response": False, + "show_tool_use_status": False, + "streaming_segmented": False, + "max_agent_step": 30, + "tool_call_timeout": 60, + }, + "provider_stt_settings": { + "enable": False, + "provider_id": "", + }, + "provider_tts_settings": { + "enable": False, + "provider_id": "", + "dual_output": False, + "use_file_service": False, + }, + "provider_ltm_settings": { + "group_icl_enable": False, + "group_message_max_cnt": 300, + "image_caption": False, + "active_reply": { + "enable": False, + "method": "possibility_reply", + "possibility_reply": 0.1, + "whitelist": [], + }, + }, + "content_safety": { + "also_use_in_response": False, + "internal_keywords": {"enable": True, "extra_keywords": []}, + "baidu_aip": {"enable": False, "app_id": "", "api_key": "", "secret_key": ""}, + }, + "admins_id": ["astrbot"], + "t2i": False, + "t2i_word_threshold": 150, + "t2i_strategy": "remote", + "t2i_endpoint": "", + "t2i_use_file_service": False, + "t2i_active_template": "base", + "http_proxy": "", + "no_proxy": ["localhost", "127.0.0.1", "::1"], + "dashboard": { + "enable": True, + "username": "astrbot", + "password": "77b90590a8945a7d36c963981a307dc9", + "jwt_secret": "", + "host": "0.0.0.0", + "port": 6185, + }, + "platform": [], + "platform_specific": { + # Platform-specific settings: categorized by platform, then by feature group + "lark": { + "pre_ack_emoji": {"enable": False, "emojis": ["Typing"]}, + }, + "telegram": { + "pre_ack_emoji": {"enable": False, "emojis": ["✍️"]}, + }, + }, + "wake_prefix": ["/"], + "log_level": "INFO", + "trace_enable": False, + "pip_install_arg": "", + "pypi_index_url": "https://mirrors.aliyun.com/pypi/simple/", + "persona": [], # deprecated + "timezone": "Asia/Shanghai", + "callback_api_base": "", + "default_kb_collection": "", # Default knowledge base name + "plugin_set": ["*"], # "*" means use all available plugins, empty list means none +} +``` + +## Field Details + +### `config_version` + +Configuration version, do not modify. + +### `platform_settings` + +General settings for message platform adapters. + +#### `platform_settings.unique_session` + +Whether to enable session isolation. Default is `false`. When enabled, each person's conversation context in groups or channels is independent. + +#### `platform_settings.rate_limit` + +Strategy when message rate exceeds limits. `time` is the window, `count` is the number of messages, and `strategy` is the limit strategy. `stall` means wait, `discard` means drop. + +#### `platform_settings.reply_prefix` + +Fixed prefix string when replying to messages. Default is empty. + +#### `platform_settings.forward_threshold` + +> Currently only applicable to the QQ platform adapter. + +Message forwarding threshold. When the reply content exceeds a certain number of characters, the bot will fold the message into a QQ group "forwarded message" to prevent spamming. + +#### `platform_settings.enable_id_white_list` + +Whether to enable the ID whitelist. Default is `true`. When enabled, only messages from IDs in the whitelist will be processed. + +#### `platform_settings.id_whitelist` + +ID whitelist. If filled, only message events from the specified IDs will be processed. Empty means the whitelist filter is not enabled. You can use the `/sid` command to get the session ID on a platform. + +Session IDs can also be found in AstrBot logs; when a message fails the whitelist, an INFO level log is output, e.g., `aiocqhttp:GroupMessage:547540978`. + +#### `platform_settings.id_whitelist_log` + +Whether to print logs for messages that fail the ID whitelist. Default is `true`. + +#### `platform_settings.wl_ignore_admin_on_group` & `platform_settings.wl_ignore_admin_on_friend` + +- `wl_ignore_admin_on_group`: Whether group messages from admins bypass the ID whitelist. Default is `true`. + +- `wl_ignore_admin_on_friend`: Whether private messages from admins bypass the ID whitelist. Default is `true`. + +#### `platform_settings.reply_with_mention` + +Whether to @ mention the user when replying. Default is `false`. + +#### `platform_settings.reply_with_quote` + +Whether to quote the user's message when replying. Default is `false`. + +#### `platform_settings.path_mapping` + +*This configuration item has been deprecated since v4.0.0.* + +List of path mappings. Used to replace file paths in messages. Each mapping item contains `from` and `to` fields, indicating that `from` in the message path is replaced with `to`. + +#### `platform_settings.segmented_reply` + +Segmented reply settings. + +- `enable`: Whether to enable segmented replies. Default is `false`. +- `only_llm_result`: Whether to only segment replies generated by the LLM. Default is `true`. +- `interval_method`: Method for segmentation intervals. Options are `random` and `log`. Default is `random`. +- `interval`: Interval time for segmentation. For `random`, fill in two comma-separated numbers representing min and max intervals (seconds). For `log`, fill in one number representing the log base. Default is `"1.5,3.5"`. +- `log_base`: Log base, only applicable when `interval_method` is `log`. Default is `2.6`. +- `words_count_threshold`: Character limit for segmented replies. Only messages shorter than this value will be segmented; longer messages will be sent directly (unsegmented). Default is `150`. +- `regex`: Used to split a message. By default, it splits based on punctuation like periods and question marks. `re.findall(r'', text)`. Default is `".*?[。?!~…]+|.+$"`. +- `content_cleanup_rule`: Removes specified content from segments. Supports regex. For example, `[。?!]` will remove all periods, question marks, and exclamation points. `re.sub(r'', '', text)`. + +#### `platform_settings.no_permission_reply` + +Whether to reply with a "no permission" prompt when a user lacks authority. Default is `true`. + +#### `platform_settings.empty_mention_waiting` + +Whether to enable the empty @ waiting mechanism. Default is `true`. When enabled, if a user sends a message containing only an @ mention of the bot, the bot waits for the user to send the next message within 60 seconds and merges the two for processing. This is particularly useful on platforms that don't support sending @ and voice/images simultaneously. + +#### `platform_settings.empty_mention_waiting_need_reply` + +In the above item (`empty_mention_waiting`), if waiting is triggered, enabling this will make the bot immediately generate an LLM reply. Otherwise, it just waits without replying. Default is `true`. + +#### `platform_settings.friend_message_needs_wake_prefix` + +Whether private messages on platforms require a wake prefix. Default is `false`. When enabled, users must use a wake prefix to trigger a bot response in private chats. + +#### `platform_settings.ignore_bot_self_message` + +Whether to ignore messages sent by the bot itself. Default is `false`. When enabled, the bot won't process its own messages, preventing infinite loops on some platforms. + +#### `platform_settings.ignore_at_all` + +Whether to ignore @all messages. Default is `false`. When enabled, the bot won't respond to messages containing @all. + +### `provider` + +> This item only takes effect in `data/cmd_config.json`; AstrBot does not read this from configuration files in the `data/config/` directory. + +List of configured model service provider settings. + +### `provider_settings` + +General settings for LLM providers. + +#### `provider_settings.enable` + +Whether to enable LLM chat. Default is `true`. + +#### `provider_settings.default_provider_id` + +Default conversation model provider ID. Must be a provider ID already configured in the `provider` list. If empty, the first provider in the list is used. + +#### `provider_settings.default_image_caption_provider_id` + +Default image captioning model provider ID. Must be a provider ID already configured in the `provider` list. If empty, image captioning is disabled. + +This means when a user sends an image, AstrBot uses this provider to generate a text description, which is then used as part of the conversation context. This is useful when the conversation model doesn't support multimodal input. + +#### `provider_settings.image_caption_prompt` + +Prompt template for image captioning. Default is `"Please describe the image using Chinese."`. + +#### `provider_settings.provider_pool` + +*This configuration item is not yet in actual use.* + +#### `provider_settings.wake_prefix` + +Extra trigger condition for LLM chat. For example, if `chat` is filled, messages must start with `/chat` to trigger LLM chat, where `/` is the bot's wake prefix. This is a measure to prevent abuse. + +#### `provider_settings.web_search` + +Whether to enable AstrBot's built-in web search capability. Default is `false`. When enabled, the LLM may automatically search the web and answer based on the content. + +#### `provider_settings.websearch_provider` + +Web search provider type. Default is `default`. Currently supports `default` and `tavily`. + +- `default`: Works best when Google is accessible. If Google fails, it tries Bing and Sogou in order. + +- `tavily`: Uses the Tavily search engine. + +#### `provider_settings.websearch_tavily_key` + +API Key list for the Tavily search engine. Required when using `tavily` as the web search provider. + +#### `provider_settings.web_search_link` + +Whether to prompt the model to include links to search results in the reply. Default is `false`. + +#### `provider_settings.display_reasoning_text` + +Whether to display the model's reasoning process in the reply. Default is `false`. + +#### `provider_settings.identifier` + +Whether to prepend the group member's name to the prompt so the model better understands the group chat state. Default is `false`. Enabling this slightly increases token usage. + +#### `provider_settings.group_name_display` + +Whether to let the model know the name of the group it's in. Default is `false`. This currently only takes effect in the QQ platform adapter. + +#### `provider_settings.datetime_system_prompt` + +Whether to include the current machine date and time in the system prompt. Default is `true`. + +#### `provider_settings.default_personality` + +ID of the default personality to use. Configure personalities in the WebUI. + +#### `provider_settings.persona_pool` + +*This configuration item is not yet in actual use.* + +#### `provider_settings.prompt_prefix` + +User prompt. You can use `{{prompt}}` as a placeholder for user input. If no placeholder is provided, it's prepended to the user input. + +#### `provider_settings.max_context_length` + +When the conversation context exceeds this number, the oldest parts are discarded. One round of chat counts as 1. -1 means no limit. + +#### `provider_settings.dequeue_context_length` + +The number of conversation rounds to discard each time the `max_context_length` limit is triggered. + +#### `provider_settings.streaming_response` + +Whether to enable streaming responses. Default is `false`. When enabled, the model's reply is sent to the user in real-time with a typewriter effect. This only takes effect on WebChat, Telegram, and Lark platforms. + +#### `provider_settings.show_tool_use_status` + +Whether to show tool usage status. Default is `false`. When enabled, the model displays the tool name and input parameters when using a tool. + +#### `provider_settings.streaming_segmented` + +Whether platforms that don't support streaming responses should fall back to segmented replies. Default is `false`. This means if streaming is enabled but the platform doesn't support it, segmented multiple replies are used instead. + +#### `provider_settings.max_agent_step` + +Limit on the maximum number of Agent steps. Default is `30`. Each tool call by the model counts as one step. + +#### `provider_settings.tool_call_timeout` + +Added in `v4.3.5` + +Maximum timeout for tool calls (seconds), default is `60` seconds. + +#### `provider_stt_settings` + +General settings for Speech-to-Text (STT) providers. + +#### `provider_stt_settings.enable` + +Whether to enable STT services. Default is `false`. + +#### `provider_stt_settings.provider_id` + +STT provider ID. Must be an STT provider ID already configured in the `provider` list. + +#### `provider_tts_settings` + +General settings for Text-to-Speech (TTS) providers. + +#### `provider_tts_settings.enable` + +Whether to enable TTS services. Default is `false`. + +#### `provider_tts_settings.provider_id` + +TTS provider ID. Must be a TTS provider ID already configured in the `provider` list. + +#### `provider_tts_settings.dual_output` + +Whether to enable dual output. Default is `false`. When enabled, the bot sends both text and voice messages. + +#### `provider_tts_settings.use_file_service` + +Whether to enable the file service. Default is `false`. When enabled, the bot provides the output voice file as an external HTTP link to the message platform. This depends on the `callback_api_base` configuration. + +#### `provider_ltm_settings` + +General settings for group chat context awareness providers. + +#### `provider_ltm_settings.group_icl_enable` + +Whether to enable group chat context awareness. Default is `false`. When enabled, the bot records group chat conversations to better understand context. + +The context content is placed in the conversation's system prompt. + +#### `provider_ltm_settings.group_message_max_cnt` + +Maximum number of group chat messages to record. Default is `100`. Messages exceeding this count are discarded. + +#### `provider_ltm_settings.image_caption` + +Whether to record images in group chats and automatically generate text descriptions using an image captioning model. Default is `false`. This depends on the `provider_settings.default_image_caption_provider_id` configuration. Use with caution as it can significantly increase API calls and token usage. + +#### `provider_ltm_settings.active_reply` + +- `enable`: Whether to enable active replies. Default is `false`. +- `method`: Method for active replies. Option is `possibility_reply`. +- `possibility_reply`: Probability of an active reply. Default is `0.1`. Only applicable when `method` is `possibility_reply`. +- `whitelist`: ID whitelist for active replies. Only IDs in this list will trigger active replies. Empty means no whitelist filter. You can use the `/sid` command to get the session ID on a platform. + +### `content_safety` + +Content safety settings. + +#### `content_safety.also_use_in_response` + +Whether to also perform content safety checks on LLM replies. Default is `false`. When enabled, bot-generated replies also undergo safety checks to prevent inappropriate content. + +#### `content_safety.internal_keywords` + +Internal keyword detection settings. + +- `enable`: Whether to enable internal keyword detection. Default is `true`. +- `extra_keywords`: List of extra keywords, supports regex. Default is empty. + +#### `content_safety.baidu_aip` + +Baidu AI content moderation settings. + +- `enable`: Whether to enable Baidu AI content moderation. Default is `false`. +- `app_id`: App ID for Baidu AI content moderation. +- `api_key`: API Key for Baidu AI content moderation. +- `secret_key`: Secret Key for Baidu AI content moderation. + +> [!TIP] +> To enable Baidu AI content moderation, please `pip install baidu-aip` first. + +### `admins_id` + +List of administrator IDs. Additionally, you can use `/op` and `/deop` commands to add or remove admins. + +### `t2i` + +Whether to enable Text-to-Image (T2I) functionality. Default is `false`. When enabled, if a user's message exceeds a certain character count, the bot renders the message as an image to improve readability and prevent spamming. Supports Markdown rendering. + +### `t2i_word_threshold` + +Character threshold for T2I. Default is `150`. When a message exceeds this count, the bot renders it as an image. + +### `t2i_strategy` + +Rendering strategy for T2I. Options are `local` and `remote`. Default is `remote`. + +- `local`: Uses AstrBot's local T2I service for rendering. Lower quality but doesn't depend on external services. +- `remote`: Uses a remote T2I service for rendering. Uses the official AstrBot service by default, which offers better quality. + +### `t2i_endpoint` + +AstrBot API address. Used for rendering Markdown images. Effective when `t2i_strategy` is `remote`. Default is empty, meaning the official AstrBot service is used. + +### `t2i_use_file_service` + +Whether to enable the file service. Default is `false`. When enabled, the bot provides the rendered image as an external HTTP link to the message platform. This depends on the `callback_api_base` configuration. + +### `http_proxy` + +HTTP proxy. E.g., `http://localhost:7890`. + +### `no_proxy` + +List of addresses that bypass the proxy. E.g., `["localhost", "127.0.0.1"]`. + +### `dashboard` + +AstrBot WebUI configuration. + +Please do not change the `password` value arbitrarily. It is an `md5` encoded password. Change the password in the control panel. + +- `enable`: Whether to enable the AstrBot WebUI. Default is `true`. +- `username`: Username for the AstrBot WebUI. Default is `astrbot`. +- `password`: Password for the AstrBot WebUI. Default is the `md5` encoded value of `astrbot`. Do not modify directly unless you know what you are doing. +- `jwt_secret`: JWT secret key. AstrBot generates this randomly at initialization. Do not modify unless you know what you are doing. +- `host`: Address the AstrBot WebUI listens on. Default is `0.0.0.0`. +- `port`: Port the AstrBot WebUI listens on. Default is `6185`. + +### `platform` + +> This item only takes effect in `data/cmd_config.json`; AstrBot does not read this from configuration files in the `data/config/` directory. + +List of configured AstrBot message platform adapter settings. + +### `platform_specific` + +Platform-specific settings. Categorized by platform, then by feature group. + +#### `platform_specific..pre_ack_emoji` + +When enabled, AstrBot sends a pre-reply emoji before requesting the LLM to inform the user that the request is being processed. This currently only takes effect in the Lark and Telegram platform adapters. + +##### lark + +- `enable`: Whether to enable pre-reply emojis for Lark messages. Default is `false`. +- `emojis`: List of pre-reply emojis. Default is `["Typing"]`. Refer to [Emoji Documentation](https://open.feishu.cn/document/server-docs/im-v1/message-reaction/emojis-introduce) for emoji names. + +##### telegram + +- `enable`: Whether to enable pre-reply emojis for Telegram messages. Default is `false`. +- `emojis`: List of pre-reply emojis. Default is `["✍️"]`. Telegram only supports a fixed set of reactions; refer to [reactions.txt](https://gist.github.com/Soulter/3f22c8e5f9c7e152e967e8bc28c97fc9). + +### `wake_prefix` + +Wake prefix. Default is `/`. When a message starts with `/`, AstrBot is awakened. + +> [!TIP] +> If the awakened session is not in the ID whitelist, AstrBot will not respond. + +### `log_level` + +Log level. Default is `INFO`. Can be set to `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`. + +### `trace_enable` + +Whether to enable trace recording. Default is `false`. When enabled, AstrBot records execution traces, which can be viewed on the Trace page of the admin panel. + +### `pip_install_arg` + +Arguments for `pip install`. E.g., `-i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple`. + +### `pypi_index_url` + +PyPI index URL. Default is `https://mirrors.aliyun.com/pypi/simple/`. + +### `persona` + +*This configuration item has been deprecated since v4.0.0. Please use the WebUI to configure personalities.* + +List of configured personalities. Each personality contains `id`, `name`, `description`, and `system_prompt` fields. + +### `timezone` + +Timezone setting. Please fill in an IANA timezone name, such as Asia/Shanghai. If empty, the system default timezone is used. See all timezones at: [IANA Time Zone Database](https://data.iana.org/time-zones/tzdb-2021a/zone1970.tab). + +### `callback_api_base` + +Base address for the AstrBot API. Used for file services, plugin callbacks, etc. E.g., `http://example.com:6185`. Default is empty, meaning file services and plugin callbacks are disabled. + +### `default_kb_collection` + +Default knowledge base name. Used for RAG. If empty, no knowledge base is used. + +### `plugin_set` + +List of enabled plugins. `*` means all available plugins are enabled. Default is `["*"]`. diff --git a/docs/en/dev/openapi.md b/docs/en/dev/openapi.md new file mode 100644 index 000000000..c6e9dd3ce --- /dev/null +++ b/docs/en/dev/openapi.md @@ -0,0 +1,51 @@ +--- +outline: deep +--- + +# AstrBot HTTP API + +Starting from v4.18.0, AstrBot provides API Key based HTTP APIs for programmatic access. + +## Quick Start + +1. Create an API key in WebUI - Settings. +2. Include the API key in request headers: + +```http +Authorization: Bearer abk_xxx +``` + +Also supported: + +```http +X-API-Key: abk_xxx +``` + +3. For chat endpoints, `username` is required: + +- `POST /api/v1/chat`: request body must include `username` +- `GET /api/v1/chat/sessions`: query params must include `username` + +## Common Endpoints + +- `POST /api/v1/chat`: send chat message (SSE stream, server generates UUID when `session_id` is omitted) +- `GET /api/v1/chat/sessions`: list sessions for a specific `username` with pagination +- `GET /api/v1/configs`: list available config files +- `POST /api/v1/file`: upload attachment +- `POST /api/v1/im/message`: proactive message via UMO +- `GET /api/v1/im/bots`: list bot/platform IDs + +## Example + +```bash +curl -N 'http://localhost:6185/api/v1/chat' \ + -H 'Authorization: Bearer abk_xxx' \ + -H 'Content-Type: application/json' \ + -d '{"message":"Hello","username":"alice"}' +``` + +## Full API Reference + +Use the interactive docs: + +- https://docs.astrbot.app/scalar.html diff --git a/docs/en/dev/plugin-platform-adapter.md b/docs/en/dev/plugin-platform-adapter.md new file mode 100644 index 000000000..8e65528e2 --- /dev/null +++ b/docs/en/dev/plugin-platform-adapter.md @@ -0,0 +1,185 @@ +--- +outline: deep +--- + +# 开发一个平台适配器 + +AstrBot 支持以插件的形式接入平台适配器,你可以自行接入 AstrBot 没有的平台。如飞书、钉钉甚至是哔哩哔哩私信、Minecraft。 + +我们以一个平台 `FakePlatform` 为例展开讲解。 + +首先,在插件目录下新增 `fake_platform_adapter.py` 和 `fake_platform_event.py` 文件。前者主要是平台适配器的实现,后者是平台事件的定义。 + +## 平台适配器 + +假设 FakePlatform 的客户端 SDK 是这样: + +```py +import asyncio + +class FakeClient(): + '''模拟一个消息平台,这里 5 秒钟下发一个消息''' + def __init__(self, token: str, username: str): + self.token = token + self.username = username + # ... + + async def start_polling(self): + while True: + await asyncio.sleep(5) + await getattr(self, 'on_message_received')({ + 'bot_id': '123', + 'content': '新消息', + 'username': 'zhangsan', + 'userid': '123', + 'message_id': 'asdhoashd', + 'group_id': 'group123', + }) + + async def send_text(self, to: str, message: str): + print('发了消息:', to, message) + + async def send_image(self, to: str, image_path: str): + print('发了消息:', to, image_path) +``` + +我们创建 `fake_platform_adapter.py`: + +```py +import asyncio + +from astrbot.api.platform import Platform, AstrBotMessage, MessageMember, PlatformMetadata, MessageType +from astrbot.api.event import MessageChain +from astrbot.api.message_components import Plain, Image, Record # 消息链中的组件,可以根据需要导入 +from astrbot.core.platform.astr_message_event import MessageSesion +from astrbot.api.platform import register_platform_adapter +from astrbot import logger +from .client import FakeClient +from .fake_platform_event import FakePlatformEvent + +# 注册平台适配器。第一个参数为平台名,第二个为描述。第三个为默认配置。 +@register_platform_adapter("fake", "fake 适配器", default_config_tmpl={ + "token": "your_token", + "username": "bot_username" +}) +class FakePlatformAdapter(Platform): + + def __init__(self, platform_config: dict, platform_settings: dict, event_queue: asyncio.Queue) -> None: + super().__init__(event_queue) + self.config = platform_config # 上面的默认配置,用户填写后会传到这里 + self.settings = platform_settings # platform_settings 平台设置。 + + async def send_by_session(self, session: MessageSesion, message_chain: MessageChain): + # 必须实现 + await super().send_by_session(session, message_chain) + + def meta(self) -> PlatformMetadata: + # 必须实现,直接像下面一样返回即可。 + return PlatformMetadata( + "fake", + "fake 适配器", + ) + + async def run(self): + # 必须实现,这里是主要逻辑。 + + # FakeClient 是我们自己定义的,这里只是示例。这个是其回调函数 + async def on_received(data): + logger.info(data) + abm = await self.convert_message(data=data) # 转换成 AstrBotMessage + await self.handle_msg(abm) + + # 初始化 FakeClient + self.client = FakeClient(self.config['token'], self.config['username']) + self.client.on_message_received = on_received + await self.client.start_polling() # 持续监听消息,这是个堵塞方法。 + + async def convert_message(self, data: dict) -> AstrBotMessage: + # 将平台消息转换成 AstrBotMessage + # 这里就体现了适配程度,不同平台的消息结构不一样,这里需要根据实际情况进行转换。 + abm = AstrBotMessage() + abm.type = MessageType.GROUP_MESSAGE # 还有 friend_message,对应私聊。具体平台具体分析。重要! + abm.group_id = data['group_id'] # 如果是私聊,这里可以不填 + abm.message_str = data['content'] # 纯文本消息。重要! + abm.sender = MessageMember(user_id=data['userid'], nickname=data['username']) # 发送者。重要! + abm.message = [Plain(text=data['content'])] # 消息链。如果有其他类型的消息,直接 append 即可。重要! + abm.raw_message = data # 原始消息。 + abm.self_id = data['bot_id'] + abm.session_id = data['userid'] # 会话 ID。重要! + abm.message_id = data['message_id'] # 消息 ID。 + + return abm + + async def handle_msg(self, message: AstrBotMessage): + # 处理消息 + message_event = FakePlatformEvent( + message_str=message.message_str, + message_obj=message, + platform_meta=self.meta(), + session_id=message.session_id, + client=self.client + ) + self.commit_event(message_event) # 提交事件到事件队列。不要忘记! +``` + + +`fake_platform_event.py`: + +```py +from astrbot.api.event import AstrMessageEvent, MessageChain +from astrbot.api.platform import AstrBotMessage, PlatformMetadata +from astrbot.api.message_components import Plain, Image +from .client import FakeClient +from astrbot.core.utils.io import download_image_by_url + +class FakePlatformEvent(AstrMessageEvent): + def __init__(self, message_str: str, message_obj: AstrBotMessage, platform_meta: PlatformMetadata, session_id: str, client: FakeClient): + super().__init__(message_str, message_obj, platform_meta, session_id) + self.client = client + + async def send(self, message: MessageChain): + for i in message.chain: # 遍历消息链 + if isinstance(i, Plain): # 如果是文字类型的 + await self.client.send_text(to=self.get_sender_id(), message=i.text) + elif isinstance(i, Image): # 如果是图片类型的 + img_url = i.file + img_path = "" + # 下面的三个条件可以直接参考一下。 + if img_url.startswith("file:///"): + img_path = img_url[8:] + elif i.file and i.file.startswith("http"): + img_path = await download_image_by_url(i.file) + else: + img_path = img_url + + # 请善于 Debug! + + await self.client.send_image(to=self.get_sender_id(), image_path=img_path) + + await super().send(message) # 需要最后加上这一段,执行父类的 send 方法。 +``` + +最后,main.py 只需这样,在初始化的时候导入 fake_platform_adapter 模块。装饰器会自动注册。 + +```py +from astrbot.api.star import Context, Star + +class MyPlugin(Star): + def __init__(self, context: Context): + from .fake_platform_adapter import FakePlatformAdapter # noqa +``` + +搞好后,运行 AstrBot: + +![image](https://files.astrbot.app/docs/source/images/plugin-platform-adapter/QQ_1738155926221.png) + +这里出现了我们创建的 fake。 + +![image](https://files.astrbot.app/docs/source/images/plugin-platform-adapter/QQ_1738155982211.png) + +启动后,可以看到正常工作: + +![image](https://files.astrbot.app/docs/source/images/plugin-platform-adapter/QQ_1738156166893.png) + + +有任何疑问欢迎加群询问~ \ No newline at end of file diff --git a/docs/en/dev/plugin.md b/docs/en/dev/plugin.md new file mode 100644 index 000000000..8e2eaf276 --- /dev/null +++ b/docs/en/dev/plugin.md @@ -0,0 +1 @@ +This page has moved to [AstrBot Plugin Development Guide](/en/dev/star/plugin-new). diff --git a/docs/en/dev/star/guides/ai.md b/docs/en/dev/star/guides/ai.md new file mode 100644 index 000000000..53edde3b1 --- /dev/null +++ b/docs/en/dev/star/guides/ai.md @@ -0,0 +1,489 @@ + +# AI + +AstrBot provides built-in support for multiple Large Language Model (LLM) providers and offers a unified interface, making it convenient for plugin developers to access various LLM services. + +You can use the LLM / Agent interfaces provided by AstrBot to implement your own intelligent agents. + +Starting from version `v4.5.7`, we've made significant improvements to the way LLM providers are invoked. We recommend using the new approach, which is more concise and supports additional features. The legacy invocation method remains documented in the previous Chinese-only guide. + +## Getting the Chat Model ID for the Current Session + +> [!TIP] +> Added in v4.5.7 + +```py +umo = event.unified_msg_origin +provider_id = await self.context.get_current_chat_provider_id(umo=umo) +``` + +## Invoking Large Language Models + +> [!TIP] +> Added in v4.5.7 + + +```py +llm_resp = await self.context.llm_generate( + chat_provider_id=provider_id, # Chat model ID + prompt="Hello, world!", +) +# print(llm_resp.completion_text) # Get the returned text +``` + +## Defining Tools + +Tools enable large language models to invoke external capabilities. + +```py +from pydantic import Field +from pydantic.dataclasses import dataclass + +from astrbot.core.agent.run_context import ContextWrapper +from astrbot.core.agent.tool import FunctionTool, ToolExecResult +from astrbot.core.astr_agent_context import AstrAgentContext + + +@dataclass +class BilibiliTool(FunctionTool[AstrAgentContext]): + name: str = "bilibili_videos" # Tool name + description: str = "A tool to fetch Bilibili videos." # Tool description + parameters: dict = Field( + default_factory=lambda: { + "type": "object", + "properties": { + "keywords": { + "type": "string", + "description": "Keywords to search for Bilibili videos.", + }, + }, + "required": ["keywords"], + } + ) + + async def call( + self, context: ContextWrapper[AstrAgentContext], **kwargs + ) -> ToolExecResult: + return "1. Video Title: How to Use AstrBot\nVideo Link: xxxxxx" +``` + +## Invoking Agents + +> [!TIP] +> Added in v4.5.7 + + +An Agent can be defined as a combination of system_prompt + tools + llm, enabling more sophisticated intelligent behavior. + +After defining the Tool above, you can invoke an Agent as follows: + +```py +llm_resp = await self.context.tool_loop_agent( + event=event, + chat_provider_id=prov_id, + prompt="Search for videos related to AstrBot on Bilibili.", + tools=ToolSet([BilibiliTool()]), + max_steps=30, # Maximum agent execution steps + tool_call_timeout=60, # Tool invocation timeout +) +# print(llm_resp.completion_text) # Get the returned text +``` + +`tool_loop_agent()` method automatically handles the loop of tool invocations and LLM requests until the model stops calling tools or the maximum number of steps is reached. + +## Multi-Agent + +> [!TIP] +> Added in v4.5.7 + + +Multi-Agent systems decompose complex applications into multiple specialized agents that collaborate to solve problems. Unlike relying on a single agent to handle every step, multi-agent architectures allow smaller, more focused agents to be composed into coordinated workflows. We implement multi-agent systems using the `agent-as-tool` pattern. + +In the example below, we define a Main Agent responsible for delegating tasks to different Sub-Agents based on user queries. Each Sub-Agent focuses on specific tasks, such as retrieving weather information. + +![multi-agent-example-1](https://files.astrbot.app/docs/en/dev/star/guides/multi-agent-example-1.svg) + +Define Tools: + +```py +@dataclass +class AssignAgentTool(FunctionTool[AstrAgentContext]): + """Main agent uses this tool to decide which sub-agent to delegate a task to.""" + + name: str = "assign_agent" + description: str = "Assign an agent to a task based on the given query" + parameters: dict = field( + default_factory=lambda: { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "The query to call the sub-agent with.", + }, + }, + "required": ["query"], + } + ) + + async def call( + self, context: ContextWrapper[AstrAgentContext], **kwargs + ) -> str | CallToolResult: + # Here you would implement the actual agent assignment logic. + # For demonstration purposes, we'll return a dummy response. + return "Based on the query, you should assign agent 1." + + +@dataclass +class WeatherTool(FunctionTool[AstrAgentContext]): + """In this example, sub agent 1 uses this tool to get weather information.""" + + name: str = "weather" + description: str = "Get weather information for a location" + parameters: dict = field( + default_factory=lambda: { + "type": "object", + "properties": { + "city": { + "type": "string", + "description": "The city to get weather information for.", + }, + }, + "required": ["city"], + } + ) + + async def call( + self, context: ContextWrapper[AstrAgentContext], **kwargs + ) -> str | CallToolResult: + city = kwargs["city"] + # Here you would implement the actual weather fetching logic. + # For demonstration purposes, we'll return a dummy response. + return f"The current weather in {city} is sunny with a temperature of 25°C." + + +@dataclass +class SubAgent1(FunctionTool[AstrAgentContext]): + """Define a sub-agent as a function tool.""" + + name: str = "subagent1_name" + description: str = "subagent1_description" + parameters: dict = field( + default_factory=lambda: { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "The query to call the sub-agent with.", + }, + }, + "required": ["query"], + } + ) + + async def call( + self, context: ContextWrapper[AstrAgentContext], **kwargs + ) -> str | CallToolResult: + ctx = context.context.context + event = context.context.event + logger.info(f"the llm context messages: {context.messages}") + llm_resp = await ctx.tool_loop_agent( + event=event, + chat_provider_id=await ctx.get_current_chat_provider_id( + event.unified_msg_origin + ), + prompt=kwargs["query"], + tools=ToolSet([WeatherTool()]), + max_steps=30, + ) + return llm_resp.completion_text + + +@dataclass +class SubAgent2(FunctionTool[AstrAgentContext]): + """Define a sub-agent as a function tool.""" + + name: str = "subagent2_name" + description: str = "subagent2_description" + parameters: dict = field( + default_factory=lambda: { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "The query to call the sub-agent with.", + }, + }, + "required": ["query"], + } + ) + + async def call( + self, context: ContextWrapper[AstrAgentContext], **kwargs + ) -> str | CallToolResult: + return "I am useless :(, you shouldn't call me :(" +``` + +Then, similarly, invoke the Agent using the `tool_loop_agent()` method: + +```py +@filter.command("test") +async def test(self, event: AstrMessageEvent): + umo = event.unified_msg_origin + prov_id = await self.context.get_current_chat_provider_id(umo) + llm_resp = await self.context.tool_loop_agent( + event=event, + chat_provider_id=prov_id, + prompt="Test calling sub-agent for Beijing's weather information.", + system_prompt=( + "You are the main agent. Your task is to delegate tasks to sub-agents based on user queries." + "Before delegating, use the 'assign_agent' tool to determine which sub-agent is best suited for the task." + ), + tools=ToolSet([SubAgent1(), SubAgent2(), AssignAgentTool()]), + max_steps=30, + ) + yield event.plain_result(llm_resp.completion_text) +``` + +## Conversation Manager + +### Getting the Current LLM Conversation History for a Session + +```py +from astrbot.core.conversation_mgr import Conversation + +uid = event.unified_msg_origin +conv_mgr = self.context.conversation_manager +curr_cid = await conv_mgr.get_curr_conversation_id(uid) +conversation = await conv_mgr.get_conversation(uid, curr_cid) # Conversation +``` + +::: details Conversation 类型定义 + +```py +@dataclass +class Conversation: + """The conversation entity representing a chat session.""" + + platform_id: str + """The platform ID in AstrBot""" + user_id: str + """The user ID associated with the conversation.""" + cid: str + """The conversation ID, in UUID format.""" + history: str = "" + """The conversation history as a string.""" + title: str | None = "" + """The title of the conversation. For now, it's only used in WebChat.""" + persona_id: str | None = "" + """The persona ID associated with the conversation.""" + created_at: int = 0 + """The timestamp when the conversation was created.""" + updated_at: int = 0 + """The timestamp when the conversation was last updated.""" +``` + +::: + +### Main Methods + +#### `new_conversation` + +- **Usage** + Create a new conversation in the current session and automatically switch to it. +- **Arguments** + - `unified_msg_origin: str` – In the format `platform_name:message_type:session_id` + - `platform_id: str | None` – Platform identifier, defaults to parsing from `unified_msg_origin` + - `content: list[dict] | None` – Initial message history + - `title: str | None` – Conversation title + - `persona_id: str | None` – Associated persona ID +- **Returns** + `str` – Newly generated UUID conversation ID + +#### `switch_conversation` + +- **Usage** + Switch the session to a specified conversation. +- **Arguments** + - `unified_msg_origin: str` + - `conversation_id: str` +- **Returns** + `None` + +#### `delete_conversation` + +- **Usage** + Delete a conversation from the session; if `conversation_id` is `None`, deletes the current conversation. +- **Arguments** + - `unified_msg_origin: str` + - `conversation_id: str | None` +- **Returns** + `None` + +#### `get_curr_conversation_id` + +- **Usage** + Get the conversation ID currently in use by the session. +- **Arguments** + - `unified_msg_origin: str` +- **Returns** + `str | None` – Current conversation ID, returns `None` if it doesn't exist + +#### `get_conversation` + +- **Usage** + Get the complete object for a specified conversation; automatically creates it if it doesn't exist and `create_if_not_exists=True`. +- **Arguments** + - `unified_msg_origin: str` + - `conversation_id: str` + - `create_if_not_exists: bool = False` +- **Returns** + `Conversation | None` + +#### `get_conversations` + +- **Usage** + Retrieve the complete list of conversations for a user or platform. +- **Arguments** + - `unified_msg_origin: str | None` – When `None`, does not filter by user + - `platform_id: str | None` +- **Returns** + `List[Conversation]` + +#### `update_conversation` + +- **Usage** + Update the title, history, or persona_id of a conversation. +- **Arguments** + - `unified_msg_origin: str` + - `conversation_id: str | None` – Uses the current conversation when `None` + - `history: list[dict] | None` + - `title: str | None` + - `persona_id: str | None` +- **Returns** + `None` + +## Persona Manager + +`PersonaManager` is responsible for unified loading, caching, and providing CRUD interfaces for all Personas, while maintaining compatibility with the legacy persona format (v3) from before AstrBot 4.x. +During initialization, it automatically reads all personas from the database and generates v3-compatible data for seamless use with legacy code. + +```py +persona_mgr = self.context.persona_manager +``` + +### Main Methods + +#### `get_persona` + +- **Usage** + Get persona data by persona ID. +- **Arguments** + - `persona_id: str` – Persona ID +- **Returns** + `Persona` – Persona data, returns None if it doesn't exist +- **Raises** + `ValueError` – Raised when it doesn't exist + +#### `get_all_personas` + +- **Usage** + Retrieve all personas from the database at once. +- **Returns** + `list[Persona]` – Persona list, may be empty + +#### `create_persona` + +- **Usage** + Create a new persona and immediately write it to the database; automatically refreshes the local cache upon success. +- **Arguments** + - `persona_id: str` – New persona ID (unique) + - `system_prompt: str` – System prompt + - `begin_dialogs: list[str]` – Optional, opening dialogs (even number of entries, alternating user/assistant) + - `tools: list[str]` – Optional, list of allowed tools; `None`=all tools, `[]`=disable all +- **Returns** + `Persona` – Newly created persona object +- **Raises** + `ValueError` – If `persona_id` already exists + +#### `update_persona` + +- **Usage** + Update any fields of an existing persona and synchronize to database and cache. +- **Arguments** + - `persona_id: str` – Persona ID to update + - `system_prompt: str` – Optional, new system prompt + - `begin_dialogs: list[str]` – Optional, new opening dialogs + - `tools: list[str]` – Optional, new tool list; semantics same as `create_persona` +- **Returns** + `Persona` – Updated persona object +- **Raises** + `ValueError` – If `persona_id` doesn't exist + +#### `delete_persona` + +- **Usage** + Delete the specified persona and clean up both database and cache. +- **Arguments** + - `persona_id: str` – Persona ID to delete +- **Raises** + `ValueError` – If `persona_id` doesn't exist + +#### `get_default_persona_v3` + +- **Usage** + Get the default persona (v3 format) to use based on the current session configuration. + Falls back to `DEFAULT_PERSONALITY` if configuration doesn't specify one or the specified persona doesn't exist. +- **Arguments** + - `umo: str | MessageSession | None` – Session identifier, used to read user-level configuration +- **Returns** + `Personality` – Default persona object in v3 format + +::: details Persona / Personality 类型定义 + +```py + +class Persona(SQLModel, table=True): + """Persona is a set of instructions for LLMs to follow. + + It can be used to customize the behavior of LLMs. + """ + + __tablename__ = "personas" + + id: int = Field(primary_key=True, sa_column_kwargs={"autoincrement": True}) + persona_id: str = Field(max_length=255, nullable=False) + system_prompt: str = Field(sa_type=Text, nullable=False) + begin_dialogs: Optional[list] = Field(default=None, sa_type=JSON) + """a list of strings, each representing a dialog to start with""" + tools: Optional[list] = Field(default=None, sa_type=JSON) + """None means use ALL tools for default, empty list means no tools, otherwise a list of tool names.""" + created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) + updated_at: datetime = Field( + default_factory=lambda: datetime.now(timezone.utc), + sa_column_kwargs={"onupdate": datetime.now(timezone.utc)}, + ) + + __table_args__ = ( + UniqueConstraint( + "persona_id", + name="uix_persona_id", + ), + ) + + +class Personality(TypedDict): + """LLM Persona class. + + Starting from v4.0.0 and later, it's recommended to use the Persona class above. Additionally, the mood_imitation_dialogs field has been deprecated. + """ + + prompt: str + name: str + begin_dialogs: list[str] + mood_imitation_dialogs: list[str] + """Mood imitation dialog preset. Deprecated since v4.0.0 and later.""" + tools: list[str] | None + """Tool list. None means use all tools, empty list means don't use any tools""" +``` + +::: diff --git a/docs/en/dev/star/guides/env.md b/docs/en/dev/star/guides/env.md new file mode 100644 index 000000000..7dd0480b9 --- /dev/null +++ b/docs/en/dev/star/guides/env.md @@ -0,0 +1,48 @@ + +# 开发环境准备 + +## 获取插件模板 + +1. 打开 AstrBot 插件模板: [helloworld](https://github.com/Soulter/helloworld) +2. 点击右上角的 `Use this template` +3. 然后点击 `Create new repository`。 +4. 在 `Repository name` 处填写您的插件名。插件名格式: + - 推荐以 `astrbot_plugin_` 开头; + - 不能包含空格; + - 保持全部字母小写; + - 尽量简短。 +5. 点击右下角的 `Create repository`。 + +![New repo](https://files.astrbot.app/docs/source/images/plugin/image.png) + +## Clone 插件和 AstrBot 项目 + +Clone AstrBot 项目本体和刚刚创建的插件仓库到本地。 + +```bash +git clone https://github.com/AstrBotDevs/AstrBot +mkdir -p AstrBot/data/plugins +cd AstrBot/data/plugins +git clone 插件仓库地址 +``` + +然后,使用 `VSCode` 打开 `AstrBot` 项目。找到 `data/plugins/<你的插件名字>` 目录。 + +更新 `metadata.yaml` 文件,填写插件的元数据信息。 + +> [!NOTE] +> AstrBot 插件市场的信息展示依赖于 `metadata.yaml` 文件。 + +## 调试插件 + +AstrBot 采用在运行时注入插件的机制。因此,在调试插件时,需要启动 AstrBot 本体。 + +您可以使用 AstrBot 的热重载功能简化开发流程。 + +插件的代码修改后,可以在 AstrBot WebUI 的插件管理处找到自己的插件,点击右上角 `...` 按钮,选择 `重载插件`。 + +## 插件依赖管理 + +目前 AstrBot 对插件的依赖管理使用 `pip` 自带的 `requirements.txt` 文件。如果你的插件需要依赖第三方库,请务必在插件目录下创建 `requirements.txt` 文件并写入所使用的依赖库,以防止用户在安装你的插件时出现依赖未找到(Module Not Found)的问题。 + +> `requirements.txt` 的完整格式可以参考 [pip 官方文档](https://pip.pypa.io/en/stable/reference/requirements-file-format/)。 diff --git a/docs/en/dev/star/guides/html-to-pic.md b/docs/en/dev/star/guides/html-to-pic.md new file mode 100644 index 000000000..b04e5c119 --- /dev/null +++ b/docs/en/dev/star/guides/html-to-pic.md @@ -0,0 +1,66 @@ + +# Text to Image + +> [!TIP] +> For easier development, you can use the [AstrBot Text2Image Playground](https://t2i-playground.astrbot.app/) for online visual editing and testing of HTML templates. + +## Basic Usage + +AstrBot supports rendering text into images. + +```python +@filter.command("image") # Register an /image command that accepts a text parameter. +async def on_aiocqhttp(self, event: AstrMessageEvent, text: str): + url = await self.text_to_image(text) # text_to_image() is a method of the Star class. + # path = await self.text_to_image(text, return_url = False) # If you want to save the image locally + yield event.image_result(url) + +``` + +![image](https://files.astrbot.app/docs/source/images/plugin/image-3.png) + +## Customization (HTML-Based) + +If you find the default rendered images insufficiently aesthetic, you can use custom HTML templates to render images. + +AstrBot supports rendering text-to-image templates using `HTML + Jinja2`. + +```py{7} +# Custom Jinja2 template with CSS support +TMPL = ''' +
+

Todo List

+ +
    +{% for item in items %} +
  • {{ item }}
  • +{% endfor %} +
+''' + +@filter.command("todo") +async def custom_t2i_tmpl(self, event: AstrMessageEvent): + options = {} # Optionally pass rendering options. + url = await self.html_render(TMPL, {"items": ["Eat", "Sleep", "Play Genshin"]}, options=options) # The second parameter is the data for Jinja2 rendering + yield event.image_result(url) +``` + +The result: + +![image](https://files.astrbot.app/docs/source/images/plugin/fcc2dcb472a91b12899f617477adc5c7.png) + +This is just a simple example. Thanks to the powerful capabilities of HTML and DOM renderers, you can create more complex and visually appealing designs. Additionally, Jinja2 supports syntax for loops, conditionals, and more to accommodate data structures like lists and dictionaries. You can learn more about Jinja2 online. + +**Image Rendering Options (options)**: + +Please refer to Playwright's [screenshot](https://playwright.dev/python/docs/api/class-page#page-screenshot) API. + +- `timeout` (float, optional): Screenshot timeout duration. +- `type` (Literal["jpeg", "png"], optional): Screenshot image type. +- `quality` (int, optional): Screenshot quality, only applicable to JPEG format images. +- `omit_background` (bool, optional): Whether to hide the default white background, allowing transparent screenshots. Only applicable to PNG format. +- `full_page` (bool, optional): Whether to capture the entire page rather than just the viewport size. Defaults to True. +- `clip` (dict, optional): The region to crop after taking the screenshot. Refer to Playwright's screenshot API. +- `animations`: (Literal["allow", "disabled"], optional): Whether to allow CSS animations to play. +- `caret`: (Literal["hide", "initial"], optional): When set to hide, the text cursor will be hidden during the screenshot. Defaults to hide. +- `scale`: (Literal["css", "device"], optional): Page scaling setting. When set to css, device resolution maps one-to-one with CSS pixels, which may result in smaller screenshots on high-DPI screens. When set to device, scaling is based on the device's screen scaling settings or the device_scale_factor parameter in the current Playwright Page/Context. diff --git a/docs/en/dev/star/guides/listen-message-event.md b/docs/en/dev/star/guides/listen-message-event.md new file mode 100644 index 000000000..705ca63f9 --- /dev/null +++ b/docs/en/dev/star/guides/listen-message-event.md @@ -0,0 +1,348 @@ + +# Handling Message Events + +Event listeners can receive message content delivered by the platform and implement features such as commands, command groups, and event listening. + +Event listener decorators are located in `astrbot.api.event.filter` and must be imported first. Please make sure to import it, otherwise it will conflict with Python's built-in `filter` higher-order function. + +```py +from astrbot.api.event import filter, AstrMessageEvent +``` + +## Messages and Events + +AstrBot receives messages delivered by messaging platforms and encapsulates them as `AstrMessageEvent` objects, which are then passed to plugins for processing. + +![message-event](https://files.astrbot.app/docs/en/dev/star/guides/message-event.svg) + +### Message Events + +`AstrMessageEvent` is AstrBot's message event object, which stores information about the message sender, message content, etc. + +### Message Object + +`AstrBotMessage` is AstrBot's message object, which stores the specific content of messages delivered by the messaging platform. The `AstrMessageEvent` object contains a `message_obj` attribute to retrieve this message object. + +```py{11} +class AstrBotMessage: + '''AstrBot's message object''' + type: MessageType # Message type + self_id: str # Bot's identification ID + session_id: str # Session ID. Depends on the unique_session setting. + message_id: str # Message ID + group_id: str = "" # Group ID, empty if it's a private chat + sender: MessageMember # Sender + message: List[BaseMessageComponent] # Message chain. For example: [Plain("Hello"), At(qq=123456)] + message_str: str # The most straightforward plain text message string, concatenating Plain messages (text messages) from the message chain + raw_message: object + timestamp: int # Message timestamp +``` + +Here, `raw_message` is the **raw message object** from the messaging platform adapter. + +### Message Chain + +![message-chain](https://files.astrbot.app/docs/en/dev/star/guides/message-chain.svg) + +A `message chain` describes the structure of a message. It's an ordered list where each element is called a `message segment`. + +Common message segment types include: + +- `Plain`: Text message segment +- `At`: Mention message segment +- `Image`: Image message segment +- `Record`: Audio message segment +- `Video`: Video message segment +- `File`: File message segment + +Most messaging platforms support the above message segment types. + +Additionally, the OneBot v11 platform (QQ personal accounts, etc.) also supports the following common message segment types: + +- `Face`: Emoji message segment +- `Node`: A node in a forward message +- `Nodes`: Multiple nodes in a forward message +- `Poke`: Poke message segment + +In AstrBot, message chains are represented as lists of type `List[BaseMessageComponent]`. + +## Commands + +![message-event-simple-command](https://files.astrbot.app/docs/en/dev/star/guides/message-event-simple-command.svg) + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.star import Context, Star + +class MyPlugin(Star): + def __init__(self, context: Context): + super().__init__(context) + + @filter.command("helloworld") # from astrbot.api.event.filter import command + async def helloworld(self, event: AstrMessageEvent): + '''This is a hello world command''' + user_name = event.get_sender_name() + message_str = event.message_str # Get the plain text content of the message + yield event.plain_result(f"Hello, {user_name}!") +``` + +> [!TIP] +> Commands cannot contain spaces, otherwise AstrBot will parse them as a second parameter. You can use the command group feature below, or use a listener to parse the message content yourself. + +## Commands with Parameters + +![command-with-param](https://files.astrbot.app/docs/en/dev/star/guides/command-with-param.svg) + +AstrBot will automatically parse command parameters for you. + +```python +@filter.command("add") +def add(self, event: AstrMessageEvent, a: int, b: int): + # /add 1 2 -> Result is: 3 + yield event.plain_result(f"Wow! The answer is {a + b}!") +``` + +## Command Groups + +Command groups help you organize commands. + +```python +@filter.command_group("math") +def math(self): + pass + +@math.command("add") +async def add(self, event: AstrMessageEvent, a: int, b: int): + # /math add 1 2 -> Result is: 3 + yield event.plain_result(f"Result is: {a + b}") + +@math.command("sub") +async def sub(self, event: AstrMessageEvent, a: int, b: int): + # /math sub 1 2 -> Result is: -1 + yield event.plain_result(f"Result is: {a - b}") +``` + +The command group function doesn't need to implement any logic; just use `pass` directly or add comments within the function. Subcommands of the command group are registered using `command_group_name.command`. + +When a user doesn't input a subcommand, an error will be reported and the tree structure of the command group will be rendered. + +![image](https://files.astrbot.app/docs/source/images/plugin/image-1.png) + +![image](https://files.astrbot.app/docs/source/images/plugin/898a169ae7ed0478f41c0a7d14cb4d64.png) + +![image](https://files.astrbot.app/docs/source/images/plugin/image-2.png) + +Theoretically, command groups can be nested infinitely! + +```py +''' +math +├── calc +│ ├── add (a(int),b(int),) +│ ├── sub (a(int),b(int),) +│ ├── help (command with no parameters) +''' + +@filter.command_group("math") +def math(): + pass + +@math.group("calc") # Note: this is group, not command_group +def calc(): + pass + +@calc.command("add") +async def add(self, event: AstrMessageEvent, a: int, b: int): + yield event.plain_result(f"Result is: {a + b}") + +@calc.command("sub") +async def sub(self, event: AstrMessageEvent, a: int, b: int): + yield event.plain_result(f"Result is: {a - b}") + +@calc.command("help") +def calc_help(self, event: AstrMessageEvent): + # /math calc help + yield event.plain_result("This is a calculator plugin with add and sub commands.") +``` + +## Command Aliases + +> Available after v3.4.28 + +You can add different aliases for commands or command groups: + +```python +@filter.command("help", alias={'帮助', 'helpme'}) +def help(self, event: AstrMessageEvent): + yield event.plain_result("This is a calculator plugin with add and sub commands.") +``` + +### Event Type Filtering + +#### Receive All + +This will receive all events. + +```python +@filter.event_message_type(filter.EventMessageType.ALL) +async def on_all_message(self, event: AstrMessageEvent): + yield event.plain_result("Received a message.") +``` + +#### Group Chat and Private Chat + +```python +@filter.event_message_type(filter.EventMessageType.PRIVATE_MESSAGE) +async def on_private_message(self, event: AstrMessageEvent): + message_str = event.message_str # Get the plain text content of the message + yield event.plain_result("Received a private message.") +``` + +`EventMessageType` is an `Enum` type that contains all event types. Current event types are `PRIVATE_MESSAGE` and `GROUP_MESSAGE`. + +#### Messaging Platform + +```python +@filter.platform_adapter_type(filter.PlatformAdapterType.AIOCQHTTP | filter.PlatformAdapterType.QQOFFICIAL) +async def on_aiocqhttp(self, event: AstrMessageEvent): + '''Only receive messages from AIOCQHTTP and QQOFFICIAL''' + yield event.plain_result("Received a message") +``` + +In the current version, `PlatformAdapterType` includes `AIOCQHTTP`, `QQOFFICIAL`, `GEWECHAT`, and `ALL`. + +#### Admin Commands + +```python +@filter.permission_type(filter.PermissionType.ADMIN) +@filter.command("test") +async def test(self, event: AstrMessageEvent): + pass +``` + +Only admins can use the `test` command. + +### Multiple Filters + +Multiple filters can be used simultaneously by adding multiple decorators to a function. Filters use `AND` logic, meaning the function will only execute if all filters pass. + +```python +@filter.command("helloworld") +@filter.event_message_type(filter.EventMessageType.PRIVATE_MESSAGE) +async def helloworld(self, event: AstrMessageEvent): + yield event.plain_result("Hello!") +``` + +### Event Hooks + +> [!TIP] +> Event hooks do not support being used together with @filter.command, @filter.command_group, @filter.event_message_type, @filter.platform_adapter_type, or @filter.permission_type. + +#### On Bot Initialization Complete + +> Available after v3.4.34 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.on_astrbot_loaded() +async def on_astrbot_loaded(self): + print("AstrBot initialization complete") + +``` + +#### On LLM Request + +In AstrBot's default execution flow, the `on_llm_request` hook is triggered before calling the LLM. + +You can obtain the `ProviderRequest` object and modify it. + +The ProviderRequest object contains all information about the LLM request, including the request text, system prompt, etc. + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.provider import ProviderRequest + +@filter.on_llm_request() +async def my_custom_hook_1(self, event: AstrMessageEvent, req: ProviderRequest): # Note there are three parameters + print(req) # Print the request text + req.system_prompt += "Custom system_prompt" + +``` + +> You cannot use yield to send messages here. If you need to send, please use the `event.send()` method directly. + +#### On LLM Response Complete + +After the LLM request completes, the `on_llm_response` hook is triggered. + +You can obtain the `ProviderResponse` object and modify it. + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.provider import LLMResponse + +@filter.on_llm_response() +async def on_llm_resp(self, event: AstrMessageEvent, resp: LLMResponse): # Note there are three parameters + print(resp) +``` + +> You cannot use yield to send messages here. If you need to send, please use the `event.send()` method directly. + +#### Before Sending Message + +Before sending a message, the `on_decorating_result` hook is triggered. + +You can implement some message decoration here, such as converting to voice, converting to image, adding prefixes, etc. + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.on_decorating_result() +async def on_decorating_result(self, event: AstrMessageEvent): + result = event.get_result() + chain = result.chain + print(chain) # Print the message chain + chain.append(Plain("!")) # Add an exclamation mark at the end of the message chain +``` + +> You cannot use yield to send messages here. This hook is only for decorating event.get_result().chain. If you need to send, please use the `event.send()` method directly. + +#### After Message Sent + +After a message is sent to the messaging platform, the `after_message_sent` hook is triggered. + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.after_message_sent() +async def after_message_sent(self, event: AstrMessageEvent): + pass +``` + +> You cannot use yield to send messages here. If you need to send, please use the `event.send()` method directly. + +### Priority + +Commands, event listeners, and event hooks can have priority set to execute before other commands, listeners, or hooks. The default priority is `0`. + +```python +@filter.command("helloworld", priority=1) +async def helloworld(self, event: AstrMessageEvent): + yield event.plain_result("Hello!") +``` + +## Controlling Event Propagation + +```python{6} +@filter.command("check_ok") +async def check_ok(self, event: AstrMessageEvent): + ok = self.check() # Your own logic + if not ok: + yield event.plain_result("Check failed") + event.stop_event() # Stop event propagation +``` + +When event propagation is stopped, all subsequent steps will not be executed. + +Assuming there's a plugin A, after A terminates event propagation, all subsequent operations will not be executed, such as executing other plugins' handlers or requesting the LLM. diff --git a/docs/en/dev/star/guides/plugin-config.md b/docs/en/dev/star/guides/plugin-config.md new file mode 100644 index 000000000..538f872b9 --- /dev/null +++ b/docs/en/dev/star/guides/plugin-config.md @@ -0,0 +1,211 @@ + +# Plugin Configuration + +As plugin functionality grows, you may need to define configurations to allow users to customize plugin behavior. + +AstrBot provides "powerful" configuration parsing and visualization features. Users can configure plugins directly in the management panel without modifying code. + +## Configuration Definition + +To register configurations, first add a `_conf_schema.json` JSON file in your plugin directory. + +The file content is a `Schema` that represents the configuration. The Schema is in JSON format, for example: + +```json +{ + "token": { + "description": "Bot Token", + "type": "string", + }, + "sub_config": { + "description": "Test nested configuration", + "type": "object", + "hint": "xxxx", + "items": { + "name": { + "description": "testsub", + "type": "string", + "hint": "xxxx" + }, + "id": { + "description": "testsub", + "type": "int", + "hint": "xxxx" + }, + "time": { + "description": "testsub", + "type": "int", + "hint": "xxxx", + "default": 123 + } + } + } +} +``` + +- `type`: **Required**. The type of the configuration. Supports `string`, `text`, `int`, `float`, `bool`, `object`, `list`, `dict`, `template_list`, `file`. When the type is `text`, it will be visualized as a larger resizable textarea component to accommodate large text. +- `description`: Optional. Description of the configuration. A one-sentence description of the configuration's behavior is recommended. +- `hint`: Optional. Hint information for the configuration, displayed in the question mark button on the right in the image above, shown when hovering over it. +- `obvious_hint`: Optional. Whether the configuration hint should be prominently displayed, like `token` in the image above. +- `default`: Optional. The default value of the configuration. If the user hasn't configured it, the default value will be used. Default values: int is 0, float is 0.0, bool is False, string is "", object is {}, list is []. +- `items`: Optional. If the configuration type is `object`, the `items` field needs to be added. The content of `items` is the sub-Schema of this configuration item. Theoretically, it can be nested infinitely, but excessive nesting is not recommended. +- `invisible`: Optional. Whether the configuration is hidden. Default is `false`. If set to `true`, it will not be displayed in the management panel. +- `options`: Optional. A list, such as `"options": ["chat", "agent", "workflow"]`. Provides dropdown list options. +- `editor_mode`: Optional. Whether to enable code editor mode. Requires AstrBot >= `v3.5.10`. Versions below this won't report errors but won't take effect. Default is false. +- `editor_language`: Optional. The code language for the code editor, defaults to `json`. +- `editor_theme`: Optional. The theme for the code editor. Options are `vs-light` (default) and `vs-dark`. +- `_special`: Optional. Used to call AstrBot's visualization features for provider selection, persona selection, knowledge base selection, etc. See details below. + +When the code editor is enabled, it looks like this: + +![editor_mode](https://files.astrbot.app/docs/source/images/plugin/image-6.png) + +![editor_mode_fullscreen](https://files.astrbot.app/docs/source/images/plugin/image-7.png) + +The **_special** field is only available after v4.0.0. Currently supports `select_provider`, `select_provider_tts`, `select_provider_stt`, `select_persona`, allowing users to quickly select model providers, personas, and other data already configured in the WebUI. Results are all strings. Using select_provider as an example, it will present the following effect: + +![image](https://files.astrbot.app/docs/source/images/plugin/image-select-provider.png) + +### `file` type schema + +Introduced in v4.13.0, this allows plugins to define file-upload configuration items to guide users to upload files required by the plugin. + +```json +{ + "demo_files": { + "type": "file", + "description": "Uploaded files for demo", + "default": [], + "file_types": ["pdf", "docx"] + } +} +``` + +### `dict` type schema + +Used to visualize editing a Python `dict` type configuration. For example, AstrBot Core's custom extra body parameter configuration: + +```py +"custom_extra_body": { + "description": "Custom request body parameters", + "type": "dict", + "items": {}, + "hint": "Used to add extra parameters to requests, such as temperature, top_p, max_tokens, etc.", + "template_schema": { + "temperature": { + "name": "Temperature", + "description": "Temperature parameter", + "hint": "Controls randomness of output, typically 0-2. Higher is more random.", + "type": "float", + "default": 0.6, + "slider": {"min": 0, "max": 2, "step": 0.1}, + }, + "top_p": { + "name": "Top-p", + "description": "Top-p sampling", + "hint": "Nucleus sampling parameter, typically 0-1. Controls probability mass considered.", + "type": "float", + "default": 1.0, + "slider": {"min": 0, "max": 1, "step": 0.01}, + }, + "max_tokens": { + "name": "Max Tokens", + "description": "Maximum tokens", + "hint": "Maximum number of tokens to generate.", + "type": "int", + "default": 8192, + }, + }, +} +``` + +### `template_list` type schema + +> [!NOTE] +> Introduced in v4.10.4. For more details see: [#4208](https://github.com/AstrBotDevs/AstrBot/pull/4208) + +Plugin developers can add a template-style configuration to `_conf_schema` in the following format (somewhat similar to nested configs): + +```json + "field_id": { + "type": "template_list", + "description": "Template List Field", + "templates": { + "template_1": { + "name": "Template One", + "hint":"hint", + "items": { + "attr_a": { + "description": "Attribute A", + "type": "int", + "default": 10 + }, + "attr_b": { + "description": "Attribute B", + "hint": "This is a boolean attribute", + "type": "bool", + "default": true + } + } + }, + "template_2": { + "name": "Template Two", + "hint":"hint", + "items": { + "attr_c": { + "description": "Attribute A", + "type": "int", + "default": 10 + }, + "attr_d": { + "description": "Attribute B", + "hint": "This is a boolean attribute", + "type": "bool", + "default": true + } + } + } + } +} +``` + +Saved config example: + +```json +"field_id": [ + { + "__template_key": "template_1", + "attr_a": 10, + "attr_b": true + }, + { + "__template_key": "template_2", + "attr_c": 10, + "attr_d": true + } +] +``` + +image + + +## Using Configuration in Plugins + +When loading plugins, AstrBot will check if there's a `_conf_schema.json` file in the plugin directory. If it exists, it will automatically parse the configuration and save it under `data/config/_config.json` (a configuration file entity created according to the Schema), and pass it to `__init__()` when instantiating the plugin class. + +```py +from astrbot.api import AstrBotConfig + +class ConfigPlugin(Star): + def __init__(self, context: Context, config: AstrBotConfig): # AstrBotConfig inherits from Dict and has all dictionary methods + super().__init__(context) + self.config = config + print(self.config) + + # Supports direct configuration saving + # self.config.save_config() # Save configuration +``` + +## Configuration Updates + +When you update the Schema across different versions, AstrBot will recursively inspect the configuration items in the Schema, automatically adding default values for missing items and removing those that no longer exist. \ No newline at end of file diff --git a/docs/en/dev/star/guides/send-message.md b/docs/en/dev/star/guides/send-message.md new file mode 100644 index 000000000..417b60ea0 --- /dev/null +++ b/docs/en/dev/star/guides/send-message.md @@ -0,0 +1,131 @@ + +# Sending Messages + +## Passive Messages + +Passive messages refer to the bot responding to messages reactively. + +```python +@filter.command("helloworld") +async def helloworld(self, event: AstrMessageEvent): + yield event.plain_result("Hello!") + yield event.plain_result("你好!") + + yield event.image_result("path/to/image.jpg") # Send an image + yield event.image_result("https://example.com/image.jpg") # Send an image from URL, must start with http or https +``` + +## Active Messages + +Active messages refer to the bot proactively pushing messages. Some platforms may not support active message sending. + +For scheduled tasks or when you don't want to send messages immediately, you can use `event.unified_msg_origin` to get a string and store it, then use `self.context.send_message(unified_msg_origin, chains)` to send messages when needed. + +```python +from astrbot.api.event import MessageChain + +@filter.command("helloworld") +async def helloworld(self, event: AstrMessageEvent): + umo = event.unified_msg_origin + message_chain = MessageChain().message("Hello!").file_image("path/to/image.jpg") + await self.context.send_message(event.unified_msg_origin, message_chain) +``` + +With this feature, you can store the `unified_msg_origin` and send messages when needed. + +> [!TIP] +> About unified_msg_origin. +> `unified_msg_origin` is a string that records the unique ID of a session. AstrBot uses it to identify which messaging platform and which session it belongs to. This allows messages to be sent to the correct session when using `send_message`. For more about MessageChain, see the next section. + +## Rich Media Messages + +AstrBot supports sending rich media messages such as images, audio, videos, etc. Use `MessageChain` to construct messages. + +```python +import astrbot.api.message_components as Comp + +@filter.command("helloworld") +async def helloworld(self, event: AstrMessageEvent): + chain = [ + Comp.At(qq=event.get_sender_id()), # Mention the message sender + Comp.Plain("Check out this image:"), + Comp.Image.fromURL("https://example.com/image.jpg"), # Send image from URL + Comp.Image.fromFileSystem("path/to/image.jpg"), # Send image from local file system + Comp.Plain("This is an image.") + ] + yield event.chain_result(chain) +``` + +The above constructs a `message chain`, which will ultimately send a message containing both images and text while preserving the order. + +> [!TIP] +> In the aiocqhttp message adapter, for messages of type `plain`, the `strip()` method is used during sending to remove spaces and line breaks. You can add zero-width spaces `\u200b` before and after the message to resolve this issue. + +Similarly, + +**File** + +```py +Comp.File(file="path/to/file.txt", name="file.txt") # Not supported by some platforms +``` + +**Audio Record** + +```py +path = "path/to/record.wav" # Currently only accepts wav format, please convert other formats yourself +Comp.Record(file=path, url=path) +``` + +**Video** + +```py +path = "path/to/video.mp4" +Comp.Video.fromFileSystem(path=path) +Comp.Video.fromURL(url="https://example.com/video.mp4") +``` + +## Sending Video Messages + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.command("test") +async def test(self, event: AstrMessageEvent): + from astrbot.api.message_components import Video + # fromFileSystem requires the user's protocol client and bot to be on the same system. + music = Video.fromFileSystem( + path="test.mp4" + ) + # More universal approach + music = Video.fromURL( + url="https://example.com/video.mp4" + ) + yield event.chain_result([music]) +``` + +![Sending video messages](https://files.astrbot.app/docs/source/images/plugin/db93a2bb-671c-4332-b8ba-9a91c35623c2.png) + +## Sending Group Forward Messages + +> Most platforms do not support this message type. Current support: OneBot v11 + +You can send group forward messages as follows. + +```py +from astrbot.api.event import filter, AstrMessageEvent + +@filter.command("test") +async def test(self, event: AstrMessageEvent): + from astrbot.api.message_components import Node, Plain, Image + node = Node( + uin=905617992, + name="Soulter", + content=[ + Plain("hi"), + Image.fromFileSystem("test.jpg") + ] + ) + yield event.chain_result([node]) +``` + +![Sending group forward messages](https://files.astrbot.app/docs/source/images/plugin/image-4.png) diff --git a/docs/en/dev/star/guides/session-control.md b/docs/en/dev/star/guides/session-control.md new file mode 100644 index 000000000..e08bae7ae --- /dev/null +++ b/docs/en/dev/star/guides/session-control.md @@ -0,0 +1,113 @@ + +# Session Control + +> v3.4.36 and above + +Why do we need session control? Consider a Chinese idiom chain game plugin where a user or group needs to have multiple conversations with the bot rather than a one-time command. This is when session control becomes necessary. + +```txt +User: /idiom-chain +Bot: Please send an idiom +User: One horse takes the lead (一马当先) +Bot: Foresight (先见之明) +User: Keen observation (明察秋毫) +... +``` + +AstrBot provides out-of-the-box session control functionality: + +Import: + +```py +import astrbot.api.message_components as Comp +from astrbot.core.utils.session_waiter import ( + session_waiter, + SessionController, +) +``` + +Code within the handler can be written as follows: + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.command("idiom-chain") +async def handle_empty_mention(self, event: AstrMessageEvent): + """Idiom chain game implementation""" + try: + yield event.plain_result("Please send an idiom~") + + # How to use the session controller + @session_waiter(timeout=60, record_history_chains=False) # Register a session controller with a 60-second timeout, without recording message history + async def empty_mention_waiter(controller: SessionController, event: AstrMessageEvent): + idiom = event.message_str # The idiom sent by the user, e.g., "one horse takes the lead" + + if idiom == "exit": # If the user wants to exit the idiom chain game by typing "exit" + await event.send(event.plain_result("Exited the idiom chain game~")) + controller.stop() # Stop the session controller, which will end immediately. + return + + if len(idiom) != 4: # If the user's input is not a 4-character idiom + await event.send(event.plain_result("The idiom must be four characters~")) # Send a reply, cannot use yield + return + # Exit the current method without executing subsequent logic, but the session is not interrupted; subsequent user input will still enter the current session + + # ... + message_result = event.make_result() + message_result.chain = [Comp.Plain("Foresight")] # import astrbot.api.message_components as Comp + await event.send(message_result) # Send a reply, cannot use yield + + controller.keep(timeout=60, reset_timeout=True) # Reset timeout to 60s. If not reset, it will continue the previous timeout countdown. + + # controller.stop() # Stop the session controller, which will end immediately. + # If history chains are recorded, you can retrieve them via controller.get_history_chains() + + try: + await empty_mention_waiter(event) + except TimeoutError as _: # When timeout occurs, the session controller will raise TimeoutError + yield event.plain_result("You timed out!") + except Exception as e: + yield event.plain_result("An error occurred, please contact the administrator: " + str(e)) + finally: + event.stop_event() + except Exception as e: + logger.error("handle_empty_mention error: " + str(e)) +``` + +Once the session controller is activated, messages subsequently sent by that sender will first be processed by the `empty_mention_waiter` function you defined above, until the session controller is stopped or times out. + +## SessionController + +Used by developers to control whether a session should end, and to retrieve message history chains. + +- keep(): Keep this session alive + - timeout (float): Required. Session timeout duration. + - reset_timeout (bool): When set to True, it resets the timeout; timeout must be > 0, if <= 0 the session ends immediately. When set to False, it maintains the original timeout; new timeout = remaining timeout + timeout (can be < 0) +- stop(): End this session +- get_history_chains() -> List[List[Comp.BaseMessageComponent]]: Retrieve message history chains + +## Custom Session ID Filter + +By default, the AstrBot session controller uses `sender_id` (the sender's ID) as the identifier for distinguishing different sessions. If you want to treat an entire group as one session, you need to customize the session ID filter. + +```py +import astrbot.api.message_components as Comp +from astrbot.core.utils.session_waiter import ( + session_waiter, + SessionFilter, + SessionController, +) + +# Using the handler from above +# ... +class CustomFilter(SessionFilter): + def filter(self, event: AstrMessageEvent) -> str: + return event.get_group_id() if event.get_group_id() else event.unified_msg_origin + +await empty_mention_waiter(event, session_filter=CustomFilter()) # Pass in session_filter here +# ... +``` + +After this setup, when a user in a group sends a message, the session controller will treat the entire group as one session, and messages from other users in the group will also be considered part of the same session. + +You can even use this feature to enable team-based activities within groups! diff --git a/docs/en/dev/star/guides/simple.md b/docs/en/dev/star/guides/simple.md new file mode 100644 index 000000000..f2dc11be9 --- /dev/null +++ b/docs/en/dev/star/guides/simple.md @@ -0,0 +1,58 @@ +# Minimal Example + +The `main.py` file in the plugin template is a minimal plugin instance. + +```python +from astrbot.api.event import filter, AstrMessageEvent, MessageEventResult +from astrbot.api.star import Context, Star +from astrbot.api import logger # Use the logger interface provided by AstrBot + +class MyPlugin(Star): + def __init__(self, context: Context): + super().__init__(context) + + # Decorator to register a command. The command name is "helloworld". Once registered, sending `/helloworld` will trigger this command and respond with `Hello, {user_name}!` + @filter.command("helloworld") + async def helloworld(self, event: AstrMessageEvent): + '''This is a hello world command''' # This is the handler's description, which will be parsed to help users understand the plugin's functionality. Highly recommended to provide. + user_name = event.get_sender_name() + message_str = event.message_str # Get the plain text content of the message + logger.info("Hello world command triggered!") + yield event.plain_result(f"Hello, {user_name}!") # Send a plain text message + + async def terminate(self): + '''Optionally implement the terminate function, which will be called when the plugin is uninstalled/disabled.''' +``` + +Explanation: + +- Plugins must inherit from the `Star` class. +- The `Context` class is used for plugin interaction with AstrBot Core, allowing you to call various APIs provided by AstrBot Core. +- Specific handler functions are defined within the plugin class, such as the `helloworld` function here. +- `AstrMessageEvent` is AstrBot's message event object, which stores information about the message sender, message content, etc. +- `AstrBotMessage` is AstrBot's message object, which stores the specific content of messages delivered by the messaging platform. It can be accessed via `event.message_obj`. + +> [!TIP] +> +> Handlers must be registered within the plugin class, with the first two parameters being `self` and `event`. If the file becomes too long, you can write services externally and call them from the handler. +> +> The file containing the plugin class must be named `main.py`. + +All handler functions must be written within the plugin class. To keep content concise, in subsequent sections, we may omit the plugin class definition. +``` + +解释如下: + +- 插件需要继承 `Star` 类。 +- `Context` 类用于插件与 AstrBot Core 交互,可以由此调用 AstrBot Core 提供的各种 API。 +- 具体的处理函数 `Handler` 在插件类中定义,如这里的 `helloworld` 函数。 +- `AstrMessageEvent` 是 AstrBot 的消息事件对象,存储了消息发送者、消息内容等信息。 +- `AstrBotMessage` 是 AstrBot 的消息对象,存储了消息平台下发的消息的具体内容。可以通过 `event.message_obj` 获取。 + +> [!TIP] +> +> `Handler` 一定需要在插件类中注册,前两个参数必须为 `self` 和 `event`。如果文件行数过长,可以将服务写在外部,然后在 `Handler` 中调用。 +> +> 插件类所在的文件名需要命名为 `main.py`。 + +所有的处理函数都需写在插件类中。为了精简内容,在之后的章节中,我们可能会忽略插件类的定义。 diff --git a/docs/en/dev/star/guides/storage.md b/docs/en/dev/star/guides/storage.md new file mode 100644 index 000000000..5e7c5cadf --- /dev/null +++ b/docs/en/dev/star/guides/storage.md @@ -0,0 +1,31 @@ +# Plugin Storage + +## Simple KV Storage + +> [!TIP] +> Requires AstrBot version >= 4.9.2. + +Plugins can use AstrBot's simple key-value store to persist configuration or temporary data. The storage is scoped per plugin, so each plugin has its own isolated space. + +```py +class Main(star.Star): + @filter.command("hello") + async def hello(self, event: AstrMessageEvent): + """Aloha!""" + await self.put_kv_data("greeted", True) + greeted = await self.get_kv_data("greeted", False) + await self.delete_kv_data("greeted") +``` + + +## Large File Storage Convention + +To keep large file handling consistent, store large files under `data/plugin_data/{plugin_name}/`. + +You can fetch the plugin data directory with: + +```py +from astrbot.core.utils.astrbot_path import get_astrbot_data_path + +plugin_data_path = get_astrbot_data_path() / "plugin_data" / self.name # self.name is the plugin name; available in v4.9.2 and above. For lower versions, specify the plugin name yourself. +``` diff --git a/docs/en/dev/star/plugin-new.md b/docs/en/dev/star/plugin-new.md new file mode 100644 index 000000000..3c5971814 --- /dev/null +++ b/docs/en/dev/star/plugin-new.md @@ -0,0 +1,128 @@ +--- +outline: deep +--- + +# AstrBot Plugin Development Guide 🌠 + +Welcome to the AstrBot Plugin Development Guide! This section will guide you through developing AstrBot plugins. Before we begin, we hope you have the following foundational knowledge: + +1. Some experience with Python programming. +2. Some experience with Git and GitHub. + +## Environment Setup + +### Obtain the Plugin Template + +1. Open the AstrBot plugin template: [helloworld](https://github.com/Soulter/helloworld) +2. Click `Use this template` in the upper right corner +3. Then click `Create new repository`. +4. Fill in your plugin name in the `Repository name` field. Plugin naming conventions: + - Recommended to start with `astrbot_plugin_`; + - Must not contain spaces; + - Keep all letters lowercase; + - Keep it concise. +5. Click `Create repository` in the lower right corner. + +### Clone the Project Locally + +Clone both the AstrBot main project and the plugin repository you just created to your local machine. + +```bash +git clone https://github.com/AstrBotDevs/AstrBot +mkdir -p AstrBot/data/plugins +cd AstrBot/data/plugins +git clone +``` + +Then, use `VSCode` to open the `AstrBot` project. Navigate to the `data/plugins/` directory. + +Update the `metadata.yaml` file with your plugin's metadata information. + +> [!WARNING] +> Please make sure to modify this file, as AstrBot relies on the `metadata.yaml` file to recognize plugin metadata. + +### Set Plugin Logo (Optional) + +You can add a `logo.png` file in the plugin directory as the plugin's logo. Please maintain an aspect ratio of 1:1, with a recommended size of 256x256. + +![Plugin logo example](https://files.astrbot.app/docs/source/images/plugin/plugin_logo.png) + +### Plugin Display Name (Optional) + +You can modify (or add) the `display_name` field in the `metadata.yaml` file to serve as the plugin's display name in scenarios like the plugin marketplace, making it easier for users to read. + +### Declare Supported Platforms (Optional) + +You can add a `support_platforms` field (`list[str]`) to `metadata.yaml` to declare which platform adapters your plugin supports. The WebUI plugin page will display this field. + +```yaml +support_platforms: + - telegram + - discord +``` + +The values in `support_platforms` must be keys from `ADAPTER_NAME_2_TYPE`. Currently supported: + +- `aiocqhttp` +- `qq_official` +- `telegram` +- `wecom` +- `lark` +- `dingtalk` +- `discord` +- `slack` +- `kook` +- `vocechat` +- `weixin_official_account` +- `satori` +- `misskey` +- `line` + +### Declare AstrBot Version Range (Optional) + +You can add an `astrbot_version` field in `metadata.yaml` to declare the required AstrBot version range for your plugin. The format follows dependency specifiers in `pyproject.toml` (PEP 440), and must not include a `v` prefix. + +```yaml +astrbot_version: ">=4.16,<5" +``` + +Examples: + +- `>=4.17.0` +- `>=4.16,<5` +- `~=4.17` + +If you only want to declare a minimum version, use: + +- `>=4.17.0` + +If the current AstrBot version does not satisfy this range, the plugin will be blocked from loading with a compatibility error. +In the WebUI installation flow, you can choose to "Ignore Warning and Install" to bypass this check. + +### Debugging Plugins + +AstrBot uses a runtime plugin injection mechanism. Therefore, when debugging plugins, you need to start the AstrBot main application. + +You can use AstrBot's hot reload feature to streamline the development process. + +After modifying the plugin code, you can find your plugin in the AstrBot WebUI's plugin management section, click the `...` button in the upper right corner, and select `Reload Plugin`. + +If the plugin fails to load due to code errors or other reasons, you can also click **"Try one-click reload fix"** in the error prompt on the admin panel to reload it. + +### Plugin Dependency Management + +Currently, AstrBot manages plugin dependencies using pip's built-in `requirements.txt` file. If your plugin requires third-party libraries, please be sure to create a `requirements.txt` file in the plugin directory and list the dependencies used, to prevent Module Not Found errors when users install your plugin. + +> For the complete format of `requirements.txt`, please refer to the [pip official documentation](https://pip.pypa.io/en/stable/reference/requirements-file-format/). + +## Development Principles + +Thank you for contributing to the AstrBot ecosystem. Please follow these principles when developing plugins, which are also good programming practices: + +- Features must be tested. +- Include comprehensive comments. +- Store persistent data in the `data` directory, not in the plugin's own directory, to prevent data loss when updating/reinstalling the plugin. +- Implement robust error handling mechanisms; don't let a single error crash the plugin. +- Before committing, please use the [ruff](https://docs.astral.sh/ruff/) tool to format your code. +- Do not use the `requests` library for network requests; use asynchronous network request libraries such as `aiohttp` or `httpx`. +- If you're extending functionality for an existing plugin, please prioritize submitting a PR to that plugin rather than creating a separate one (unless the original plugin author has stopped maintaining it). diff --git a/docs/en/dev/star/plugin-publish.md b/docs/en/dev/star/plugin-publish.md new file mode 100644 index 000000000..29864fd32 --- /dev/null +++ b/docs/en/dev/star/plugin-publish.md @@ -0,0 +1,9 @@ +# Publishing Plugins to the Plugin Marketplace + +After completing your plugin development, you can choose to publish it to the AstrBot Plugin Marketplace, allowing more users to benefit from your work. + +AstrBot uses GitHub to host plugins, so you'll need to push your plugin code to the GitHub plugin repository you created earlier. + +You can submit your plugin by visiting the [AstrBot Plugin Marketplace](https://plugins.astrbot.app). Once on the website, click the `+` button in the bottom-right corner, fill in the basic information, author details, repository information, and other required fields. Then click the `Submit to GITHUB` button. You will be redirected to the AstrBot repository's Issue submission page. Please verify that all information is correct, then click the `Create` button to complete the plugin publication process. + +![fill out the form](https://files.astrbot.app/docs/source/images/plugin-publish/image.png) diff --git a/docs/en/faq.md b/docs/en/faq.md new file mode 100644 index 000000000..3955f544f --- /dev/null +++ b/docs/en/faq.md @@ -0,0 +1,79 @@ +# FAQ + +## Dashboard Related + +### Encountering 404 Error When Opening the Dashboard + +Download `dist.zip` from the [release](https://github.com/AstrBotDevs/AstrBot/releases) page, extract it, and move it to `AstrBot/data`. If it still doesn't work, try restarting your computer (based on community feedback). + +### Forgot Dashboard Password + +If you forgot your AstrBot dashboard password, you can modify the `"dashboard"` field in the `AstrBot/data/cmd_config.json` configuration file, where `"username"` is your username and `"password"` is your password encrypted with MD5. + +To modify your account credentials, follow these steps: + +1. Modify the `"username"` field, keeping the `""` quotation marks. If you don't want to change the username, skip this step +2. Visit the website: [Online MD5 Generator](https://www.metools.info/code/c26.html) +3. Enter your new password in the input text box +4. Select MD5 encryption (32-bit), make sure to choose the 32-bit option +5. Paste the converted string into the configuration file, keeping the `""` quotation marks + +## Bot Core Related + +### How to Let AstrBot Control My Mac / Windows / Linux Computer? + +1. In AstrBot WebUI's `Config -> General Config`, find `Use Computer Capabilities`, and select `local` for the runtime environment. +2. In `Config -> Other Config`, find `Admin ID List`, and add your user ID (you can get it through the `/sid` command). + +> [!TIP] +> For security reasons, when runtime environment is set to `local`, AstrBot only allows AstrBot administrators to use computer capabilities by default. +> You can select `sandbox` for the runtime environment, which allows all users to use computer capabilities (in an isolated sandbox). For more details, see [AstrBot Sandbox Environment](/en/use/astrbot-agent-sandbox.md) + +### Bot Cannot Chat in Group Conversations + +1. In group chats, to prevent message flooding, the bot will not respond to every monitored message. Please try mentioning (@) the bot or using a wake word to chat, such as the default `/`, for example: `/hello`. + +### No Permission to Execute Admin Commands + +1. `/reset, /persona, /dashboard_update, /op, /deop, /wl, /dewl` are the default admin commands. You can use the `/sid` command to get a user's ID, then add it to the admin ID list in Settings -> Other Settings. + +### Chinese Characters Garbled When Locally Rendering Markdown Images (t2i) + +You can customize the font. See details -> [#957](https://github.com/AstrBotDevs/AstrBot/issues/957#issuecomment-2749981802) + +Recommended font: [Maple Mono](https://github.com/subframe7536/maple-font). + +### Cannot Parse API Returned Completion & LLM Returns `` + +This is because the provider's API returned empty text. Try the following steps: + +1. Check if the API key is still valid +2. Check if the API call limit or quota has been reached +3. Check network connection +4. Try reset +5. Lower the maximum conversation count setting +6. Switch to another model from the same provider / a different provider + +## Plugin Related + +### Cannot Install Plugin + +1. Plugins are installed via GitHub. Access to GitHub from mainland China can indeed be unstable. You can use a proxy, then go to Other Settings -> HTTP Proxy to configure it. Alternatively, download the plugin archive directly and upload it. + +### Error `No module named 'xxx'` After Installing Plugin + +![image](https://files.astrbot.app/docs/source/images/faq/image.png) + +This is because the plugin's dependencies were not installed properly. Normally, AstrBot automatically installs plugin dependencies after installing the plugin, but installation may fail in the following situations: + +1. Network issues preventing dependency downloads +2. Plugin author did not include a `requirements.txt` file +3. Python version incompatibility + +Solution: + +Based on the error message, refer to the plugin's README to manually install dependencies. You can install dependencies in the AstrBot WebUI under `Console` -> `Install Pip Package`. + +![image](https://files.astrbot.app/docs/source/images/faq/image-1.png) + +If you find that the plugin author did not include a `requirements.txt` file, please submit an issue in the plugin repository to remind the author to add it. diff --git a/docs/en/index.md b/docs/en/index.md new file mode 100644 index 000000000..add09acdf --- /dev/null +++ b/docs/en/index.md @@ -0,0 +1,31 @@ +--- +# https://vitepress.dev/reference/default-theme-home-page +layout: home + +hero: + name: >- + Soulter%2FAstrBot | Trendshift + text: "Agentic AI assistant for personal and group chats" + tagline: Connect any IM / 1000+ plugins / General Agent Orchestration + actions: + - theme: brand + text: Quick Start + link: /en/what-is-astrbot + - theme: alt + text: GitHub Repository + link: https://github.com/AstrBotDevs/AstrBot + +features: + - icon: ✨ + title: Multi-Platform Support + details: Seamlessly supports multiple messaging platforms including QQ, WeCom, Telegram, Discord, and more with multi-instance deployment. + - icon: 😌 + title: User-Friendly + details: Easy deployment via Docker or Windows one-click installer with no complex configuration required. Features a highly visual management dashboard. + - icon: 🧩 + title: Highly Extensible + details: Built on event bus and pipeline architecture with full modularity. All features can be enabled or disabled, with comprehensive plugin development support. + - icon: 🌟 + title: Large Language Models + details: Compatible with multiple model providers including OpenAI, Anthropic, Google, Ollama, Deepseek, and more, supporting diverse LLM integrations. +--- diff --git a/docs/en/ospp/2025.md b/docs/en/ospp/2025.md new file mode 100644 index 000000000..b4511363f --- /dev/null +++ b/docs/en/ospp/2025.md @@ -0,0 +1,31 @@ +# 开源之夏 2025 + +**开源之夏**是由中国科学院软件研究所“开源软件供应链点亮计划”发起并长期支持的一项暑期开源活动,旨在鼓励在校学生积极参与开源软件的开发维护,培养和发掘更多优秀的开发者,促进优秀开源软件社区的蓬勃发展,助力开源软件供应链建设。具体活动信息请参考 [开源之夏官网](https://summer-ospp.ac.cn/)。 + +AstrBot 社区有幸作为开源社区参与了本次活动,下面列出了目前我们已经发布的项目,欢迎感兴趣的同学们参与。 + +## 插件数据存储逻辑优化 + +目前,AstrBot 插件系统在数据存储方面缺乏一致的架构。部分插件使用 SharedPreference 存储机制和 JSON 格式进行数据持久化。这种多样化的存储方式导致了存储逻辑的不统一,既影响了数据的安全性,也增加了插件间的兼容性问题。此外,缺乏标准化的接口使得插件的数据存储和访问方式各异,给系统的维护和扩展带来挑战。本项目旨在重构当前存储方案,引入更安全且高效的数据存储机制,并设计一个统一的插件数据接口模型,规范插件的数据存储与访问,提升系统的安全性、可扩展性和可维护性,为未来插件的开发与管理提供坚实基础。 + +**项目链接**:[插件数据存储逻辑优化](https://summer-ospp.ac.cn/org/prodetail/253550342?lang=zh&list=pro) + +**难度**:进阶 + +**导师**:[Soulter](https://github.com/Soulter) + +**期望完成时间**:210 小时 + +**项目产出要求**: + +1. 设计并实现统一且高效的插件数据存储接口模型,规范插件的数据存储; +2. 重构当前 SharedPreference 的存储逻辑,采用更安全的存储方式; +3. 补充相关技术文档。 + +**项目技术要求**: + +1. 熟悉 Python、Javascript 语言及 asyncio 异步编程技术; +2. 熟悉 SQLite 等关系型数据库相关开发; +3. 熟悉 AstrBot 框架及插件开发。 + +**成果仓库**:[https://github.com/AstrBotDevs/AstrBot](https://github.com/AstrBotDevs/AstrBot) diff --git a/docs/en/others/self-host-t2i.md b/docs/en/others/self-host-t2i.md new file mode 100644 index 000000000..85638c657 --- /dev/null +++ b/docs/en/others/self-host-t2i.md @@ -0,0 +1,28 @@ +# Self-host the Text-to-Image Service + +AstrBot uses [AstrBotDevs/astrbot-t2i-service](https://github.com/AstrBotDevs/astrbot-t2i-service) as the default text-to-image service. The default service endpoints are: + +```plain +https://t2i.soulter.top/text2img +https://t2i.rcfortress.site/text2img +``` + +This interface can ensure normal response for most of the time. However, due to the deployment of servers in New York, the response speed may be slower in some areas. + +> [!TIP] +> If you'd like to support us to help pay for server costs, please consider supporting us on [Afdian](https://afdian.com/a/astrbot_team). + +You can choose to self-host the text-to-image service to improve response speed. + +```bash +docker run -itd -p 8999:8999 soulter/astrbot-t2i-service:latest +``` + +After deployment, go to AstrBot Dashboard -> Config -> System, and change `Text-to-Image Service API Endpoint` to the URL you deployed (as shown below). + +> If you deployed AstrBot using the Docker tutorial in this documentation, the URL should be `http://:8999`. + +> If you deployed on the same machine as AstrBot, the URL should be `http://localhost:8999`. + +image + diff --git a/docs/en/platform/aiocqhttp/lagrange.md b/docs/en/platform/aiocqhttp/lagrange.md new file mode 100644 index 000000000..c2be79b68 --- /dev/null +++ b/docs/en/platform/aiocqhttp/lagrange.md @@ -0,0 +1,55 @@ +# Connect to Lagrange + +> [!TIP] +> - Please control message frequency responsibly. Sending messages too frequently may trigger risk control. +> - This project must not be used for illegal purposes. +> - For the latest deployment steps, always refer to the official [Lagrange Docs](https://lagrangedev.github.io/Lagrange.Doc/Lagrange.OneBot/Config/#%E4%B8%8B%E8%BD%BD%E5%AE%89%E8%A3%85). + +## Download + +Download the latest `Lagrange.OneBot` from [GitHub Releases](https://github.com/LagrangeDev/Lagrange.Core/releases). + +- Windows: `Lagrange.OneBot_win-x64_xxxx` +- Linux x86_64: `Lagrange.OneBot_linux-x64_xxx` +- Linux ARM64: `Lagrange.OneBot_linux-arm64_xxx` +- macOS Apple Silicon: `Lagrange.OneBot_osx-arm64_xxx` +- macOS Intel: `Lagrange.OneBot_osx-x64_xxx` + +## Deploy + +Follow the official docs: + +- Run guide: +- Config file guide: + +In your config file, add this under `Implementations`: + +```json +{ + "Type": "ReverseWebSocket", + "Host": "127.0.0.1", + "Port": 6199, + "Suffix": "/ws", + "ReconnectInterval": 5000, + "HeartBeatInterval": 5000, + "AccessToken": "" +} +``` + +Make sure `Suffix` is exactly `/ws`. + +## Connect to AstrBot + +### Configure `aiocqhttp` Adapter + +1. Open AstrBot Dashboard. +2. Click `Bots` in the left sidebar. +3. Click `+ Create Bot`. +4. Select `aiocqhttp (OneBot v11)`. + +Fill in: + +- ID (`id`): any unique identifier. +- Enable (`enable`): checked. +- Reverse WebSocket host: your machine IP (usually `0.0.0.0`). +- Reverse WebSocket port: an available port, for example `6199`. diff --git a/docs/en/platform/aiocqhttp/napcat.md b/docs/en/platform/aiocqhttp/napcat.md new file mode 100644 index 000000000..bc081721a --- /dev/null +++ b/docs/en/platform/aiocqhttp/napcat.md @@ -0,0 +1,141 @@ +# Using NapCat + +> [!TIP] +> +> - Please control usage frequency appropriately. Sending messages too frequently may be identified as abnormal behavior, increasing the risk of triggering risk control mechanisms. +> - This project is strictly prohibited from being used for any purpose that violates laws and regulations. If you intend to use AstrBot for illegal industries or activities, we **explicitly oppose and refuse** your use of this project. +> - AstrBot connects to the OneBot v11 protocol through the `aiocqhttp` adapter. OneBot v11 protocol is an open communication protocol and does not represent any specific software or service. + +NapCat's GitHub Repository: [NapCat](https://github.com/NapNeko/NapCatQQ) +NapCat's Documentation: [NapCat Documentation](https://napcat.napneko.icu/) + +NapCat provides multiple deployment methods, including Docker, Windows one-click installation packages, and more. + +## Deploy via One-Click Script + +This deployment method is recommended. + +### Windows + +Refer to this article: [NapCat.Shell - Windows Manual Start Tutorial](https://napneko.github.io/guide/boot/Shell#napcat-shell-win%E6%89%8B%E5%8A%A8%E5%90%AF%E5%8A%A8%E6%95%99%E7%A8%8B) + +### Linux + +Refer to this article: [NapCat.Installer - Linux One-Click Script (Supports Ubuntu 20+/Debian 10+/Centos9)](https://napneko.github.io/guide/boot/Shell#napcat-installer-linux%E4%B8%80%E9%94%AE%E4%BD%BF%E7%94%A8%E8%84%9A%E6%9C%AC-%E6%94%AF%E6%8C%81ubuntu-20-debian-10-centos9) + +> [!TIP] +> **Where to open Napcat WebUI**: +> The WebUI link will be displayed in napcat's logs. +> +> If napcat is deployed via Linux command line one-click deployment: `docker log `. +> +> For Docker-deployed NapCat: `docker logs napcat`. + +## Deploy via Docker Compose + +> [!TIP] +> If deploying with Docker Compose, no configuration is needed on the NapCat side. Just log in via NapCat WebUI (running on port 6099) or `docker logs napcat`, enable the aiocqhttp adapter on the AstrBot side to connect, and you can directly implement normal receiving and sending of `voice data` and `file data`. + +1. Download or copy the content of [astrbot.yml](https://github.com/NapNeko/NapCat-Docker/blob/main/compose/astrbot.yml) +2. Rename the downloaded file to `astrbot.yml` +3. Modify `astrbot.yml`, change `#- "6199:6199` to `- "6199:6199"`, remove the flag of "#" +4. Execute in the directory where the `astrbot.yml` file is located: + +```bash +NAPCAT_UID=$(id -u) NAPCAT_GID=$(id -g) docker compose -f ./astrbot.yml up -d +``` + +## Deploy via Docker + +> [!TIP] +> If deploying with Docker, you will not be able to properly receive `voice data` and `file data`. This means voice-to-text and sandbox file input functions will not be available. You can receive text messages, image messages, and other types of messages. + +This tutorial assumes you have Docker installed. + +Execute the following command in the terminal for one-click deployment. + +```bash +docker run -d \ +-e NAPCAT_GID=$(id -g) \ +-e NAPCAT_UID=$(id -u) \ +-p 3000:3000 \ +-p 3001:3001 \ +-p 6099:6099 \ +--name napcat \ +--restart=always \ +mlikiowa/napcat-docker:latest +``` + +After successful execution, you need to check the logs to get the login QR code and the management panel URL. + +```bash +docker logs napcat +``` + +Please copy the management panel URL and open it in your browser. + +Then use the account you want to log in with to scan the QR code that appears. + +If there are no issues during the login stage, deployment is successful. + +## Connect to AstrBot + +## Configure aiocqhttp in AstrBot + +1. Enter AstrBot's management panel +2. Click `Bots` in the left sidebar +3. Then in the interface on the right, click `+ Create Bot` +4. Select `OneBot v11` + +Fill in the configuration items that appear: +- ID(id): Fill in arbitrarily, only used to distinguish different messaging platform instances. +- Enable: Check this. +- Reverse WebSocket Host Address: Please fill in your machine's IP address, generally fill in `0.0.0.0` directly +- Reverse WebSocket Port: Fill in a port, default is `6199`. +- Reverse Websocket Token: Only needs to be filled when a token is configured in NapCat's network settings. + +Example image: (At the fastest, just check Enable, then save) + +xinjianya + + +Click `Save`. + +### Configure Administrator + +After filling in, go to the `Configuration File` page, click the `Platform Configuration` tab, find `Administrator ID`, and fill in your account number (not the bot's account number). + +Remember to click `Save` in the lower right corner, AstrBot will restart and apply the configuration. + +### Add WebSocket Client in NapCat + +Switch back to NapCat's management panel, click `Network Configuration->New->WebSockets Client`. + +jiaochenXJY + + +In the newly opened window: + +- Check `Enable`. +- Fill in `URL` with `ws://HostIP:Port/ws`. For example, `ws://localhost:6199/ws` or `ws://127.0.0.1:6199/ws`. + +> [!IMPORTANT] +> 1. If deploying with Docker and both AstrBot and NapCat containers are connected to the same network, use `ws://astrbot:6199/ws` (refer to the Docker script in this documentation). +> 2. Due to Docker network isolation, when not on the same network, please use the internal network IP address or public network IP address ***(unsafe)*** to connect, i.e., `ws://(internal/public IP):6199/ws`. + +- Message Format: `Array` +- Heartbeat Interval: `5000` +- Reconnection Interval: `5000` + +> [!WARNING] +> +> 1. Remember to add `/ws` at the end! +> 2. The IP here cannot be `0.0.0.0` + +Click `Save`. + +Go to AstrBot WebUI `Console`, if you see the blue log ` aiocqhttp(OneBot v11) adapter connected.`, it means the connection is successful. If not, and after several seconds ` aiocqhttp adapter has been closed` appears, it indicates connection timeout (failed), please check if the configuration is correct. + +## 🎉 All Done + +At this point, your AstrBot and NapCat should be successfully connected! Use `private message` to send `/help` to the bot to check if the connection is successful. diff --git a/docs/en/platform/aiocqhttp/others.md b/docs/en/platform/aiocqhttp/others.md new file mode 100644 index 000000000..8d1c560ce --- /dev/null +++ b/docs/en/platform/aiocqhttp/others.md @@ -0,0 +1 @@ +AstrBot can connect to any bot protocol client that supports OneBot v11 reverse WebSocket (AstrBot acts as the server side). diff --git a/docs/en/platform/dingtalk.md b/docs/en/platform/dingtalk.md new file mode 100644 index 000000000..9b21f7cdf --- /dev/null +++ b/docs/en/platform/dingtalk.md @@ -0,0 +1,65 @@ +# Connect to DingTalk + +## Supported Basic Message Types + +> Version v4.15.0. + +| Message Type | Receive | Send | Notes | +| --- | --- | --- | --- | +| Text | Yes | Yes | | +| Image | Yes | Yes | | +| Voice | No | Yes | | +| Video | No | Yes | | +| File | No | Yes | | + +Proactive message push: Supported. + +## Create and Configure the App + +Go to the [DingTalk Open Platform](https://open-dev.dingtalk.com/fe/app), then create an app: + +![image](https://files.astrbot.app/docs/source/images/dingtalk/image-4.png) + +After creation, add app capability and choose Bot: + +![image](https://files.astrbot.app/docs/source/images/dingtalk/image-5.png) + +Open Bot settings and fill in bot information: + +![image](https://files.astrbot.app/docs/source/images/dingtalk/image-7.png) + +After confirming all settings, click Publish. + +Go to Credentials & Basic Information, then copy `ClientID` and `ClientSecret`. + +## Connect in AstrBot + +Open AstrBot Dashboard -> `Bots` -> `+ Create Bot`, then create a DingTalk adapter. + +Fill in `ClientID` and `ClientSecret`, then click Save. AstrBot will request authorization from DingTalk Open Platform automatically. + +Back in DingTalk Open Platform, open Event Subscriptions, select `Stream mode push`, and click Save. If successful, you will see a connected status. + +![image](https://files.astrbot.app/docs/source/images/dingtalk/image-8.png) + +Save the configuration. + +## Publish a Version + +In the left sidebar, open Version Management and Release, then create a new version. + +Fill in version number, description, and visibility scope (all employees or as needed), then save and publish. + +![alt text](https://files.astrbot.app/docs/source/images/dingtalk/image-11.png) + +Open a DingTalk group chat and click the top-right settings: + +![image](https://files.astrbot.app/docs/source/images/dingtalk/image-12.png) + +Scroll down to Add Bot, select the bot you just created, and add it: + +![image](https://files.astrbot.app/docs/source/images/dingtalk/image-9.png) + +## Done + +In a group chat, mention the bot and send `/help`. If the bot replies, the integration is successful. diff --git a/docs/en/platform/discord.md b/docs/en/platform/discord.md new file mode 100644 index 000000000..4298ce38c --- /dev/null +++ b/docs/en/platform/discord.md @@ -0,0 +1,74 @@ +# Connecting to Discord + +## Create AstrBot Discord Platform Adapter + +Navigate to the messaging platform, click to add a new adapter, find Discord and click to enter the Discord configuration page. + +![Click to create bot, select discord type](https://files.astrbot.app/docs/source/images/discord/image.png) + +![Options from top to bottom: 1. Bot name 2. Enable 3. Bot token 4. Discord proxy address 5. Auto-register plugin commands as Discord slash commands 6. discord_guild_id_for_debug 7. Discord activity name](https://files.astrbot.app/docs/source/images/discord/image-3.png) +> For this tutorial, you only need to configure items 1, 2, 3, and 5 + +- Bot Name: Customize this to easily distinguish between different adapters +- Enable: Check to enable this adapter +- Bot Token: Token obtained after creating an App in Discord (see below) +- Discord Proxy Address: If you need to use a proxy to access Discord, you can enter the proxy address here (optional) +- Auto-register Plugin Commands as Discord Slash Commands: When checked, AstrBot will automatically register commands from installed plugins as Discord slash commands for user convenience. + +## Create an App in Discord + +1. Go to [Discord Developer Portal](https://discord.com/developers/applications), click the blue button in the top right corner, enter an application name, and create the application. + +![Create bot (enter name)](https://files.astrbot.app/docs/source/images/discord/image-1.png) + +2. Click on Bot in the left sidebar, click the Reset Token button. After the token is created, click the Copy button and paste the token into the Discord Bot Token field in the configuration. + +![Token options](https://files.astrbot.app/docs/source/images/discord/image-4.png) + +3. Scroll down and enable all three of these options: + +![Presence Intent, Server Members Intent, Message Content Intent screenshot](https://files.astrbot.app/docs/source/images/discord/image-2.png) + +- Presence Intent: Allows the bot to access user online status +- Server Members Intent: Allows the bot to access server member information +- Message Content Intent: Allows the bot to read message content + +4. Click OAuth2 in the left sidebar, and in the OAuth2 URL Generator, select `Bot` +Like this: +![OAuth2 URL Generator](https://files.astrbot.app/docs/source/images/discord/image-6.png) +Then in the Bot Permissions section that appears below, select the allowed permissions. Generally, it's recommended to add the following permissions: + - Send Messages + - Create Public Threads + - Create Private Threads + - Send TTS Messages + - Manage Messages + - Manage Threads + - Embed Links + - Attach Files + - Read Message History + - Add Reactions +If you find this tedious, you can directly use administrator permissions, but it's still recommended to use the permissions configured above (or the permissions you specifically need) in your production environment. + +> Remember, the higher the permissions, the greater the risk. + +5. Copy the Generated URL that appears below. Open this URL to add the bot to your desired server. +![Generated URL location](https://files.astrbot.app/docs/source/images/discord/image-5.png) + +6. Enter your Discord server, your bot should now show as online +![Bot online](https://files.astrbot.app/docs/source/images/discord/image-7.png) +@ mention the bot you just created (or don't mention it), type `/help`. If it responds successfully, the test is successful. + +## Pre-acknowledgment Emoji + +Discord supports the pre-acknowledgment emoji feature. When enabled, the bot will add an emoji reaction when processing a message, letting users know the bot is working on their request. + +In the admin panel's "Configuration" page, find `Platform Specific -> Discord -> Pre-acknowledgment Emoji`: + +- **Enable Pre-acknowledgment Emoji**: When enabled, the bot will automatically add an emoji reaction upon receiving a message +- **Emoji List**: Enter Unicode emoji symbols, e.g., 👍, 🤔, ⏳. You can add multiple emojis, and the bot will randomly select one to use + +# Troubleshooting + +- If you're stuck at the final step and the bot is not online, please ensure your server can directly connect to Discord + +If you have any questions, please [submit an Issue](https://github.com/AstrBotDevs/AstrBot/issues). diff --git a/docs/en/platform/kook.md b/docs/en/platform/kook.md new file mode 100644 index 000000000..96714566b --- /dev/null +++ b/docs/en/platform/kook.md @@ -0,0 +1,46 @@ +# Connect to KOOK + +## Supported Message Types + +> Version v4.19.2 + +| Message Type | Receive | Send | Remarks | +| ------------ | ------- | ---- | -------------------------------------------------- | +| Text | Yes | Yes | Supports official [kmarkdown] syntax | +| Image | Yes | Yes | Supports external links; `jpeg`, `gif`, `png` only | +| Audio | Yes | Yes | Supports external links | +| Video | Yes | Yes | Supports external links; `mp4`, `mov` only | +| File | Yes | Yes | Supports external links | +| Card (JSON) | Yes | Yes | See [Kook Docs - Card Messages] | + +Proactive message push: Supported +Message receiving mode: WebSocket + +## Create a Bot on Kook + +1. Go to the [Kook Developer Center] and follow these steps: +2. Log in and complete identity verification. +3. Click "Create Application" and customize your Bot's nickname. +4. Enter the application dashboard, select the **Bot** module, and enable **WebSocket connection mode**. Make sure to save the generated **Token**, as you will need it for the subsequent AstrBot configuration. +5. Under the "Bot" page in the left sidebar, click "Invite Link" and set the role permissions (full permissions are recommended to ensure all features work). +6. Copy the invite link, open it in your browser, and add the bot to your desired server. + + ![image](https://files.astrbot.app/docs/source/images/kook/image-1.png) + +## Configure in AstrBot + +1. Access the AstrBot management panel. +2. Click **Bots** in the left sidebar. +3. Click `+ Create Bot` on the right side of the interface. +4. Select the `kook` adapter. +5. Fill in the configuration fields: + - ID (id): Any name to identify this specific instance. + - Enable (enable): Check the box. + - Bot Token: Paste the Token generated from the [Kook Developer Center]. + +6. Click `Save` after filling in the details. +7. Finally, in a Kook server channel (create one first if you haven't), @ the bot and type `/sid`. If the bot responds, the configuration is successful. + +[Kook Developer Center]: https://developer.kookapp.cn/app +[kmarkdown]: https://developer.kookapp.cn/doc/kmarkdown +[Kook Docs - Card Messages]: https://developer.kookapp.cn/doc/cardmessage diff --git a/docs/en/platform/lark.md b/docs/en/platform/lark.md new file mode 100644 index 000000000..852ed3b0e --- /dev/null +++ b/docs/en/platform/lark.md @@ -0,0 +1,115 @@ +# Connecting to Lark + +## Supported Message Types + +> Version v4.15.0. + +| Message Type | Receive Support | Send Support | Notes | +| --- | --- | --- | --- | +| Text | Yes | Yes | | +| Image | Yes | Yes | | +| Voice | No | Yes | | +| Video | No | Yes | | +| File | No | Yes | | + +Proactive message push: Supported. + +## Creating a Bot + +Navigate to the [Developer Console](https://open.feishu.cn/app) and create a custom enterprise application. + +![Create Custom Enterprise Application](https://files.astrbot.app/docs/source/images/lark/image.png) + +Add the Bot capability to your application. + +![Add Bot Capability](https://files.astrbot.app/docs/source/images/lark/image-1.png) + +Click on "Credentials & Basic Info" to obtain your app_id and app_secret. + +![Get app_id and app_secret](https://files.astrbot.app/docs/source/images/lark/image-4.png) + +## Configuring AstrBot + +1. Access the AstrBot management panel +2. Click on `Bots` in the left sidebar +3. In the right panel, click `+ Create Bot` +4. Select `lark` + +Fill in the configuration fields as follows: + +- ID: Choose any identifier to distinguish between different messaging platform instances +- Enable: Check this option +- app_id: The app_id you obtained earlier +- app_secret: The app_secret you obtained earlier +- Bot name: Your Lark bot's name + +For the domain field, if you're using Lark China, keep the default value. If you're using Lark International, set it to `https://open.larksuite.com`. If you're using a self-hosted enterprise Lark instance, enter your Lark instance's domain. + +For the subscription method, `socket` uses a long connection subscription approach, while `webhook` sends events to your developer server and requires a public server. Generally, `socket` is recommended. However, if you're using Lark International or a self-hosted Lark instance, choose `webhook`. The subsequent configuration steps will differ accordingly. + +If you selected the `webhook` method, navigate to the Lark Developer Console, click on "Events & Callbacks," then "Encryption Policy," and fill in the Encrypt Key. While not mandatory, AstrBot takes your data security seriously, so we strongly recommend setting this up. After filling it in, copy the `Encrypt Key` and `Verification Token` to the corresponding `encrypt_key` and `verification_token` fields in AstrBot's configuration. + +Click `Save`. + +## Setting up Callbacks and Permissions + +The following steps vary depending on the subscription method you selected above. Please proceed to the corresponding section based on your choice. + +### `socket` Long Connection Method + +Next, click on "Events & Callbacks," select "Receive events using long connection," and click Save. **If the previous step didn't start successfully, you won't be able to save here.** + +![Configure Events & Callbacks](https://files.astrbot.app/docs/source/images/lark/image-6.png) + +### `webhook` Send Events to Developer Server Method + +> [!TIP] +> To make better use of this method, please refer to [Unified Webhook Mode](/en/use/unified-webhook.md#how-to-use-unified-webhook-mode) for the necessary configuration. + +After clicking `Save`, the bot card will display "View Webhook URL." Click to view and copy the callback URL. + +![](https://files.astrbot.app/docs/source/images/lark/webhook.png) + +Next, return to Lark's Events & Callbacks page, click "Event Configuration," select "Send events to developer server," enter the callback URL you just copied as the "Request URL," and click Save. If everything is correct, no errors will appear. + +### Setting up Events + +After completing the event configuration in the previous step, click "Add Event," navigate to "Messages & Groups," scroll down to find `Receive Message`, and add it. + +![Add Event](https://files.astrbot.app/docs/source/images/lark/image-7.png) + +Click to enable the following permissions. + +![Enable Permissions](https://files.astrbot.app/docs/source/images/lark/image-8.png) + +Then click the `Save` button at the top. + +Next, click on "Permission Management," click "Enable Permissions," and enter `im:message:send,im:message,im:message:send_as_bot`. Add the filtered permissions. + +Enter `im:resource:upload,im:resource` again to enable image upload permissions. + +The final set of permissions should look like this: + +![Final Permissions](https://files.astrbot.app/docs/source/images/lark/image-11.png) + +## Creating a Version + +Create a new version. + +![Create Version](https://files.astrbot.app/docs/source/images/lark/image-2.png) + +Fill in the version number, update notes, and visibility scope, then click Save and confirm the release. + +## Adding the Bot to a Group + +Open the Lark app (the web version doesn't support adding bots), enter a group chat, click the button in the upper right corner → Group Bots → Add Bot. + +Search for the bot you just created. For example, if you created the `AstrBot` bot as shown in this tutorial: + +![Add Bot](https://files.astrbot.app/docs/source/images/lark/image-9.png) + +## 🎉 All Done! + +Send a `/help` command in the group, and the bot will respond. + +![Success](https://files.astrbot.app/docs/source/images/lark/image-13.png) diff --git a/docs/en/platform/line.md b/docs/en/platform/line.md new file mode 100644 index 000000000..884a84166 --- /dev/null +++ b/docs/en/platform/line.md @@ -0,0 +1,79 @@ +# Connecting to LINE + +## Supported Message Types + +> Version v4.17.0. + +| Message Type | Receive Support | Send Support | Notes | +| --- | --- | --- | --- | +| Text | Yes | Yes | | +| Image | Yes | Yes | | +| Voice | Yes | Yes | | +| Video | Yes | Yes | | +| File | Yes | Yes | | +| Sticker | Yes | No | | + +Proactive message push: Supported. + +## Create a LINE Messaging API Channel + +1. Open the [LINE Developers Console](https://developers.line.biz/console/) +2. Create or select a Provider +3. Create a `Messaging API` channel (not a `LINE Login` channel) +4. Complete bot initialization on the `Messaging API` page + +## Get Credentials + +You need the following values: + +- `channel_secret` +- `channel_access_token` + +How to get them: + +1. Open your channel settings page +2. Get `Channel secret` from `Basic settings` +3. Issue a `Channel access token` on the `Messaging API` page + +![](https://files.astrbot.app/docs/source/images/line/7ecee0a9102f191245330f8408eb0493.png) + +## Configure AstrBot + +1. Open the AstrBot admin panel +2. Click `Bots` in the left sidebar +3. Click `+ Create Bot` +4. Select `line` + +Fill in these fields: + +- `ID`: Custom identifier to distinguish instances +- `Enable`: Checked +- `LINE Channel Access Token`: your `channel_access_token` +- `LINE Channel Secret`: your `channel_secret` +- `LINE Bot User ID`: optional; if empty, AstrBot uses webhook `destination` + +Click Save. + +## Configure Callback URL (Unified Webhook) + +The LINE adapter supports **unified webhook mode only**. + +After saving, click `View Webhook URL` on the bot card and copy the URL. + +Then in LINE Developers Console: + +1. Open `Messaging API` +2. Paste the URL into `Webhook settings` -> `Webhook URL` +3. Click `Verify` +4. Enable `Use webhook` + +> [!TIP] +> If AstrBot is not publicly reachable, set up a public domain and reverse proxy first so LINE can access your webhook URL. + +## Test + +1. Add your Official Account as a friend in LINE +2. Send a message to the bot (for example, `hi`) +3. If the bot replies, setup is successful + +If you want to use it in a group, invite the Official Account to the group first. diff --git a/docs/en/platform/matrix.md b/docs/en/platform/matrix.md new file mode 100644 index 000000000..17d673def --- /dev/null +++ b/docs/en/platform/matrix.md @@ -0,0 +1,20 @@ +# Connecting to Matrix + +> [!TIP] +> This platform adapter is maintained by the community ([stevessr](https://github.com/stevessr)). If you find it helpful, please support the developer by giving the repository a Star. ❤️ + +## Installing the astrbot_plugin_matrix_adapter Plugin + +Go to the AstrBot WebUI plugin marketplace, search for `astrbot_plugin_matrix_adapter`, and click Install. + +After installation, navigate to Messaging Platforms → Add Adapter → Select Matrix (if the option is missing, try restarting AstrBot or check the plugin installation status). + +Click `Enable` in the configuration dialog that appears. + +## Configuration + +Please refer to the repository's [README.md](https://github.com/stevessr/astrbot_plugin_matrix_adapter?tab=readme-ov-file#astrbot-matrix-adapter-%E6%8F%92%E4%BB%B6) for configuration instructions. + +## Issue Reporting + +If you have any questions, please submit an issue to the [plugin repository](https://github.com/stevessr/astrbot_plugin_matrix_adapter/issues). diff --git a/docs/en/platform/misskey.md b/docs/en/platform/misskey.md new file mode 100644 index 000000000..801dd572f --- /dev/null +++ b/docs/en/platform/misskey.md @@ -0,0 +1,113 @@ +# Connecting to Misskey Platform + +> [!WARNING] +> +> 1. We recommend that before deploying a bot on a Misskey instance you don't manage, you should review the instance rules or seek approval from the instance administration or moderation team, and enable the `Bot` identifier for the bot account after deployment. +> 2. This project is strictly prohibited from being used for any illegal purposes. If you intend to use AstrBot for illegal industries or activities, we explicitly oppose and refuse your use of this project. + +## Create AstrBot Misskey Platform Adapter + +Navigate to the messaging platform, click to add a new adapter, find Misskey and click to enter the Misskey configuration page. + +![Create Misskey Platform Adapter](https://files.astrbot.app/docs/source/images/misskey/create.png) + +## Configure Platform Adapter Settings + +On the AstrBot Misskey platform adapter configuration page, we need to fill in the Misskey connection information and configure some adapter behaviors. + +::: tip Note +Don't forget to click `Enable` before saving to activate the Misskey platform adapter! +::: + +How to obtain the Misskey connection information is described below. + +![Misskey Platform Adapter Configuration](https://files.astrbot.app/docs/source/images/misskey/config.png) + +## Misskey Instance URL + +This is the frontend address of the Misskey instance where your bot account is located, in standard domain format. For example, `https://misskey.example`. + +## Obtain Bot Account Access Token + +1. First, open the Misskey Web frontend page, find and open the `Settings > Connected Services` page in the frontend sidebar. + +![Open Misskey Connected Services Page](https://files.astrbot.app/docs/source/images/misskey/pat-1.png) + +2. Click "Generate Access Token" to generate an account access token. + +![Generate Misskey Account Token](https://files.astrbot.app/docs/source/images/misskey/pat-2.png) + +3. On the access token configuration page that appears, give the token a name, such as `AstrBot`. + +4. Then we need to configure the relevant permissions for the token to allow the bot to interact with the Misskey instance. + +::: tip Note +If third-party AstrBot plugins you use require additional permissions, please refer to their documentation to add the corresponding permissions. If you fully trust the bot's deployment environment, you can temporarily enable all permissions to simplify debugging, but we still recommend limiting the bot's permissions in production environments. +::: + +![Configure Access Token Permissions](https://files.astrbot.app/docs/source/images/misskey/pat-3.png) + +**Permissions Required by Default** + +| Permission Name | Description | Purpose | +|---|---:|---| +| Read account information | View basic account information | Obtain bot's own user information and account ID | +| Compose or delete posts | Create, edit, and delete note content | Send message replies and publish content | +| Compose or delete messages | Create, edit, and delete direct messages | Handle direct message conversations | +| View notifications | Receive system notifications and reminders | Obtain mention, reply, and other notification information | +| View messages | Read direct messages and chat history | Receive and process user direct messages | +| View reactions | View replies and reactions to posts | Handle user responses to bot messages | + +5. After completing the permission configuration, click "Done" to view the account access token. Copy the obtained token and paste it into the Access Token input box on the AstrBot configuration page. + +![View Account Token](https://files.astrbot.app/docs/source/images/misskey/pat-4.png) + +## Default Post Visibility + +Modify the default visibility when the bot posts + +| Name | Description | +|---|---| +| public | Anyone can see the bot's posts | +| home | Publish bot posts to the instance home timeline | +| followers | Only users who follow the bot account can see bot posts in the home timeline | + +## Local Only (Do Not Federate) + +When enabled, all posts sent by the bot will not participate in Fediverse federation. This is very suitable for scenarios where you only want to use and distribute the bot's posts within your own instance. + +## Enable Chat Message Response + +::: tip Note +Misskey's "Chat" component feature is not supported by all Misskey Fork versions! It cannot federate across instances. + +Misskey added "Chat" component support in `v2025.4.0` and later versions, and it is only supported by its web frontend, not well-supported by third-party apps. +::: + +Enabled by default. When enabled, the bot will respond to private chat messages sent by users in Misskey chat. + +## History Records + +Conversation history for individual users in chat and posts will be recorded in the AstrBot WebUI console "Conversation History" with the ID `chat:UserID`, while traditional posts will be recorded with the ID `note:UserID`. + +::: tip Where is the Misskey user's UserID? +It can be found on the user's personal page in the `Raw` section. UserID is the unique key identifier for Misskey users within a single instance. +::: + +![UserID](https://files.astrbot.app/docs/source/images/misskey/userid.png) + +## Test the Connection + +After completing the configuration and enabling it, go to Misskey to create a new post and mention the bot (@mention) to test. If the bot account successfully triggers a reply, the configuration is successful. + +![Demo Example](https://files.astrbot.app/docs/source/images/misskey/demo.png) + +## Additional Notes + +We recommend enabling the Misskey `Bot` identifier for bot accounts to respect the relevant regulations and rate limits of various Misskey instances, which can also effectively help Misskey instance administrators manage and identify bot usage. + +**How to Enable** + +Enable "This is a bot account" in the advanced settings of the bot account's profile page. + +![This is a bot account](https://files.astrbot.app/docs/source/images/misskey/botset.png) diff --git a/docs/en/platform/qqofficial.md b/docs/en/platform/qqofficial.md new file mode 100644 index 000000000..b4237a03b --- /dev/null +++ b/docs/en/platform/qqofficial.md @@ -0,0 +1,8 @@ +# Connect QQ Official Bot + +QQ Official Bot is Tencent's official bot platform. It lets you connect bots to QQ group chats and private chats through official APIs. + +Currently, the main integration method is Webhook. + +- [Webhook Method](/en/platform/qqofficial/webhook) +- [Websockets Method](/en/platform/qqofficial/websockets) diff --git a/docs/en/platform/qqofficial/webhook.md b/docs/en/platform/qqofficial/webhook.md new file mode 100644 index 000000000..8ed67fdc1 --- /dev/null +++ b/docs/en/platform/qqofficial/webhook.md @@ -0,0 +1,93 @@ +# Connect QQ via QQ Official Bot (Webhook) + +> [!WARNING] +> 1. QQ Official Bot currently requires an IP whitelist. +> 2. It supports group chat, private chat, channel chat, and channel private chat. +> 3. You need a server with a public IP and a domain. + +## Supported Basic Message Types + +> Version v4.15.0. + +| Message Type | Receive | Send | Notes | +| --- | --- | --- | --- | +| Text | Yes | Yes | | +| Image | Yes | Yes | | +| Voice | No | No | | +| Video | No | No | | +| File | No | No | | + +Proactive message push: Not supported. + +## Apply for a Bot + +Open [QQ Official Bot](https://q.qq.com) and sign in. + +Create a bot, fill in name/description/avatar, then submit for review. After security verification passes, creation is complete. + +Open the created bot to enter its management page: + +![image](https://files.astrbot.app/docs/source/images/qqofficial/image.png) + +## Allow Bot in Channel / Group / Private Chat + +Open `Sandbox Configuration` to set a sandbox channel / QQ group / QQ private chat (up to 20 members). + +Then configure QQ groups, private chat QQ accounts, and QQ channels as needed. + +![image](https://files.astrbot.app/docs/source/images/qqofficial/image-1.png) + +## Get `appid` and `secret` + +After adding the bot where you need it, open `Development -> Development Settings`, then copy `appid` and `secret`. + +## Add IP Whitelist + +Open `Development -> Development Settings`, find IP whitelist, and add your server IP. + +![image](https://files.astrbot.app/docs/source/images/qqofficial/image-3.png) + +## Configure in AstrBot + +1. Open AstrBot Dashboard. +2. Click `Bots` in the left sidebar. +3. Click `+ Create Bot`. +4. Select `qq_official_webhook`. + +Fill in: + +- ID (`id`): any unique identifier. +- Enable (`enable`): checked. +- `appid`: from QQ Official Bot platform. +- `secret`: from QQ Official Bot platform. + +Click `Save`. + +## Configure Callback URL + +In `Development -> Callback Configuration`, configure callback URL. + +Set request URL to `/astrbot-qo-webhook/callback`. + +Your domain should reverse-proxy traffic to AstrBot port `6196` using `Caddy`, `Nginx`, or `Apache`. + +Then add callback events and select all four event categories (private, group, channel, etc.). + +![image](https://files.astrbot.app/docs/source/images/webhook/image.png) + +After entering values, move focus out of the input box to trigger validation. If validation passes, the confirm button on the right becomes clickable. + +Then restart AstrBot. + +## Done + +AstrBot should now be connected. If messages do not respond immediately, wait 1-2 minutes, restart AstrBot, and test again. + +## Appendix: Reverse Proxy Setup + +If you are new to reverse proxy, Caddy is recommended: + +1. Install Caddy: +2. Configure reverse proxy: + +Caddy can automatically apply TLS certificates for Webhook access. diff --git a/docs/en/platform/qqofficial/websockets.md b/docs/en/platform/qqofficial/websockets.md new file mode 100644 index 000000000..0188dcef4 --- /dev/null +++ b/docs/en/platform/qqofficial/websockets.md @@ -0,0 +1,87 @@ +# Connect QQ via QQ Official Bot (Websockets) + +## Supported Basic Message Types + +> Version v4.15.0. + +| Message Type | Receive | Send | Notes | +| --- | --- | --- | --- | +| Text | Yes | Yes | | +| Image | Yes | Yes | | +| Voice | No | No | | +| Video | No | No | | +| File | No | No | | + +Proactive message push: Not supported. + +## Quick Deployment Steps + +> Updated: `2026/03/06`. This method only supports `private chat`. + +1. Open [QQ Open Platform](https://q.qq.com/qqbot/openclaw/). Register an account if you don't have one. +2. Click the `Create Bot` button on the right. +3. Obtain your `AppID` and `AppSecret`. +4. In AstrBot WebUI, click `Bots` in the left sidebar, then click `+ Create Bot`, select `QQ Official Bot (WebSocket)`, paste the `AppID` and `AppSecret` into the form, click `Enable`, then click `Save`. +5. Back on the QQ Open Platform page, click `Scan QR Code to Chat` next to your bot, then scan with your mobile QQ to start chatting. + +To use the bot in group chats, refer to the `Allow Bot in Channel / Group / Private Chat` section below. + +--- + +## Apply for a Bot + +> [!WARNING] +> 1. QQ Official Bot currently requires an IP whitelist. +> 2. It supports group chat, private chat, channel chat, and channel private chat. +> 3. Tencent is phasing out Websockets access, so this method is no longer recommended. Please use [Webhook](/en/platform/qqofficial/webhook) instead. + +Open [QQ Official Bot](https://q.qq.com) and sign in. + +Create a bot, fill in name/description/avatar, then submit for review. After security verification passes, creation is complete. + +Open the created bot to enter its management page: + +![image](https://files.astrbot.app/docs/source/images/qqofficial/image.png) + +## Allow Bot in Channel / Group / Private Chat + +Open `Sandbox Configuration` to set a sandbox channel / QQ group / QQ private chat (up to 20 members). + +Then configure QQ groups, private chat QQ accounts, and QQ channels as needed. + +![image](https://files.astrbot.app/docs/source/images/qqofficial/image-1.png) + +## Get `appid` and `secret` + +After adding the bot where you need it, open `Development -> Development Settings`, then copy `appid` and `secret`. + +## Add IP Whitelist + +Open `Development -> Development Settings`, find IP whitelist, and add your server IP. + +![image](https://files.astrbot.app/docs/source/images/qqofficial/image-3.png) + +> [!TIP] +> If you do not know your server IP, run `curl ifconfig.me` or check [ip138.com](https://ip138.com/). +> +> In NAT environments without a public IP, the observed IP may change depending on your carrier. Use proxy/tunnel if needed. + +## Configure in AstrBot + +1. Open AstrBot Dashboard. +2. Click `Bots` in the left sidebar. +3. Click `+ Create Bot`. +4. Select `qq_official`. + +Fill in: + +- ID (`id`): any unique identifier. +- Enable (`enable`): checked. +- `appid`: from QQ Official Bot platform. +- `secret`: from QQ Official Bot platform. + +Click `Save`. + +## Done + +AstrBot should now be connected. Send `/help` to the bot in QQ private chat to verify. diff --git a/docs/en/platform/satori/llonebot.md b/docs/en/platform/satori/llonebot.md new file mode 100644 index 000000000..3fc8df55f --- /dev/null +++ b/docs/en/platform/satori/llonebot.md @@ -0,0 +1,73 @@ +# Connect LLTwoBot (Satori) + +> [!TIP] +> LLTwoBot is a multi-protocol implementation based on QQNT (OneBot v11 + Satori), allowing AstrBot to communicate with QQ via Satori. + +> [!TIP] +> - Please control message frequency responsibly. +> - This project must not be used for illegal purposes. + +## Preparation + +First complete basic setup using official LLTwoBot documentation: + +[LLTwoBot Docs](https://llonebot.com/guide/getting-started) + +Make sure you have: + +1. Installed LLTwoBot. +2. Logged into a QQ account successfully. + +## Configure Satori in LLTwoBot + +After QQ login succeeds, open LLTwoBot WebUI: + +> Default WebUI URL: + +In the WebUI sidebar, open the `Satori` tab and configure: + +1. Enable Satori protocol. +2. Port defaults to `5600`. +3. Set Satori token if needed. +4. Click Save. + +![image](https://files.astrbot.app/docs/source/images/satori/2025-10-10_15-52-32.png) + +## Configure Satori Adapter in AstrBot + +1. Open AstrBot Dashboard. +2. Click `Bots`. +3. Click `+ Create Bot`. +4. Select `satori`. + +Fill in: + +- Bot ID (`id`): `LLTwoBot` +- Enable (`enable`): checked +- Satori API endpoint (`satori_api_base_url`): `http://localhost:5600/v1` +- Satori WebSocket endpoint (`satori_endpoint`): `ws://localhost:5600/v1/events` +- Satori token (`satori_token`): from LLTwoBot config if set + +> [!NOTE] +> - LLTwoBot Satori service defaults to port `5600`. +> - The complete API base path is `http://localhost:5600/v1`. +> - If your Satori service runs on another port/path, adjust these values. + +![image](https://files.astrbot.app/docs/source/images/satori/2025-10-10_16-10-54.png) + +Click `Save`. + +## Done + +AstrBot should now be connected to LLTwoBot via Satori. + +Send `/help` in QQ to verify. + +## Troubleshooting + +If connection fails, check: + +1. LLTwoBot is running. +2. Satori service is enabled. +3. Port/path are configured correctly. +4. Token matches (if configured). diff --git a/docs/en/platform/satori/server-satori.md b/docs/en/platform/satori/server-satori.md new file mode 100644 index 000000000..bcf5beeaf --- /dev/null +++ b/docs/en/platform/satori/server-satori.md @@ -0,0 +1,65 @@ +# Connect server-satori (Koishi) + +> [!TIP] +> `server-satori` is a Koishi plugin that exposes Koishi as a Satori server, so AstrBot can connect to Koishi through Satori. + +## Preparation + +Make sure you already have a running Koishi instance. + +If not, follow official docs first: + +- Koishi starter docs: +- Koishi community: + +## Enable `server-satori` in Koishi + +1. Open Koishi admin panel. +2. Go to `Plugin Config`. +3. Install and enable `server-satori` (defaults usually work). + +After enabling, `server-satori` serves Satori API under `/satori`. + +![image](https://files.astrbot.app/docs/source/images/satori/2025-09-07_17-14-55.png) + +## Configure Satori Adapter in AstrBot + +1. Open AstrBot Dashboard. +2. Click `Bots`. +3. Click `+ Create Bot`. +4. Select `satori`. + +Fill in: + +- Bot ID (`id`): `server-satori` +- Enable (`enable`): checked +- Satori API endpoint (`satori_api_base_url`): `http://localhost:5140/satori/v1` +- Satori WebSocket endpoint (`satori_endpoint`): `ws://localhost:5140/satori/v1/events` +- Satori token (`satori_token`): usually empty unless configured in Koishi + +> [!NOTE] +> - Koishi default port is `5140`. +> - `server-satori` default path is `/satori`. +> - So the full API base is `http://localhost:5140/satori/v1`. +> - If your Koishi runs on different host/port/path, change accordingly. + +![image](https://files.astrbot.app/docs/source/images/satori/2025-10-10_16-16-25.png) + +Click `Save`. + +## Done + +AstrBot should now be connected to Koishi via `server-satori`. + +Test by sending an AstrBot command (for example `/help`) in Koishi sandbox. + +![image](https://files.astrbot.app/docs/source/images/satori/2025-09-07_17-19-04.png) + +## Troubleshooting + +If connection fails, check: + +1. Koishi is running. +2. `server-satori` is installed and enabled. +3. Port/path are configured correctly. +4. Firewall is not blocking related ports. diff --git a/docs/en/platform/slack.md b/docs/en/platform/slack.md new file mode 100644 index 000000000..28b6b0f88 --- /dev/null +++ b/docs/en/platform/slack.md @@ -0,0 +1,94 @@ +# Connecting to Slack + +## Create AstrBot Slack Platform Adapter + +Navigate to the `Bots` page, click `+ Create Bot`, find Slack and click to enter the Slack configuration page. + +![image](https://files.astrbot.app/docs/source/images/slack/image-1.png) + +In the configuration dialog that appears, click `Enable`. + +## Create an App in Slack + +Slack supports two connection methods: `Webhook` and `Socket`. If you don't have a public server and your message volume is relatively small, we recommend using the `socket` method. If you have a public server (or have technical knowledge about setting up tunnels, such as Cloudflare Tunnel), you can choose the `webhook` method. The `socket` method is relatively simpler to deploy. + +1. Create a [Slack](https://slack.com/signin) account and a Workspace. +2. Go to [Apps Management](https://api.slack.com/apps), click "Create New App" -> "From Scratch", enter the `App Name` and the workspace to add it to, then click "Create App". +3. (Webhook only) Obtain the `Signing Secret`. In the Basic Information page on the left sidebar, find `Signing Secret` under App Credentials, click Show and copy it to the signing_secret field in the platform adapter configuration. + +![image](https://files.astrbot.app/docs/source/images/slack/image.png) + +4. In the Basic Information page on the left sidebar, find App-Level Tokens and click "Generate Token and Scopes". Enter any Token Name, click Add Scope, select `connections:write`, then click "Generate". Click Copy and paste the result into the app_token field on the AstrBot configuration page. + +![image](https://files.astrbot.app/docs/source/images/slack/image-2.png) + +5. In the OAuth & Permissions page on the left sidebar, add the following permissions under Bot Token Scopes: + - channels:history + - channels:read + - channels:write.invites + - chat:write + - chat:write.customize + - chat:write.public + - files:read + - files:write + - groups:history + - groups:read + - groups:write + - im:history + - im:read + - im:write + - reactions:read + - reactions:write + - users:read + +6. In the OAuth & Permissions page on the left sidebar, click `Install to xxx` under OAuth Token (where xxx is your workspace name). Then copy the generated Bot User OAuth Token to the bot_token field in the platform adapter configuration. + +7. (Socket only) In the Socket Mode page on the left sidebar, enable Socket Mode. + +![image](https://files.astrbot.app/docs/source/images/slack/image-3.png) + +## Start the Platform Adapter + +The configuration is now complete. If you're using Socket mode, simply click the Save button in the bottom right corner of the configuration. + +If you're using Webhook mode, please keep `Unified Webhook Mode (unified_webhook_mode)` enabled. + +> [!TIP] +> Before v4.8.0, there is no `Unified Webhook Mode`. You need to fill in the following configuration items: +> Slack Webhook Host, Slack Webhook Port, and Slack Webhook Path + + +## Enable Event Subscriptions + +After successfully creating the platform adapter, return to the Slack settings. In the Event Subscriptions page on the left sidebar, click Enable Events to enable event reception. + +If you're using Webhook mode: + +- If `Unified Webhook Mode` is enabled, after clicking save, AstrBot will automatically generate a unique Webhook callback URL for you. You can find it in the logs or on the bot card in the WebUI's Bots page. Enter this URL in the `Request URL` field. + +![unified_webhook](https://files.astrbot.app/docs/source/images/use/unified-webhook.png) + +- If `Unified Webhook Mode` is not enabled, enter `https://your-domain/astrbot-slack-webhook/callback` in the `Request URL` field. + +> [!TIP] +> In Webhook mode, you need to first set up your domain with your DNS provider, then use reverse proxy software to forward requests to port `6185` on the AstrBot server (if Unified Webhook Mode is enabled) or the port specified in your configuration (if Unified Webhook Mode is not enabled). Alternatively, you can use Cloudflare Tunnel. For detailed tutorials, please refer to online resources; this tutorial will not cover these in detail. + +After enabling, under Subscribe to bot events below, click Add Bot User Event and add the following events: + +1. channel_created +2. channel_deleted +3. channel_left +4. member_joined_channel +5. member_left_channel +6. message.channels +7. message.groups +8. message.im +9. reaction_added +10. reaction_removed +11. team_join + +## Test the Connection + +Enter the Slack workspace you just added, navigate to the channel where you want to use the bot, then @ mention the app you just created. Click the Add button in the message subsequently sent by Slackbot to add it to the workspace. Then, @ mention the app and type `/help`. If it responds successfully, the test is successful. + +If you have any questions, please [submit an Issue](https://github.com/AstrBotDevs/AstrBot/issues). diff --git a/docs/en/platform/start.md b/docs/en/platform/start.md new file mode 100644 index 000000000..3dbfce917 --- /dev/null +++ b/docs/en/platform/start.md @@ -0,0 +1,6 @@ +# Messaging Platforms + +AstrBot supports integration with many mainstream instant messaging platforms, so you can use AstrBot on the IM platform your team already uses. + +In WebUI, click **Bots** in the left sidebar to open the messaging platform integration page. +Then click **Create Bot** in the top-right corner, choose the platform you want to connect, and follow the platform-specific guide in the left sidebar of this documentation. diff --git a/docs/en/platform/telegram.md b/docs/en/platform/telegram.md new file mode 100644 index 000000000..68ae143fc --- /dev/null +++ b/docs/en/platform/telegram.md @@ -0,0 +1,55 @@ + +# Connecting to Telegram + +## Supported Message Types + +> Version v4.15.0. + +| Message Type | Receive Support | Send Support | Notes | +| --- | --- | --- | --- | +| Text | Yes | Yes | | +| Image | Yes | Yes | | +| Voice | Yes | Yes | | +| Video | Yes | Yes | | +| File | Yes | Yes | | + +Proactive message push: Supported. + +## 1. Create a Telegram Bot + +First, open Telegram and search for `BotFather`. Click `Start`, then send `/newbot` and follow the prompts to enter your bot's name and username. + +After successful creation, `BotFather` will provide you with a `token`. Please keep it secure. + +If you need to use the bot in group chats, you must disable the bot's [Privacy mode](https://core.telegram.org/bots/features#privacy-mode). Send the `/setprivacy` command to `BotFather`, select your bot, and then choose `Disable`. + +## 2. Configure AstrBot + +1. Enter the AstrBot admin panel +2. Click `Bots` in the left sidebar +3. In the interface on the right, click `+ Create Bot` +4. Select `telegram` + +Fill in the configuration fields that appear: + +- ID: Enter any value to distinguish between different messaging platform instances. +- Enable: Check this option. +- Bot Token: Your Telegram bot's `token`. + +Please ensure your network environment can access Telegram. You may need to configure a proxy using `Configuration -> Other Settings -> HTTP Proxy`. + +## Streaming Output + +The Telegram platform supports streaming output. Enable the "Streaming Output" switch in "AI Configuration" -> "Other Settings". + +### Private Chat Streaming + +In private chats, AstrBot uses the `sendMessageDraft` API (added in Telegram Bot API v9.3) for streaming output. This displays a "typing" draft preview animation in the chat interface, creating a more natural "typewriter" effect. It avoids issues with the traditional approach such as message flickering, push notification interference, and API edit frequency limits. + +### Group Chat Streaming + +In group chats, since the `sendMessageDraft` API only supports private chats, AstrBot automatically falls back to the traditional `send_message` + `edit_message_text` approach. + +:::warning +`sendMessageDraft` requires `python-telegram-bot>=22.6`. +::: diff --git a/docs/en/platform/vocechat.md b/docs/en/platform/vocechat.md new file mode 100644 index 000000000..bb03266df --- /dev/null +++ b/docs/en/platform/vocechat.md @@ -0,0 +1,44 @@ +# Connect to VoceChat + +> [!TIP] +> AstrBot does not include this adapter by default. Install [astrbot_plugin_vocechat](https://github.com/HikariFroya/astrbot_plugin_vocechat), developed by [HikariFroya](https://github.com/HikariFroya). + +> [!WARNING] +> This adapter is community-maintained and not officially maintained by AstrBot. + +## Deploy VoceChat + +VoceChat is an open-source instant messaging platform with simple multi-platform deployment. + +See deployment methods on the [VoceChat official website](https://voce.chat/en-US). + +## Install `astrbot_plugin_vocechat` + +In AstrBot Dashboard Plugin Market, search for `astrbot_plugin_vocechat` and install it. + +![image](https://files.astrbot.app/docs/source/images/vocechat/image.png) + +After installation, go to `Bots` -> `+ Create Bot` -> `VoceChat`. +If VoceChat is missing, restart AstrBot or verify plugin installation. + +Enable the adapter in the configuration dialog. + +## Configuration + +- `vocechat_server_url` (required): full VoceChat server URL, e.g. `http://localhost:3009` or `https://your.vocechat.domain` (no trailing `/`). +- `api_key` (required): API key generated for the bot account in VoceChat. +- `webhook_path` (recommended default/custom): webhook path used by AstrBot to receive VoceChat messages, e.g. `/vocechat_webhook`. +- `webhook_listen_host` (usually `0.0.0.0`): listen host for AstrBot webhook server. +- `webhook_port` (required): listen port for AstrBot webhook server, e.g. `8080`. +- `get_user_nickname_from_api` (boolean, default `true`): fetch nickname via VoceChat API. +- `send_plain_as_markdown` (boolean, default `false`): send plain text in markdown format. +- `default_bot_self_uid` (required): UID of your VoceChat bot account. + +After configuration, click Save and test in VoceChat. + +## Issue Reporting + +If needed, report issues to: + +- Plugin repo: +- AstrBot repo: diff --git a/docs/en/platform/wecom.md b/docs/en/platform/wecom.md new file mode 100644 index 000000000..a102c563c --- /dev/null +++ b/docs/en/platform/wecom.md @@ -0,0 +1,137 @@ +# Connect AstrBot to WeCom + +AstrBot supports both WeCom Applications and WeCom Customer Service. + +## Supported Basic Message Types + +> Version v4.15.0. + +| Message Type | Receive | Send | Notes | +| --- | --- | --- | --- | +| Text | Yes | Yes | | +| Image | Yes | Yes | | +| Voice | Yes | Yes | | +| Video | No | Yes | | +| File | No | Yes | | + +Proactive message push: Supported for WeCom Application. Not fully tested for WeCom Customer Service. + +## Before You Start + +1. Open AstrBot Dashboard. +2. Click `Bots` in the left sidebar. +3. Click `+ Create Bot`. +4. Select `wecom`. + +A configuration dialog will appear. Keep it open and continue with the steps below. + +## Method 1: WeCom Customer Service + +> [!NOTE] +> 1. Requires AstrBot >= v3.5.7. +> 2. This method works directly inside WeChat. + +1. Open [WeCom Customer Service Console](https://kf.weixin.qq.com/) and sign in with WeCom QR login. +2. Create a customer service account in `Customer Service Account`, then copy its **name** (not account ID) to AstrBot field `wechat_kf_account_name`. +3. Go to [WeCom Enterprise Info](https://work.weixin.qq.com/wework_admin/frame#profile), copy `Corpid`, and fill AstrBot `corpid`. +4. Configure callback verification: + +- If this is your first customer service bot, open `Development Configuration`, click `Start` next to internal access. +- If you used it before, open `Callback Configuration` directly and click edit. + +![image](https://files.astrbot.app/docs/source/images/wecom/8287fd9fec5823847e6b590dc3f0f545.png) + +5. Click random generation buttons to get `Token` and `EncodingAESKey`, then fill AstrBot `token` and `encoding_aes_key`. +6. Keep `Unified Webhook Mode (unified_webhook_mode)` enabled, click `Save`, and wait for adapter reload. + +For callback URL: + +- If unified mode is enabled, AstrBot generates a unique webhook callback URL after save. Copy it from logs or bot card in WebUI. +- If unified mode is disabled, use `http://:6195/callback/command`. + +![unified_webhook](https://files.astrbot.app/docs/source/images/use/unified-webhook.png) + +> If unified mode is enabled, forward external requests to AstrBot port `6185`; otherwise forward to configured adapter port (default `6195`). + +Back in WeCom Customer Service callback settings, click `Complete`. If successful, status shows completed. + +7. In `Development Configuration`, get `Secret`, edit your WeCom adapter in AstrBot, set `secret`, then save again. + +> [!TIP] +> Based on [#571](https://github.com/Soulter/AstrBot/issues/571), for newly registered enterprises, `corp_id` may take about 30 minutes to become valid. + +Then open AstrBot `Console`, you should see logs asking you to open a WeChat scan link. + +```txt +Please open the following link and scan with WeChat ... +``` + +![image](https://files.astrbot.app/docs/source/images/wecom/image-13.png) + +Open the link, scan with WeChat, then send `help` in the customer service chat to test connectivity. + +## Method 2: WeCom Application + +Open: + +1. Click `My Company`, copy enterprise ID (`Corpid`), and fill AstrBot `corpid`. + +> [!TIP] +> For newly registered enterprises, `corp_id` may take time to become valid. See [#571](https://github.com/Soulter/AstrBot/issues/571). + +![image](https://files.astrbot.app/docs/source/images/wecom/image-5.png) + +2. Create a custom app (`Custom App`) and fill name/avatar/visibility scope. +3. Open the app, copy `Secret`, and fill AstrBot `secret`. + +![image](https://files.astrbot.app/docs/source/images/wecom/image-4.png) + +4. In app settings, find `Receive Messages`, click `Set API Receive`. + +![image](https://files.astrbot.app/docs/source/images/wecom/image-6.png) + +![image](https://files.astrbot.app/docs/source/images/wecom/image-9.png) + +5. Generate `Token` and `EncodingAESKey`, fill AstrBot `token` and `encoding_aes_key`. +6. Keep `Unified Webhook Mode (unified_webhook_mode)` enabled (recommended), then click Save in AstrBot and wait for restart. + +For callback URL: + +- If unified mode is enabled, use the generated unique callback URL from logs or bot card. +- If unified mode is disabled, use `http://:6195/callback/command`. + +![unified_webhook](https://files.astrbot.app/docs/source/images/use/unified-webhook.png) + +> If unified mode is enabled, forward to port `6185`; otherwise forward to configured adapter port (default `6195`). + +7. Configure trusted enterprise IP in WeCom. + +![image](https://files.astrbot.app/docs/source/images/wecom/image-10.png) + +Add your public IP and confirm. + +![image](https://files.astrbot.app/docs/source/images/wecom/image-12.png) + +After AstrBot restart, return to API receive page and click save. If you see callback verification errors, re-check all required fields. + +If save succeeds, AstrBot can receive messages from WeCom. + +## Test + +In WeCom Workbench, open the app you just created and send `/help`. + +If AstrBot replies, integration is successful. + +## Reverse Proxy (Custom API Base) + +AstrBot supports custom WeCom endpoint (`api_base_url`) for environments without stable public IP. + +Set your custom endpoint in `api_base_url`. + +## Voice Input + +Install `ffmpeg` for voice input support. + +- Linux: `apt install ffmpeg` +- Windows: download from [FFmpeg website](https://ffmpeg.org/download.html) +- macOS: `brew install ffmpeg` diff --git a/docs/en/platform/wecom_ai_bot.md b/docs/en/platform/wecom_ai_bot.md new file mode 100644 index 000000000..51635b4c0 --- /dev/null +++ b/docs/en/platform/wecom_ai_bot.md @@ -0,0 +1,87 @@ +# Connect to WeCom AI Bot Platform + +WeCom AI Bot is an official AI-friendly bot platform by WeCom. It can be used directly in one-on-one chats and internal group chats, and supports streaming responses. + +AstrBot supports this platform since v4.3.5. + +## Supported Basic Message Types + +| Message Type | Receive | Send | Notes | +| --- | --- | --- | --- | +| Text | Yes | Yes | | +| Image | Yes | Yes | Requires message push Webhook URL to be configured. | +| Voice | No | Yes | Requires message push Webhook URL to be configured. | +| Video | No | Yes | Requires message push Webhook URL to be configured. | +| File | No | Yes | Requires message push Webhook URL to be configured. | + +Proactive message push: Supported, but requires a message push Webhook URL. + +## Configure WeCom AI Bot + +1. Sign in to [WeCom Admin Console](https://work.weixin.qq.com/wework_admin). +2. In the left sidebar, open `Management Tools` -> `AI Bot`, then click Create Bot. + +![Management Tools - AI Bot](https://files.astrbot.app/docs/source/images/wecom_ai_bot/image-1.png) + +3. On the create page, choose `Create via API Mode`. Fill bot name/avatar and other basic info. +Generate `Token` and `EncodingAESKey` using random generation, but do not click Create yet. + +![Create AI Bot Account](https://files.astrbot.app/docs/source/images/wecom_ai_bot/image.png) + +## Configure AstrBot + +1. Open AstrBot Dashboard, click `Messaging Platforms`, then click `+ Add Adapter`, choose `WeCom AI Bot`. + +![Add Adapter](https://files.astrbot.app/docs/source/images/wecom_ai_bot/image-2.png) + +2. Fill AstrBot fields with values from the WeCom AI Bot create page: + +- Bot name +- `token` +- `encoding_aes_key` +- `id` (any unique value) +- `port` (default `6198`, change if needed) + +Keep `Unified Webhook Mode (unified_webhook_mode)` enabled and click `Save`. + +3. Return to WeCom AI Bot create page and set `URL`: + +- If unified mode is enabled, AstrBot generates a unique callback URL after save. Copy it from logs or bot card in WebUI. +- If unified mode is disabled, use `http://IP:port/webhook/wecom-ai-bot`. + +![unified_webhook](https://files.astrbot.app/docs/source/images/use/unified-webhook.png) + +> It is recommended to use a domain + reverse proxy + HTTPS. You can also use [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-guide/). + +4. Click `Create`. If successful, you will enter bot details page. +If you see `Service did not respond correctly`, re-check AstrBot config and firewall rules. + +![Bot Details](https://files.astrbot.app/docs/source/images/wecom_ai_bot/image-3.png) + +5. Optional (recommended): Configure WeCom message push Webhook URL. +By default, WeCom AI Bot replies only when users send messages first. Configuring message push enables proactive notifications. + +6. Optional (recommended): Enable `Send messages via Webhook only` for richer multi-message output and to bypass single-bubble reply limits. +This option requires the message push Webhook URL from step 5. + +## Use the Bot + +### Add Bot to Group Chat + +In WeCom client internal group chat, click Add Member -> AI Bot, select the bot you created, and add it. + +![Add Member](https://files.astrbot.app/docs/source/images/wecom_ai_bot/image-4.png) + +![Added Successfully](https://files.astrbot.app/docs/source/images/wecom_ai_bot/image-5.png) + +### Chat with the Bot + +Send a message in private chat or group chat to talk to the bot. + +If you need typing-like streaming effect, enable `Streaming Reply` in AstrBot. + +![Streaming Reply](https://files.astrbot.app/docs/source/images/wecom_ai_bot/image-6.png) + +## Help & Support + +If you have issues during setup/use or need enterprise support, contact: [community@astrbot.app](mailto:community@astrbot.app). diff --git a/docs/en/platform/weixin-official-account.md b/docs/en/platform/weixin-official-account.md new file mode 100644 index 000000000..2ca2783c1 --- /dev/null +++ b/docs/en/platform/weixin-official-account.md @@ -0,0 +1,78 @@ +# Connect AstrBot to WeChat Official Account Platform + +AstrBot supports WeChat Official Account integration (version >= v3.5.8). After setup, you can chat with AstrBot directly in the WeChat Official Account chat interface. + +## Before You Start + +1. Open AstrBot Dashboard. +2. Click `Bots` in the left sidebar. +3. Click `+ Create Bot`. +4. Select `weixin_official_account`. + +A configuration dialog will appear. Keep it open and continue. + +## Create / Sign In to WeChat Official Account Platform + +Open [WeChat Official Account Platform](https://mp.weixin.qq.com/). + +- If you already have an account, sign in. +- If not, register a new account and choose `Official Account`. + +> [!NOTE] +> A newly registered account may require 1-2 days for review before it can be used. + +## Configure Callback Service + +Open `Settings & Development` -> `Development Interface Management`. + +![Development Interface Management](https://files.astrbot.app/docs/source/images/weixin-official-account/image.png) + +Copy AppID and AppSecret from WeChat platform to AstrBot fields `appid` and `secret`. + +Open IP whitelist and add your public IP(s), one per line if multiple. + +In server configuration, click modify. + +- `Token`: create any string with length 3-32, and fill the same value in AstrBot `token`. +- `EncodingAESKey`: click random generate and fill AstrBot `encoding_aes_key`. + +Keep `Unified Webhook Mode (unified_webhook_mode)` enabled (recommended), then save AstrBot config and wait for restart. + +For `URL`: + +- If unified mode is enabled, use the unique callback URL generated by AstrBot (from logs or bot card). +- If unified mode is disabled, use `http:///callback/command`. + +![unified_webhook](https://files.astrbot.app/docs/source/images/use/unified-webhook.png) + +> [!NOTE] +> WeChat Official Account callback supports only ports 80 or 443. You usually need a domain and reverse proxy: +> - Unified mode enabled: forward to AstrBot port `6185` +> - Unified mode disabled: forward to adapter port `6194` + +Set message encryption mode to `Security Mode`. + +Wait a moment and click `Submit`. If configuration is correct, you will see success. + +## Test + +In WeChat Official Account platform, open account profile and find your QR code. + +Scan it with WeChat, send `help`, and check whether AstrBot replies. + +If it replies, integration is successful. + +> [!NOTE] +> If console shows `ip xxxxx not in whitelist`, your public IP is not in WeChat whitelist yet. Add it and wait a few minutes for WeChat to refresh. + +## Reverse Proxy (Custom API Base) + +AstrBot supports custom endpoint via `api_base_url` for environments without stable public IP. + +## Voice Input + +Install `ffmpeg` for voice input support. + +- Linux: `apt install ffmpeg` +- Windows: download from [FFmpeg website](https://ffmpeg.org/download.html) +- macOS: `brew install ffmpeg` diff --git a/docs/en/providers/302ai.md b/docs/en/providers/302ai.md new file mode 100644 index 000000000..50a3abe77 --- /dev/null +++ b/docs/en/providers/302ai.md @@ -0,0 +1,21 @@ +# 接入 302.AI + +302.AI 是企业级 AI 应用平台,支持快捷接入全球各类 AI 模型。 + +## 使用 + +点击[此链接](https://share.302.ai/rr1M3l) 注册账户。 + +注册完毕之后,点击[此链接](https://302.ai/apis/)选择需要接入的模型。 + +根据需求,进入[此链接](https://dash.302.ai/charge) 充值对应的金额。 + +## 接入 + +打开 AstrBot 控制台 -> 服务提供商页面,点击新增提供商,找到并点击 `302.AI`(需要版本 >= 3.5.18) + +修改 ID,并将 API Key 和模型名称填入对话框表单,点击保存,即可完成创建。 + +## 使用 + +对机器人输入 `/provider` 指令,将提供商切换到刚刚添加的 302.AI 提供商,即可使用。 diff --git a/docs/en/providers/agent-runners.md b/docs/en/providers/agent-runners.md new file mode 100644 index 000000000..3f982bc18 --- /dev/null +++ b/docs/en/providers/agent-runners.md @@ -0,0 +1,19 @@ +# Agent Runners + +## What Is an Agent Runner? + +An Agent Runner is the component in AstrBot that executes Agent capabilities and handles AI-related workflows. + +AstrBot includes a powerful built-in Agent Runner. You can also integrate third-party Agent Runner services like Dify, Coze, Alibaba Bailian, and DeerFlow, or build your own. + +If you already have a model provider that handles single requests, you still need an execution layer for multi-turn conversations, tool calling, and orchestration. That is exactly what an Agent Runner does. + +For more details, see [Usage · Agent Runner](/en/use/agent-runner). + +## Quick Links + +- [Built-in Agent Runner](/en/providers/agent-runners/astrbot-agent-runner) +- [Dify](/en/providers/agent-runners/dify) +- [Coze](/en/providers/agent-runners/coze) +- [Alibaba Bailian](/en/providers/agent-runners/dashscope) +- [DeerFlow](/en/providers/agent-runners/deerflow) diff --git a/docs/en/providers/agent-runners/astrbot-agent-runner.md b/docs/en/providers/agent-runners/astrbot-agent-runner.md new file mode 100644 index 000000000..74873d710 --- /dev/null +++ b/docs/en/providers/agent-runners/astrbot-agent-runner.md @@ -0,0 +1,8 @@ +# Built-in Agent Runner + +By default, AstrBot uses the built-in Agent Runner as the default executor. You don't need to configure anything to use AstrBot's powerful built-in Agent Runner. + +![image](https://files.astrbot.app/docs/source/images/astrbot-agent-runner/image.png) + +With the built-in Agent Runner, you can use AstrBot's [MCP Server](/use/mcp), [Knowledge Base](/use/knowledge-base), [Web Search](/use/websearch), and persona features. + diff --git a/docs/en/providers/agent-runners/coze.md b/docs/en/providers/agent-runners/coze.md new file mode 100644 index 000000000..3ef4e8612 --- /dev/null +++ b/docs/en/providers/agent-runners/coze.md @@ -0,0 +1,65 @@ +# Connect to Coze + +AstrBot v4.2.1 and later versions support connecting to [Coze](https://www.coze.cn/) Agent service. + +## Preparation: Get API Key + +First, register and log in to your [Coze](https://www.coze.cn/) account, then go to the [API Key Management Page](https://www.coze.cn/open/oauth/pats) to create a new API Key. + +You can follow the steps in the image to reach the API Key management page, or click the link above to go directly. + +![Create API Key](https://files.astrbot.app/docs/source/images/coze/image_1.png) + +Then, click "Create", fill in your API Key name on the following page, select an expiration time (permanent tokens are not recommended), click "Select All" under "Permissions", select a workspace, and then click "Confirm". + +![Create Token](https://files.astrbot.app/docs/source/images/coze/image_2.png) + +After that, we will get a new API Key. Please copy and save it, as it will be needed later. + +![New API Key](https://files.astrbot.app/docs/source/images/coze/image_3.png) + +## Preparation: Configure the Agent + +Go to the [Project Development](https://www.coze.cn/space/develop) page, click "+Project" in the upper right corner to create a new project, and select to create an agent. + +![Create Project](https://files.astrbot.app/docs/source/images/coze/image_4.png) + +![Create Project](https://files.astrbot.app/docs/source/images/coze/image_5.png) + +**Note**: After creating the agent, you must first click the **Publish** button in the upper right corner to publish the agent. In the "Select Publishing Platform" section, check all API options, then click "Publish". + +> If you don't publish or don't check the API options during publishing, you won't be able to call the agent via API. + +![Publish Agent](https://files.astrbot.app/docs/source/images/coze/image_6.png) + +After clicking publish, the agent creation is complete. You can see the publish history on the left side of the publish button on the agent development page to confirm the agent has been published successfully. + +Next, note the URL on the agent development page: + +![Agent Development](https://files.astrbot.app/docs/source/images/coze/image_7.png) + +For example, if the URL in the example is: "https://www.coze.cn/space/7553214941005004863/bot/7553248674860826660" + +Then the `bot_id` is the string of numbers after `bot/` in the URL: `7553248674860826660` + +We need to record the `bot_id` for later use. + +## Configure Coze in AstrBot + +After completing all the preparation work, we can now configure Coze in AstrBot. + +Go to AstrBot Admin Panel -> Service Provider -> Add Service Provider -> Coze to enter the configuration page. + +![Coze Provider](https://files.astrbot.app/docs/source/images/coze/image_8.png) + +Fill in the API Key and bot_id you just created, then click Save. + +> Other configuration notes: +> +> - API Base URL: Generally no modification is needed. If you are using the international version of Coze, change this to: "https://api.coze.com" +> - Let Coze manage conversation history: As described. + +## Select Agent Runner + +Go to the Configuration page in the left sidebar, click "Agent Execution Method", select "Coze", then select the ID of the Coze Agent Runner you just created in the new configuration options that appear below, and click "Save" in the bottom right corner to complete the configuration. + diff --git a/docs/en/providers/agent-runners/dashscope.md b/docs/en/providers/agent-runners/dashscope.md new file mode 100644 index 000000000..6323824d6 --- /dev/null +++ b/docs/en/providers/agent-runners/dashscope.md @@ -0,0 +1,52 @@ +# Connect to Alibaba Cloud Bailian Application + +Since v3.4.30, AstrBot supports connecting to Alibaba Cloud Bailian Application. + +## Configure Alibaba Cloud Bailian Application in AstrBot + +On the [Alibaba Cloud Bailian Application](https://bailian.console.aliyun.com/app-center#/app-center) website, click to add a new application. Create an agent application, workflow application, or agent orchestration application according to your needs, and build the agent or workflow as required. + +Record the Application ID: + +![image](https://files.astrbot.app/docs/source/images/dashscope/image-1.png) + +Click to enter the application, click Publishing Channel -> API Call -> API KEY, create and copy the API KEY: + +![alt text](https://files.astrbot.app/docs/source/images/dashscope/image-2.png) + +In the WebUI, click "Model Provider" -> "Add Provider", select "Agent Runner", select "Alibaba Cloud Bailian Application", and enter the Alibaba Cloud Bailian Application configuration page. + +According to Alibaba Cloud Bailian Application, there are four application types: + +- Agent Application (agent) +- Task Workflow Application (task-workflow) +- Dialog Workflow Application (dialog-workflow) +- Agent Orchestration Application (agent-arrange) + +> [!TIP] +> Multi-turn conversations are only supported for agent applications and dialog workflow applications. AstrBot will automatically attach conversation history for these two types of applications to support multi-turn conversations. + +Please ensure that the `Application Type` configured in AstrBot matches the application type created in Alibaba Cloud Bailian Application. + +Then fill in the Application ID in `dashscope_app_id` and the API KEY in `dashscope_api_key`. + +After filling in these three items, click Save. + +## Select Agent Runner + +Go to the Configuration page in the left sidebar, click "Agent Execution Method", select "Alibaba Cloud Bailian Application", then select the ID of the Alibaba Cloud Bailian Application Agent Runner you just created in the new configuration options that appear below, and click "Save" in the bottom right corner to complete the configuration. + +## Appendix: Dynamically Set Workflow Input Variables During Chat (Optional) + +For the two workflow applications, you can dynamically set input variables in the chat area. + +Use the `/set` command to dynamically set input variables, as shown in the figure below: + +![alt text](https://files.astrbot.app/docs/source/images/dify/image-5.png) + +After setting variables, AstrBot will attach the variables you set in the next request to Alibaba Cloud Bailian Application, flexibly adapting to your Workflow. + +Of course, you can use the `/unset` command to cancel the variables you set. For example, `/unset name` + +Variables are permanently valid in the current session. + diff --git a/docs/en/providers/agent-runners/deerflow.md b/docs/en/providers/agent-runners/deerflow.md new file mode 100644 index 000000000..04b6fc82f --- /dev/null +++ b/docs/en/providers/agent-runners/deerflow.md @@ -0,0 +1,53 @@ +# Connect to DeerFlow + +Starting from v4.19.2, AstrBot supports connecting to the [DeerFlow](https://github.com/bytedance/deer-flow) Agent Runner. + +## Preparation: Deploy DeerFlow + +If you have not deployed DeerFlow yet, please complete installation and startup by following the official DeerFlow documentation: + +- [DeerFlow GitHub Repository](https://github.com/bytedance/deer-flow) +- [DeerFlow Official Website](https://deerflow.tech/) +- [DeerFlow Configuration Guide](https://github.com/bytedance/deer-flow/blob/main/backend/docs/CONFIGURATION.md) + +Make sure DeerFlow is running properly and that AstrBot can reach the DeerFlow gateway. By default, the DeerFlow gateway address is `http://127.0.0.1:2026`. + +> [!TIP] +> - `API Base URL` must start with `http://` or `https://`. +> - If AstrBot and DeerFlow run in different containers or on different hosts, replace `127.0.0.1` with the actual reachable LAN address, hostname, or domain of your DeerFlow service. + +## Configure DeerFlow in AstrBot + +In the WebUI, click "Model Provider" -> "Add Provider", select "Agent Runner", select "DeerFlow", and enter the DeerFlow configuration page. + +Fill in the following fields: + +- `API Base URL`: DeerFlow API gateway URL. Default: `http://127.0.0.1:2026` +- `DeerFlow API Key`: Optional. Fill this if your DeerFlow gateway is protected by Bearer auth +- `Authorization Header`: Optional. Custom Authorization header value. This takes precedence over `DeerFlow API Key` +- `Assistant ID`: Maps to LangGraph `assistant_id`. Default: `lead_agent` +- `Model name override`: Optional. Overrides the default model configured in DeerFlow +- `Enable thinking mode`: Whether to enable DeerFlow thinking mode +- `Enable plan mode`: Maps to DeerFlow `is_plan_mode` +- `Enable subagent`: Maps to DeerFlow `subagent_enabled` +- `Max concurrent subagents`: Maps to `max_concurrent_subagents`. Effective only when subagents are enabled. Default: `3` +- `Recursion limit`: Maps to LangGraph `recursion_limit`. Default: `1000` + +After filling in the configuration, click Save. + +> [!TIP] +> - If DeerFlow already has a default model configured on its side, you can leave `Model name override` empty. +> - Only enable `plan mode` or `subagent` related options when the corresponding DeerFlow capabilities are already configured on the DeerFlow side. + +## Select Agent Runner + +Go to the Configuration page in the left sidebar, click "Agent Execution Method", select "DeerFlow", then select the ID of the DeerFlow Agent Runner you just created in the new configuration option below, and click "Save" in the bottom right corner to complete the configuration. + +## Common Checks + +If requests are not being executed through DeerFlow correctly, check the following first: + +- whether the DeerFlow service is running properly +- whether `API Base URL` is reachable from the AstrBot environment +- whether the authentication settings are correct +- whether `Assistant ID` matches an actual available assistant in DeerFlow diff --git a/docs/en/providers/agent-runners/dify.md b/docs/en/providers/agent-runners/dify.md new file mode 100644 index 000000000..8b17143a6 --- /dev/null +++ b/docs/en/providers/agent-runners/dify.md @@ -0,0 +1,82 @@ +# Connect to Dify + +## Install Dify + +If you haven't installed Dify yet, please refer to the [Dify Installation Documentation](https://docs.dify.ai/getting-started/install-self-hosted) to install it. + +## Configure Dify in AstrBot + +In the WebUI, click "Model Provider" -> "Add Provider", select "Agent Runner", select "Dify", and enter the Dify configuration page. + +![image](https://files.astrbot.app/docs/source/images/dify/image.png) + +In Dify, one `API Key` uniquely corresponds to one Dify application. Therefore, you can create multiple Providers to adapt to multiple Dify applications. + +According to the current Dify project, there are three types: + +- chat +- agent +- workflow + +>[!TIP] +>Please ensure that the APP type you set in AstrBot matches the application type created in Dify. +>![image](https://files.astrbot.app/docs/source/images/dify/image-3.png) + + +### Chat and Agent Applications + +Create your Dify Chat and Agent application keys as shown in the figure below: + +![image](https://files.astrbot.app/docs/source/images/dify/chat-agent-api-key.png) + +![image](https://files.astrbot.app/docs/source/images/dify/chat-agent-api-key-2.png) + +Copy the key and paste it into the `API Key` field in the configuration, then click "Save". + +### Workflow Applications + +#### Configure Input and Output Variable Names + +Workflow applications receive input variables, execute the workflow, and output the results. + +![image](https://files.astrbot.app/docs/source/images/dify/workflow-io-key.png) + +For Workflow applications, AstrBot will attach two variables with each request: + +- `astrbot_text_query`: Input variable name. This is the text content entered by the user. +- `astrbot_session_id`: Session ID + +You can customize the input variable name in the configuration, which is the "Prompt Input Variable Name" shown in the figure above. + +You need to modify the input variable name of your Workflow to adapt to AstrBot's input. + +Finally, the Workflow will output a result. You can customize the variable name of this result, which is the "Dify Workflow Output Variable Name" in the configuration above, with a default value of `astrbot_wf_output`. You need to configure this variable name in the output node of the Dify Workflow, otherwise AstrBot cannot parse it correctly. + +#### Create API Key + +Create your Dify Workflow application's API Key as shown in the figure below: + +Click the Publish button in the upper right corner -> Access API -> click API Key in the upper right corner -> Create Key, then copy the API Key. + +![image](https://files.astrbot.app/docs/source/images/dify/workflow-api-key.png) + +Copy the key and paste it into the `API Key` field in the configuration, then click "Save". + +### Select Agent Runner + +Go to the Configuration page in the left sidebar, click "Agent Execution Method", select "Dify", then select the ID of the Dify Agent Runner you just created in the new configuration options that appear below, and click "Save" in the bottom right corner to complete the configuration. + +## Appendix: Dynamically Set Workflow Input Variables During Chat (Optional) + +You can use the `/set` command to dynamically set input variables, as shown in the figure below: + +![alt text](https://files.astrbot.app/docs/source/images/dify/image-5.png) + +After setting variables, AstrBot will attach the variables you set in the next request to Dify, flexibly adapting to your Workflow. + +![alt text](https://files.astrbot.app/docs/source/images/dify/image-4.png) + +Of course, you can use the `/unset` command to cancel the variables you set. + +Variables are permanently valid in the current session. + diff --git a/docs/en/providers/aihubmix.md b/docs/en/providers/aihubmix.md new file mode 100644 index 000000000..535f62929 --- /dev/null +++ b/docs/en/providers/aihubmix.md @@ -0,0 +1,70 @@ +# Connect AIHubMix + +[AIHubMix](https://aihubmix.com/?aff=4bfH) is a multi-model AI API gateway that provides unified access to OpenAI, Claude, Gemini, DeepSeek, Kimi and more through a single API key. Beyond LLM, it also supports speech, embedding, reranking and other capabilities. + +Fully compatible with the OpenAI API format — just change the API Base and Key to get started. **Some models are completely free for development and testing.** + +## Get an API Key + +1. Sign up at [AIHubMix](https://aihubmix.com/?aff=4bfH) +2. Go to Console → API Keys to create a new key + +![Get an API Key](https://github.com/user-attachments/assets/d717f21b-2805-4aff-ac90-f5c98f17cb79) + +## Configure in AstrBot + +Open the AstrBot dashboard , click **Providers → Add Provider → OpenAI**. + +Fill in the following: + +| Field | Value | +|-------|-------| +| API Base URL | `https://aihubmix.com/v1` | +| API Key | Your AIHubMix key | + +After saving, click the provider card to add models. + +![Configure in AstrBot](https://github.com/user-attachments/assets/ee2fb8ba-652c-4e97-a781-42a9082ad7eb) + +## Recommended Models + +### Free Models 🆓 + +These models are completely free, great for development and testing: + +| Model ID | Description | +|----------|-------------| +| `gpt-4.1-free` | GPT-4.1 free tier | +| `gemini-3-flash-preview-free` | Gemini 3 Flash free tier | +| `coding-glm-5-free` | GLM-5 coding model, free | +| `coding-minimax-m2.5-free` | MiniMax M2.5 coding model, free | + +### Paid Models (Popular) + +| Model ID | Provider | Description | +|----------|----------|-------------| +| `gpt-5.4` | OpenAI | Latest flagship model | +| `claude-sonnet-4-6` | Anthropic | Great for reasoning and code | +| `gpt-5.3-chat-latest` | OpenAI | High-performance chat | +| `deepseek-v3.2` | DeepSeek | Cost-effective | +| `kimi-k2.5` | Moonshot | Long context | +| `gemini-3.1-pro-preview` | Google | Multimodal | + +> See the full model list at [AIHubMix Docs](https://doc.aihubmix.com). + +## More Than Chat Models + +AIHubMix also supports the following capabilities, all configurable in AstrBot: + +| Capability | AstrBot Config Location | +|------------|------------------------| +| Speech-to-Text (STT) | Providers → Speech to Text | +| Text-to-Speech (TTS) | Providers → Text to Speech | +| Embedding | Providers → Embedding | +| Reranking | Providers → Rerank | + +All capabilities use the same API Key and API Base — no extra setup needed. + +## Set as Default + +Go to **Settings → Provider Settings**, set "Default Chat Model Provider" to your AIHubMix provider, and save. diff --git a/docs/en/providers/coze.md b/docs/en/providers/coze.md new file mode 100644 index 000000000..942b6c964 --- /dev/null +++ b/docs/en/providers/coze.md @@ -0,0 +1 @@ +This page is deprecated. Please refer to [Coze Agent Runner](../agent-runners/coze.md). diff --git a/docs/en/providers/dashscope.md b/docs/en/providers/dashscope.md new file mode 100644 index 000000000..a2d2443ca --- /dev/null +++ b/docs/en/providers/dashscope.md @@ -0,0 +1 @@ +This page is deprecated. Please refer to [Alibaba Cloud Bailian Application Agent Runner](../agent-runners/dashscope.md). diff --git a/docs/en/providers/dify.md b/docs/en/providers/dify.md new file mode 100644 index 000000000..de0f90245 --- /dev/null +++ b/docs/en/providers/dify.md @@ -0,0 +1 @@ +This page is deprecated. Please refer to [Dify Agent Runner](../agent-runners/dify.md). diff --git a/docs/en/providers/llm.md b/docs/en/providers/llm.md new file mode 100644 index 000000000..80f1a1fc8 --- /dev/null +++ b/docs/en/providers/llm.md @@ -0,0 +1,13 @@ +# 大语言模型提供商 + +你可在管理面板->服务提供商->+新增服务提供商 处配置各种大语言模型服务。 + +> [!TIP] +> 如果没有你希望接入的模型服务,你可以试着查看您希望接入的服务提供商处是否支持 兼容 OpenAI API,如果支持,那么你可以选择上面截图中的第一项 `OpenAI` 然后通过修改 API Base URL 的方式接入。 + +![image](https://files.astrbot.app/docs/source/images/llm/image.png) + +![image](https://files.astrbot.app/docs/source/images/llm/image-1.png) + + +> 相应的配置保存在 `data/cmd_config.json` 的 `provider` 字段中。 \ No newline at end of file diff --git a/docs/en/providers/newapi.md b/docs/en/providers/newapi.md new file mode 100644 index 000000000..2fa55e906 --- /dev/null +++ b/docs/en/providers/newapi.md @@ -0,0 +1,40 @@ +# NewAPI + +[NewAPI](http://newapi.ai/) is a next-generation LLM gateway and AI asset management system built on top of One API. It provides a unified interface for managing and using multiple AI model services, including OpenAI, Anthropic, Gemini, Midjourney, and more. + +AstrBot can integrate with NewAPI as a model provider, so you can access those model services through AstrBot. + +## Setup Steps + +### 1. Create a NewAPI API Key + +After registering and signing in to NewAPI, open `Console` in the top navigation bar, go to `Token Management`, then click `Add Token` to create a new API key with appropriate permissions. + +![create-api-key](https://files.astrbot.app/docs/source/images/newapi/image.png) + +After creation, copy the generated API key. + +![copy-api-key](https://files.astrbot.app/docs/source/images/newapi/image-1.png) + +### 2. Configure NewAPI in AstrBot + +Open AstrBot WebUI, go to `Service Providers`, and click `Add Provider`. + +NewAPI fully supports OpenAI Chat Completion and Responses APIs, so select `OpenAI` and open its provider configuration. + +Set `API Base URL` to your NewAPI endpoint: + +- Self-hosted NewAPI example: `http://localhost:3000/v1` +- Hosted service example: `https://api.example.com/v1` + +Then paste your API key into `API Key` and click `Save`. + +![astrbot-provider-config](https://files.astrbot.app/docs/source/images/newapi/image-2.png) + +### 3. Apply the Provider + +Go to `Configuration`, find the model section, set `Default Chat Model` to the NewAPI-based provider you just created, and click `Save`. + +![apply](https://files.astrbot.app/docs/source/images/newapi/image-3.png) + +You have now successfully configured NewAPI as an AstrBot model provider. diff --git a/docs/en/providers/ppio.md b/docs/en/providers/ppio.md new file mode 100644 index 000000000..fceb61dd1 --- /dev/null +++ b/docs/en/providers/ppio.md @@ -0,0 +1,43 @@ +# 接入 PPIO 派欧云 + +PPIO 派欧云是中国领先的独立分布式云计算服务商,您可以在派欧云上使用稳定、低价甚至免费的模型服务。 + +## 准备 + +打开 [PPIO 派欧云官网](https://ppio.cn/user/register?invited_by=AIOONE),并注册账户(通过此链接注册的账户将会获得 15 元人民币的代金券)。 + +进入 [模型 API 服务](https://ppio.cn/model-api/console),找到你想接入的模型。你可以通过筛选器选择不同厂商或者免费的模型。 + +![image](https://files.astrbot.app/docs/source/images/ppio/image-1.png) + +找到你想要接入的模型后,点击模型卡片,侧边会展开一个模型详情卡片,找到下方的 API 接入指南,如果您还没创建过 Key 可以点击创建。 + +![image](https://files.astrbot.app/docs/source/images/ppio/image-3.png) + +打开 AstrBot 控制台 -> 服务提供商页面,点击新增提供商,找到并点击 `PPIO派欧云`(需要版本 >= 3.5.10,旧版本也可使用,见下文)。 + +![image](https://files.astrbot.app/docs/source/images/ppio/image.png) + +将 API Key 和模型名称填入对话框表单,点击保存,即可完成创建。 + +> [!TIP] +> 如果您是 AstrBot 旧版本(< 3.5.10)的用户,请打开 AstrBot 控制台 -> 服务提供商页面,点击新增提供商,找到 `OpenAI`,点击进入。 +> 1. 将 ID 命名为 `ppio`(随意) +> 2. 然后将 `API Base URL` 设置为 `https://api.ppinfra.com/v3/openai` +> 3. 然后将 API Key 和模型名称填入对话框表单,点击保存,即可完成创建。 + + +## 使用 + +对机器人输入 `/provider` 指令,将提供商切换到刚刚添加的 PPIO 派欧云提供商,即可使用。 + +## 常见问题 + +#### 显示 `400` 错误 + +```log +Error code: 400 - {'code': 400, 'message': '"auto" tool choice requires --enable-auto-tool-choice and --tool-call-parser to be set', 'type': 'BadRequestError'} +``` + + +请暂时使用 `/tool off_all` 禁用所有的函数调用工具即可使用,或者换用其他模型。 \ No newline at end of file diff --git a/docs/en/providers/provider-lmstudio.md b/docs/en/providers/provider-lmstudio.md new file mode 100644 index 000000000..969f7d7c8 --- /dev/null +++ b/docs/en/providers/provider-lmstudio.md @@ -0,0 +1,38 @@ +# 接入 LM Studio 使用 DeepSeek-R1 等模型 + +LMStudio 允许在本地电脑上部署模型(需要电脑硬件配置符合要求) + +### 下载并安装 LMStudio + +https://lmstudio.ai/download + +### 下载并运行模型 + +https://lmstudio.ai/models + +跟随 LMStudio 下载并运行想要的模型,如 deepseek-r1-qwen-7b: + +```bash +lms get deepseek-r1-qwen-7b +``` + +### 配置 AstrBot + +在 AstrBot 上: + +点击 配置->服务提供商配置->加号->openai + +API Base URL 填写 `http://localhost:1234/v1` + +API Key 填写 `lm-studio` + +> 对于 Mac/Windows 使用 Docker Desktop 部署 AstrBot 部署的用户,API Base URL 请填写为 `http://host.docker.internal:1234/v1`。 +> 对于 Linux 使用 Docker 部署 AstrBot 部署的用户,API Base URL 请填写为 `http://172.17.0.1:1234/v1`,或者将 `172.17.0.1` 替换为你的公网 IP IP(确保宿主机系统放行了 1234 端口)。 + +如果 LM Studio 使用了 Docker 部署,请确保 1234 端口已经映射到宿主机。 + +模型名填写上一步选好的 + +保存配置即可。 + +> 输入 /provider 查看 AstrBot 配置的模型 \ No newline at end of file diff --git a/docs/en/providers/provider-ollama.md b/docs/en/providers/provider-ollama.md new file mode 100644 index 000000000..b3fc172ce --- /dev/null +++ b/docs/en/providers/provider-ollama.md @@ -0,0 +1,46 @@ +# Integrating Ollama + +🦙 Ollama is a free, open-source tool that lets you run large language models (LLMs) on your own computer. (hardware must meet requirements) + +## Download and Install Ollama + +You can download Ollama from [https://ollama.com](https://ollama.com/download). + +## Select and Pull a Model + +Choose the model you want to use at [https://ollama.com/search](https://ollama.com/search). + +In the terminal (PowerShell on Windows), enter `ollama pull ` to download the model. + +model_name format: `:`. For example, `deepseek-r1:8b`. +> The 8b parameter model requires at least 16GB of video memory (VRAM). Refer to other documentation for detailed information on configurations and parameter sizes. + +After pulling is complete, use `ollama list` to view the models you have pulled. + +Then use `ollama run ` to run the model. + +## Configure AstrBot + +Open the AstrBot WebUI, locate Service Provider Management, click on Add Provider, find and click on `Ollama`. +![image](https://files.astrbot.app/docs/source/images/ollama/image.png) + +Save the configuration. + +::: tip + +For Mac/Windows users deploying AstrBot with Docker Desktop, enter `http://host.docker.internal:11434/v1` for the API Base URL.\ +For Linux users deploying AstrBot with Docker, enter `http://172.17.0.1:11434/v1` for the API Base URL, or replace `172.17.0.1` with your public IP address (ensure that port 11434 is allowed by the host system).\ +If Ollama is deployed using Docker, ensure that port 11434 is mapped to the host. + +::: + +## FAQ + +Error: +``` +AstrBot request failed. +Error type: NotFoundError +Error message: Error code: 404 - {'error': {'message': 'model "llama3.1-8b" not found, try pulling it first', 'type': 'api_error', 'param': None, 'code': None}} + +``` +Please refer to the instructions above and use `ollama pull ` to pull the model, then use `ollama run ` to run the model. diff --git a/docs/en/providers/siliconflow.md b/docs/en/providers/siliconflow.md new file mode 100644 index 000000000..b986b02e3 --- /dev/null +++ b/docs/en/providers/siliconflow.md @@ -0,0 +1,15 @@ +# Connecting to SiliconFlow + +SiliconFlow leverages its proprietary inference engine to deliver efficient acceleration for large language model inference. It provides high-performance, cost-effective API services for a wide range of large models with pay-as-you-go pricing, making application development a breeze. + +## Configuring the Chat Model + +Navigate to the SiliconFlow [API Keys](https://cloud.siliconflow.cn/me/account/ak) page and create a new API Key. Save it for later use. + +Visit the SiliconFlow [Models page](https://cloud.siliconflow.cn/me/models) to select your desired model. Note down the model name for later use. + +Open the AstrBot WebUI, click `Service Providers` in the left sidebar -> `Add Provider` -> select `SiliconFlow`. + +Paste the `API Key` and `Model Name` you obtained earlier, then click Save to complete the setup. You can click the `Refresh` button under `Service Provider Availability` to verify whether the configuration is successful. + +![Configuring Chat Model](https://files.astrbot.app/docs/source/images/siliconflow/image.png) diff --git a/docs/en/providers/start.md b/docs/en/providers/start.md new file mode 100644 index 000000000..3cc5fe9f3 --- /dev/null +++ b/docs/en/providers/start.md @@ -0,0 +1,41 @@ +# Connecting Model Services + +AstrBot supports the native API formats of OpenAI, Google GenAI, and Anthropic. You can connect any model service provider that conforms to one of these three API formats. + +> [!NOTE] +> If you are located in mainland China, we strongly recommend using **official model providers** or compliant providers that follow local laws and regulations, for example: +> +> - [MoonshotAI](https://moonshot.cn/) +> - [GLM](https://bigmodel.cn/) +> - [MiniMax](https://www.minimax.io/) +> - [Qwen](https://qwen.ai/apiplatform) +> - [DeepSeek](https://deepseek.com/) +> +> These providers support the OpenAI API format. You can find the API Base URL and API Key from their documentation and fill them into AstrBot provider settings. +> +> Please note that using non-compliant third-party model services may introduce availability, privacy, or legal risks. For details, see the [EULA](https://github.com/AstrBotDevs/AstrBot/blob/master/EULA.md). + +For example, you may choose to connect model services provided by (but not limited to): + +- Official OpenAI model services ([OpenAI](https://openai.com/)) +- Official Anthropic model services ([Anthropic](https://www.anthropic.com/)) +- Google's Gemini model services via Google Cloud ([Google Cloud](https://cloud.google.com/)) +- OpenRouter model services ([OpenRouter](https://openrouter.ai/)) + +## Integration Steps Using DeepSeek as an Example + +Using DeepSeek as an example, assuming you have registered and logged in to a DeepSeek account, the steps to connect are: + +1. Go to the DeepSeek Console (https://platform.deepseek.com/). +2. Click the "API Keys" menu in the left sidebar, create a new API Key, and copy the key. +3. Click the "API Documentation" link near the bottom of the left sidebar to open the API documentation page. +4. On the API documentation page, find the section about the "OpenAI-compatible interface" and note the API Base URL, for example `https://api.deepseek.com/v1`. (If there is no `/v1`, please add `/v1`.) +5. Open the AstrBot Console -> Service Providers page, click Add Provider, find and click `OpenAI` (if the provider type you want to connect is listed, prefer clicking that type; for some providers like DeepSeek we provide optimized adapter support). Paste the API Key into the `API Key` field of the form and paste the API Base URL into the `API Base URL` field. +6. Click Get Model List, find the model you want to use, click the + button on the right, then toggle the switch that appears on the right to enable it. +7. Go to the Configuration page, find the conversational model, click the selection button on the right, choose the provider and model you just added, then click the Save Configuration button at the bottom-right of the screen. + +## Using Environment Variables to Load Keys + +> Introduced in v4.13.0. + +You can use environment variables to load provider API keys. In the provider configuration page, set the API Key field to `$ENV_VARIABLE_NAME`, for example: `$DEESEEK_API_KEY`. diff --git a/docs/en/providers/tokenpony.md b/docs/en/providers/tokenpony.md new file mode 100644 index 000000000..4a65ce109 --- /dev/null +++ b/docs/en/providers/tokenpony.md @@ -0,0 +1,23 @@ +# Connecting to TokenPony + +## Configuring the Chat Model + +Register and log in to [TokenPony](https://www.tokenpony.cn/3YPyf). + +Navigate to the TokenPony [API Keys](https://www.tokenpony.cn/#/user/keys) page and create a new API Key. Save it for later use. + +Visit the TokenPony [Models page](https://www.tokenpony.cn/#/model) to select your desired model. Note down the model name for later use. + +Open the AstrBot WebUI, click `Service Providers` in the left sidebar -> `Add Provider` -> select `TokenPony` (requires version >= 4.3.3) + +![Configuring Chat Model](https://files.astrbot.app/docs/source/images/tokenpony/image.png) + +> If you don't see the `TokenPony` option, you can also click `Connect to OpenAI` as shown in the image and change the `API Base URL` to `https://api.tokenpony.cn/v1`. + +Paste the `API Key` and `Model Name` you obtained earlier, then click Save to complete the setup. You can click the `Refresh` button under `Service Provider Availability` to verify whether the configuration is successful. + +## Applying the Chat Model + +In the AstrBot WebUI, click `Configuration` in the left sidebar, find `Default Chat Model` under AI Configuration, select the `tokenpony` (TokenPony) provider you just created, and click Save. + +![Configuring Chat Model 2](https://files.astrbot.app/docs/source/images/tokenpony/image_1.png) diff --git a/docs/en/use/agent-runner.md b/docs/en/use/agent-runner.md new file mode 100644 index 000000000..95a85a92b --- /dev/null +++ b/docs/en/use/agent-runner.md @@ -0,0 +1,52 @@ +# Agent Runner + +The Agent Runner is a component in AstrBot used to execute Agents. + +Starting from version v4.7.0, we have migrated three providers—Dify, Coze, and Alibaba Cloud Bailian Application—to the Agent Runner layer, reducing some conflicts with AstrBot's existing features. Rest assured, if you upgrade from an older version to v4.7.0, you don't need to take any action as AstrBot will automatically migrate for you. Later versions also added DeerFlow support as an Agent Runner provider. + +AstrBot currently supports five Agent Runners: + +- AstrBot Built-in Agent Runner +- Dify Agent Runner +- Coze Agent Runner +- Alibaba Cloud Bailian Application Agent Runner +- DeerFlow Agent Runner + +By default, the AstrBot Built-in Agent Runner is the default runner. + +## Why Abstract the Agent Runner + +In earlier versions, platforms with "built-in Agent capabilities" like Dify, Coze, and Alibaba Cloud Bailian Application were integrated into AstrBot as regular Chat Providers. In practice, we found that they are fundamentally different from traditional Chat Providers that "only handle text completion". Forcing them into the same layer caused many design and usage conflicts. Therefore, starting from v4.7.0, we abstracted them into independent Agent Runners. + +From an architectural perspective, you can understand it as: + +- Chat Provider is responsible for "talking"; +- Agent Runner is responsible for "thinking + doing". + +The Agent Runner calls the Chat Provider's interface and, based on the Chat Provider's response, performs multi-turn "perceive → plan → execute action → observe result → re-plan" loops. + +A Chat Provider is essentially a `single-turn completion interface`, taking prompt + conversation history + tool list as input and outputting model responses (text, tool call instructions, etc.). + +An Agent Runner is typically a `loop` that receives user intent, context, and environment state, makes plans based on strategy/model (Plan), selects and invokes tools (Act), reads results from the environment (Observe), understands the results again, updates internal state, decides the next action, and repeats this process until the task is completed or times out. + +![image](https://files.astrbot.app/docs/source/images/use/agent-runner/agent-arch.svg) + +Platforms like Dify, Coze, Bailian Application, and DeerFlow have this loop built-in. If you treat them as regular Chat Providers, it will conflict with AstrBot's built-in Agent Runner functionality. + +## Usage + +By default, the AstrBot Built-in Agent Runner is the default runner. Using the default runner can already meet most needs, and you can use AstrBot's MCP, knowledge base, web search, and other features. + +If you need to use the capabilities of platforms like Dify, Coze, Bailian Application, or DeerFlow, you can create an Agent Runner and select the corresponding provider. + +## Creating an Agent Runner + +![image](https://files.astrbot.app/docs/source/images/use/agent-runner/image-1.png) + +In the WebUI, click "Model Provider" -> "Add Provider", select "Agent Runner", choose the platform or runner type you want to connect to, and fill in the relevant information. + +## Changing the Default Agent Runner + +![image](https://files.astrbot.app/docs/source/images/use/agent-runner/image.png) + +In the WebUI, click "Configuration" -> "Agent Execution Method", change the runner type to the Agent Runner type you just created, then select `XX Agent Runner Provider ID` as the ID of the Agent Runner provider you just created, and click save. diff --git a/docs/en/use/astrbot-agent-sandbox.md b/docs/en/use/astrbot-agent-sandbox.md new file mode 100644 index 000000000..05940b6dc --- /dev/null +++ b/docs/en/use/astrbot-agent-sandbox.md @@ -0,0 +1,76 @@ +# Agent Sandbox Environment ⛵️ + +> [!TIP] +> This feature is currently in technical preview and may have some bugs. If you encounter any issues, please submit an issue on [GitHub](https://github.com/AstrBotDevs/AstrBot/issues). + +Starting from version `v4.12.0`, AstrBot introduced the Agent sandbox environment to replace the previous code executor functionality. The sandbox environment provides Agents with safer and more flexible code execution and automation capabilities. + +![](https://files.astrbot.app/docs/source/images/astrbot-agent-sandbox/image.png) + +## Enabling the Sandbox Environment + +Currently, the sandbox environment only supports running through Docker. We are currently using the [Shipyard](https://github.com/AstrBotDevs/shipyard) project as AstrBot's sandbox environment driver. In the future, we will support more types of sandbox environment drivers, such as e2b. + +### Deploying AstrBot and Shipyard with Docker Compose + +If you haven't deployed AstrBot yet, or want to switch to our recommended deployment method with sandbox environment, we recommend using Docker Compose to deploy AstrBot with the following code: + +```bash +git clone https://github.com/AstrBotDevs/AstrBot +cd AstrBot +# Modify the environment variable configuration in the compose-with-shipyard.yml file, such as Shipyard's access token, etc. +docker compose -f compose-with-shipyard.yml up -d +docker pull soulter/shipyard-ship:latest +``` + +This will start a Docker Compose service that includes AstrBot main program and the sandbox environment. + +### Deploying Shipyard Separately + +If you have already deployed AstrBot but haven't deployed the sandbox environment, you can deploy Shipyard separately. + +Code as follows: + +```bash +mkdir astrbot-shipyard +cd astrbot-shipyard +wget https://raw.githubusercontent.com/AstrBotDevs/shipyard/refs/heads/main/pkgs/bay/docker-compose.yml -O docker-compose.yml +# Modify the environment variable configuration in the compose-with-shipyard.yml file, such as Shipyard's access token, etc. +docker compose -f docker-compose.yml up -d +# pull the latest Shipyard ship image +docker pull soulter/shipyard-ship:latest +``` + +After successful deployment, the above command will start a Shipyard service that listens on `http://:8156` by default. + +> [!TIP] +> If you deploy AstrBot using Docker, you can also modify the Compose file above to place Shipyard's network in the same Docker network as AstrBot, so you don't need to expose Shipyard's port to the host machine. + +## Configuring AstrBot to Use the Sandbox Environment + +In the AstrBot console, go to the "Configuration Files" page and find "Agent Sandbox Environment", then enable the sandbox environment switch. + +In the configuration options that appear: + +For `Shipyard API Endpoint`, if you use the Docker Compose deployment method above, fill in `http://shipyard:8156`. If you deployed Shipyard separately, please fill in the corresponding address, such as `http://:8156`. + +For `Shipyard Access Token`, please fill in the access token you configured when deploying Shipyard. + +For `Shipyard Ship Lifetime (seconds)`, this defines the lifetime of each sandbox environment instance, with a default value of 3600 seconds (1 hour). You can adjust this value as needed. + +For `Shipyard Ship Session Reuse Limit`, this defines the maximum number of sessions that each sandbox environment instance can reuse, with a default value of 10. This means that 10 sessions will share the same sandbox environment instance. You can adjust this value as needed. + +After configuring, click the "Save" button at the bottom of the page to save the configuration. + +## About `Shipyard Ship Lifetime (seconds)` + +The lifetime of a sandbox environment instance defines the maximum time each instance can exist before being destroyed. This setting needs to be determined based on your usage scenario and resources. + +- When a new session joins an existing sandbox environment instance, the instance will automatically extend its lifetime to the TTL requested by this session. +- When an operation is performed on a sandbox environment instance, the instance will automatically extend its lifetime to the current time plus the TTL. + +## About Data Persistence in the Sandbox Environment + +Shipyard allocates a working directory for each session under the `/home/` directory. + +Shipyard automatically mounts the /home directory in the sandbox environment to the `${PWD}/data/shipyard/ship_mnt_data` directory on the host machine. When a sandbox environment instance is destroyed, if a session continues to request the sandbox, Shipyard will recreate a new sandbox environment instance and remount the previously persisted data to ensure data continuity. diff --git a/docs/en/use/astrbot-sandbox.md b/docs/en/use/astrbot-sandbox.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/en/use/code-interpreter.md b/docs/en/use/code-interpreter.md new file mode 100644 index 000000000..4b7a550d1 --- /dev/null +++ b/docs/en/use/code-interpreter.md @@ -0,0 +1,96 @@ +# Docker-based Code Interpreter + +> [!WARNING] +> Deprecated, please refer to the latest [Agent Sandbox Environment](/en/use/astrbot-agent-sandbox.md) documentation. This feature will be unavailable after v4.12.0. + +Starting from version `v3.4.2`, AstrBot supports a code interpreter to enhance LLM capabilities and enable various automated operations. + +> [!TIP] +> This feature is currently in experimental stage and may have some issues. If you encounter any problems, please submit an issue on [GitHub](https://github.com/AstrBotDevs/AstrBot/issues). Join our discussion group: [322154837](https://qm.qq.com/cgi-bin/qm/qr?k=EYGsuUTfe00_iOu9JTXS7_TEpMkXOvwv&jump_from=webapi&authKey=uUEMKCROfsseS+8IzqPjzV3y1tzy4AkykwTib2jNkOFdzezF9s9XknqnIaf3CDft). + +To use this feature, ensure that `Docker` is installed on your machine. This feature requires a dedicated Docker sandbox environment to execute code and prevent malicious code generated by the LLM from harming your machine. + + +## Running AstrBot with Docker on Linux + +If you've deployed AstrBot using Docker, some additional setup is required. + +1. When starting the Docker container, mount `/var/run/docker.sock` inside the container. This allows AstrBot to launch sandbox containers. + +```bash +sudo docker run -itd -p 6180-6200:6180-6200 -p 11451:11451 -v $PWD/data:/AstrBot/data -v /var/run/docker.sock:/var/run/docker.sock --name astrbot soulter/astrbot:latest +``` + +2. Use the `/pi absdir ` command during chat to set the absolute path of AstrBot's data directory on your host machine. + +Example: + +![image](https://files.astrbot.app/docs/source/images/code-interpreter/image-4.png) + +## Running AstrBot from Source on Linux + +**If your Docker commands require sudo privileges**, you need to start AstrBot with `sudo`, otherwise the code interpreter won't be able to invoke Docker due to insufficient permissions. + +```bash +sudo -E python3 main.py +``` + +## Usage + +This feature uses the `soulter/astrbot-code-interpreter-sandbox` image. You can view detailed information about the image on [Docker Hub](https://hub.docker.com/r/soulter/astrbot-code-interpreter-sandbox). + +The image includes commonly used Python libraries: + +- Pillow +- requests +- numpy +- matplotlib +- scipy +- scikit-learn +- beautifulsoup4 +- pandas +- opencv-python +- python-docx +- python-pptx +- pymupdf +- mplfonts + +Tasks that can be accomplished include: + +- Image editing +- Web scraping +- Data analysis and simple machine learning +- Document processing, such as reading and writing Word, PPT, PDF files +- Mathematical calculations, such as plotting graphs and solving equations + +Since Docker Hub is inaccessible from mainland China, if you're in that region, use `/pi mirror` to view/set the mirror source. For example, as of this writing, you can use `cjie.eu.org` as the mirror source by setting `/pi mirror cjie.eu.org`. + +When the code interpreter is triggered for the first time, AstrBot will automatically pull the image, which may take some time. Please be patient. + +The image may be updated periodically to provide more features, so check for updates regularly. If you need to update the image, use the `/pi repull` command to re-pull it. + +> [!TIP] +> If the feature doesn't start properly initially, after successful startup, execute `/tool on python_interpreter` to enable this feature. +> You can use `/tool ls` to view all tools and their enabled status. + +![image](https://files.astrbot.app/docs/source/images/code-interpreter/image-3.png) + +## Image and File Input + +In addition to recognizing and processing images and text tasks, the code interpreter can also recognize files you send and send files back. + +After v3.4.34, use the `/pi file` command to start uploading files. After uploading, you can use `/pi list` to view your uploaded files and `/pi clean` to clear your uploaded files. + +Uploaded files will be used as input for the code interpreter. + +For example, if you want to add rounded corners to an image, you can upload the image using `/pi file`, then ask: `Please run code to add rounded corners to this image`. + +## Demo + +![image](https://files.astrbot.app/docs/source/images/code-interpreter/a3cd3a0e-aca5-41b2-aa52-66b568bd955b.png) + +![alt text](https://files.astrbot.app/docs/source/images/code-interpreter/image.png) + +![image](https://files.astrbot.app/docs/source/images/code-interpreter/image-1.png) + +![image](https://files.astrbot.app/docs/source/images/code-interpreter/image-2.png) diff --git a/docs/en/use/command.md b/docs/en/use/command.md new file mode 100644 index 000000000..57896b2c9 --- /dev/null +++ b/docs/en/use/command.md @@ -0,0 +1,5 @@ +# Built-in Commands + +AstrBot has many built-in commands that are imported as plugins. They are located in the `packages/astrbot` directory. + +Use `/help` to view all built-in commands. diff --git a/docs/en/use/context-compress.md b/docs/en/use/context-compress.md new file mode 100644 index 000000000..c5664e2f2 --- /dev/null +++ b/docs/en/use/context-compress.md @@ -0,0 +1,40 @@ +# Context Compression + +Starting from v4.11.0, AstrBot introduced an automatic context compression feature. + +![alt text](https://files.astrbot.app/docs/source/images/context-compress/image.png) + +AstrBot automatically compresses the context when the conversation context **reaches 82% of the maximum context window length of the conversation model being used**, ensuring that as much conversation content as possible is retained without losing key information. + +## Compression Strategies + +There are currently two compression strategies: + +1. Truncate by conversation rounds. This strategy simply removes the earliest conversation content until the context length meets the requirements. You can specify the number of conversation rounds to discard at once, with a default of 1 round. This is the **default strategy**. +2. LLM-based context compression. This strategy calls the model itself to summarize and compress the conversation content, thereby retaining more key information. You can specify the conversation model to use for compression; if not selected, it will automatically fall back to the "truncate by conversation rounds" strategy. You can set the number of recent conversation rounds to retain during compression, with a default of 4. You can also customize the prompt used during compression. The default prompt is: + +``` +Based on our full conversation history, produce a concise summary of key takeaways and/or project progress. +1. Systematically cover all core topics discussed and the final conclusion/outcome for each; clearly highlight the latest primary focus. +2. If any tools were used, summarize tool usage (total call count) and extract the most valuable insights from tool outputs. +3. If there was an initial user goal, state it first and describe the current progress/status. +4. Write the summary in the user's language. +``` + +After one round of compression, AstrBot will perform a secondary check to verify if the current context length meets the requirements. If it still doesn't meet the requirements, it will adopt a halving strategy, cutting the current context content in half until the requirements are met. + +- AstrBot will invoke the compressor for checking before each conversation request. +- In the current version, AstrBot does not perform context compression during tool invocations. We will support this feature in the future, so stay tuned. + +## ‼️ Important: Model Context Window Settings + +By default, when you add a model, AstrBot automatically retrieves the model's context window size from the API provided by [MODELS.DEV](https://models.dev/) based on the model's ID. However, due to the wide variety of models and the fact that some providers even modify the model IDs, AstrBot cannot automatically infer the context window size for all models you add. + +You can manually set the model's context window size in the model configuration, as shown in the image below: + +![alt text](https://files.astrbot.app/docs/source/images/context-compress/image1.png) + +> [!NOTE] +> If you don't see the configuration option shown in the image above, please delete the model and re-add it. + +When the model context window size is set to 0, AstrBot will still automatically retrieve the model's context window size from MODELS.DEV for each request. If it remains 0, context compression will not be enabled for that request. diff --git a/docs/en/use/custom-rules.md b/docs/en/use/custom-rules.md new file mode 100644 index 000000000..7545da100 --- /dev/null +++ b/docs/en/use/custom-rules.md @@ -0,0 +1,17 @@ +# Custom Rules + +> [!NOTE] +> The "unified message origin" mentioned below refers to UMO. A UMO uniquely identifies a specific conversation on a messaging platform. + +Since version v4.7.0, we have refactored AstrBot's original "Session Management" feature into the "Custom Rules" feature to reduce conflicts with configuration files. + +You can think of custom rules as more flexible, mandatory processing rules for specified message sources, which have higher priority than configuration files. + +For example, if a messaging platform originally uses the "default" configuration file, all conversations under this platform are processed according to the rules in the configuration file. If you want to apply special processing to a specific session source A, previously you would need to create a separate configuration file and bind A to it. Now, you simply need to create a custom rule in the WebUI's Custom Rules page and select message source A. You can define the following rules: + +1. Whether to enable message processing for this unified message origin. If disabled, the effect is equivalent to blacklisting this unified message origin. +2. Whether to enable LLM for messages from this unified message origin. If disabled, AI capabilities will not be used. +3. Whether to enable TTS for messages from this unified message origin. If disabled, TTS capabilities will not be used. +4. Configure specific chat models, speech recognition models (STT), and text-to-speech models (TTS) for this unified message origin. +5. Configure a specific persona for this unified message origin. + diff --git a/docs/en/use/function-calling.md b/docs/en/use/function-calling.md new file mode 100644 index 000000000..1526cbab9 --- /dev/null +++ b/docs/en/use/function-calling.md @@ -0,0 +1,54 @@ +--- +outline: deep +--- + +# Function Calling + +## Introduction + +Function calling aims to provide large language models with **the ability to invoke external tools**, enabling various Agentic functionalities. + +For example, when you ask the LLM: "Help me search for information about cats", the model will call external search tools, such as search engines, and return the search results. + +Here is the revised text, updated to reflect your new content while maintaining a formal documentation tone: + +Currently, supported models include but are not limited to: + +- GPT-5.x series +- Gemini 3.x series +- Claude 4.x series +- DeepSeek v3.2 (deepseek-chat) +- Qwen 3.x series + +Mainstream models released after 2025 typically support function calling. + +Commonly unsupported models include older models such as DeepSeek-R1 and Gemini 2.0 thinking-type models. + +In AstrBot, web search, todo reminders, and code interpreter tools are provided by default. Many plugins, such as: + +- astrbot_plugin_cloudmusic +- astrbot_plugin_bilibili +- ... + +In addition to providing traditional command invocation, also offer function calling capabilities. + +Related commands: + +- `/tool ls` - View the list of available tools +- `/tool on` - Enable a specific tool +- `/tool off` - Disable a specific tool +- `/tool off_all` - Disable all tools + +Some models may not support function calling and will return errors such as `tool call is not supported`, `function calling is not supported`, `tool use is not supported`, etc. In most cases, AstrBot can detect these errors and automatically remove function calling tools for you. If you find that a model doesn't support function calling, you can also use the `/tool off_all` command to disable all tools and try again, or switch to a model that supports function calling. + + +Below are some common tool calling demos: + +![image](https://files.astrbot.app/docs/source/images/function-calling/image.png) + +![image](https://files.astrbot.app/docs/source/images/function-calling/image-1.png) + + +## MCP + +Please refer to this documentation: [AstrBot - MCP](/use/mcp). diff --git a/docs/en/use/knowledge-base.md b/docs/en/use/knowledge-base.md new file mode 100644 index 000000000..b1f9e1dc1 --- /dev/null +++ b/docs/en/use/knowledge-base.md @@ -0,0 +1,59 @@ + +# AstrBot Knowledge Base + +> [!TIP] +> Requires AstrBot version >= 4.5.0. + +![Knowledge Base Preview](https://files.astrbot.app/docs/en/use/image-3.png) + +## Configuring Embedding Model + +Open the service provider page, click "Add Service Provider", and select Embedding. + +Currently, AstrBot supports embedding vector services compatible with OpenAI API and Gemini API. + +Click on the provider card above to enter the configuration page and fill in the configuration. + +After completing the configuration, click Save. + +## Configuring Reranker Model (Optional) + +A reranker model can improve the precision of final retrieval results to some extent. + +Similar to configuring the embedding model, open the service provider page, click "Add Service Provider", and select Reranker. For more information about reranker models, please refer to online resources. + +## Creating a Knowledge Base + +AstrBot supports multiple knowledge base management. During chat, you can **freely specify which knowledge base to use**. + +Enter the knowledge base page and click "Create Knowledge Base", as shown below: + +![image](https://files.astrbot.app/docs/source/images/knowledge-base/image.png) + +Fill in the relevant information. In the embedding model dropdown menu, you will see the embedding model and reranker model you just created (reranker model is optional). + +> [!TIP] +> Once you've selected an embedding model for a knowledge base, do not modify the **model** or **vector dimension information** of that provider, as this will **seriously affect** the retrieval accuracy of the knowledge base or even **cause errors**. + +## Uploading Files + +After creating a knowledge base, you can upload documents to it. Up to 10 files can be uploaded simultaneously, with a maximum size of 128 MB per file. + +![Upload Files](https://files.astrbot.app/docs/en/use/image-4.png) + +## Using the Knowledge Base + +In the configuration file, you can specify different knowledge bases for different configuration profiles. + +## Appendix 2: Applying for Free Embedding Models + +### PPIO Cloud + +1. Open the [PPIO Cloud website](https://ppio.cn/user/register?invited_by=AIOONE) and register an account (accounts registered through this link will receive a 15 RMB voucher). +2. Go to the [Model Marketplace](https://ppio.cn/model-api/console) and click on Embedding Models. +3. Click on BAAI:BGE-M3 (as of 2025-06-02, this model is free on this platform). +4. Find the API integration guide and apply for a Key. +5. Fill in the AstrBot OpenAI Embedding model provider configuration: + 1. API Key is the PPIO API Key you just applied for + 2. embedding api base: enter `https://api.ppinfra.com/v3/openai` + 3. model: enter the model you selected, in this example `baai/bge-m3`. diff --git a/docs/en/use/mcp.md b/docs/en/use/mcp.md new file mode 100644 index 000000000..1681d3132 --- /dev/null +++ b/docs/en/use/mcp.md @@ -0,0 +1,102 @@ + +# MCP + +MCP (Model Context Protocol) is a new open standard protocol for establishing secure bidirectional connections between large language models and data sources. Simply put, it extracts function tools as independent services, allowing AstrBot to remotely invoke these function tools via the MCP protocol, which then return results to AstrBot. + +![image](https://files.astrbot.app/docs/source/images/function-calling/image3.png) + +AstrBot v3.5.0 supports the MCP protocol, enabling you to add multiple MCP servers and use function tools from MCP servers. + +![image](https://files.astrbot.app/docs/source/images/function-calling/image2.png) + +## Initial Configuration + +MCP servers are typically launched using `uv` or `npm`, so you need to install these two tools. + +For `uv`, you can install it directly via pip. Quick installation via AstrBot WebUI: + +![image](https://files.astrbot.app/docs/en/use/image.png) + +Just enter `uv`. + +If you're deploying AstrBot with Docker, you can also execute the following command for quick installation: + +```bash +docker exec astrbot python -m pip install uv +``` + +If you're deploying AstrBot from source, please install it within the created virtual environment. + +For `npm`, you need to install `node`. + +If you're deploying AstrBot from source or using one-click installation, please refer to [Download Node.js](https://nodejs.org/en/download) to download to your local machine. + +If you're using Docker to deploy AstrBot, you need to install `node` in the container (future AstrBot Docker images will include `node` by default). Please execute the following commands: + +```bash +sudo docker exec -it astrbot /bin/bash +apt update && apt install curl -y +export NVM_NODEJS_ORG_MIRROR=http://nodejs.org/dist +# Download and install nvm: +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash +\. "$HOME/.nvm/nvm.sh" +nvm install 22 +# Verify version: +node -v +nvm current +npm -v +npx -v +``` + +After installing `node`, you need to restart `AstrBot` to apply the new environment variables. + +## Installing MCP Servers + +If you're deploying AstrBot with Docker, please install MCP servers in the data directory. + +### An Example + +I want to install an MCP server for querying papers on Arxiv and found this repository: [arxiv-mcp-server](https://github.com/blazickjp/arxiv-mcp-server). Referring to its README, + +We extract the necessary information: + +```json +{ + "command": "uv", + "args": [ + "tool", + "run", + "arxiv-mcp-server", + "--storage-path", "data/arxiv" + ] +} +``` + +If the MCP server you need requires environment variables to configure something (e.g. access token), you could use the command-line tool `env`: + +```json +{ + "command": "env", + "args": [ + "XXX_RESOURCE_FROM=local", + "XXX_API_URL=https://xxx.com", + "XXX_API_TOKEN=sk-xxxxx", + "uv", + "tool", + "run", + "xxx-mcp-server", + "--storage-path", "data/res" + ] +} +``` + +Configure it in the AstrBot WebUI: + +![image](https://files.astrbot.app/docs/en/use/image-2.png) + +That's it. + +Reference links: + +1. Learn how to use MCP here: [Model Context Protocol](https://modelcontextprotocol.io/introduction) +2. Get commonly used MCP servers here: [awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers/blob/main/README-zh.md#what-is-mcp), [Model Context Protocol servers](https://github.com/modelcontextprotocol/servers), [MCP.so](https://mcp.so) diff --git a/docs/en/use/plugin.md b/docs/en/use/plugin.md new file mode 100644 index 000000000..194c6b4f0 --- /dev/null +++ b/docs/en/use/plugin.md @@ -0,0 +1,7 @@ +# AstrBot Star + +Starting from version `3.4.0`, AstrBot renamed plugins to `Star`. AstrBot is a highly modular project, and plugins leverage this modularity to implement various functionalities. + +Use `/plugin` to view all plugins. You can also manage installed plugins in the admin panel. + +If you want to develop your own plugin, see [AstrBot Plugin Development Guide](/en/dev/star/plugin-new). diff --git a/docs/en/use/proactive-agent.md b/docs/en/use/proactive-agent.md new file mode 100644 index 000000000..72ff9cb38 --- /dev/null +++ b/docs/en/use/proactive-agent.md @@ -0,0 +1,52 @@ +# Proactive Capabilities + +AstrBot introduces a Proactive Agent system, enabling AstrBot to not only respond passively to users but also schedule future tasks and proactively execute them at specified times, delivering results (text, images, files, etc.) to users. + +![](https://files.astrbot.app/docs/source/images/proactive-agent/image.png) + +Introduced in v4.14.0, this is currently an **experimental feature** and not yet stable. + +## Future Tasks (FutureTask) + +The Main Agent can now manage a global **Cron Job List**, setting tasks for its future self. + +### Features + +- **Self-Wakeup**: AstrBot automatically wakes up at the scheduled time to execute tasks. +- **Task Feedback**: After execution, AstrBot reports the results back to the task creator. +- **WebUI Management**: You can view, edit, or delete scheduled tasks in the "Future Tasks" page of the WebUI. + +### How to Use + +> [!TIP] +> First, ensure that "Proactive Capabilities" is enabled in the configuration. + +The Main Agent has the ability to manage scheduled tasks. You can tell it: +- "Remind me to have a meeting at 8 AM tomorrow." +- "Summarize this week's work log every Friday at 5 PM." +- "Set a timer for 10 minutes." + +The Main Agent will call built-in scheduling tools to arrange these plans. + +You can view and manage all future tasks by clicking **Future Tasks** in the left navigation bar of the AstrBot WebUI. + +![](https://files.astrbot.app/docs/source/images/proactive-agent/image-1.png) + +### Supported Platforms + +Scheduling tasks is supported on all platforms. However, due to some platforms not providing APIs for proactive message pushing, only the following platforms support AstrBot proactively pushing results to users: +- Telegram +- OneBot (QQ) +- Slack +- Feishu (Lark) +- Discord +- Misskey +- Satori + +## Sending Multimedia Messages + +To make it easier for Agents to send images, audio, video, and other files directly to users, AstrBot provides a `send_message_to_user` tool by default. + +### Features +- **Direct Sending**: Agents can send generated or retrieved multimedia files directly to users without complex text conversions. +- **Multiple Formats**: Supports images, files, audio, video, etc. diff --git a/docs/en/use/skills.md b/docs/en/use/skills.md new file mode 100644 index 000000000..a57a5b12c --- /dev/null +++ b/docs/en/use/skills.md @@ -0,0 +1,37 @@ +# Anthropic Skills + +Anthropic's Agent Skills are a modular extension standard designed to turn Claude from a "general-purpose chatbot" into a "task executor" with domain-specific expertise. A Skill is a structured folder containing instructions, scripts, metadata, and reference resources. It is more than just a prompt—it functions like a specialized "operation manual" that is dynamically loaded only when the Agent needs to perform a specific task. A Tool is the model's concrete interface for interacting with the outside world (APIs/functions), while a Skill standardizes the combination of instructions, templates, and tools into a reusable task execution guide. Traditional Tools require all API definitions to be injected into the prompt at conversation start. If there are more than 50 tools, tens of thousands of tokens can be consumed before any conversation begins, making responses slower and costlier. + +Support for Anthropic Skills was introduced in AstrBot starting from v4.13.0, allowing users to easily integrate and use various predefined skill modules to improve the Agent's performance on specific tasks. + +## Key Features + +- Progressive Disclosure: The model initially loads only skill names and short descriptions. Detailed `SKILL.md` instructions are loaded only when a task matches, saving context window space and reducing cost. +- Highly Reusable: Skills can be used across different Claude API projects, Claude Code, or Claude.ai. +- Executable Capability: Skills can include executable code scripts that, together with Anthropic's code execution environment, can directly generate or process files. + +## Uploading Skills to AstrBot + +Open the AstrBot admin panel, navigate to the `Plugins` page, and find `Skills`. + +![Skills](https://files.astrbot.app/docs/source/images/skills/image.png) + +You can upload Skills with the following requirements: + +1. The upload must be a `.zip` archive. +2. **After extraction, it must contain a single Skill folder. The folder name will be used as the identifier for the Skill in AstrBot—please name it using English characters.** +3. The Skill folder must include a file named `SKILL.md`, and its contents should preferably follow the Anthropic Skills specification. You can refer to Anthropic's documentation: https://code.claude.com/docs/zh-CN/skills + +## Using Skills in AstrBot + +Skills serve as operation manuals for Agents and often include executable Python snippets and scripts. Therefore, an Agent requires an **execution environment**. + +Currently, AstrBot provides two execution environments: + +- Local — The Agent runs in your AstrBot runtime environment. **Use with caution: this allows the Agent to execute arbitrary code in your environment, which may pose security risks.** +- Sandbox — The Agent runs inside an isolated sandbox environment. **You must enable AstrBot sandbox mode first.** See: /use/astrbot-agent-sandbox. If sandbox mode is not enabled, Skills will not be passed to the Agent. + +You can select the default execution environment on the `Config` page under "Computer Use". + +> [!NOTE] +> Please note: if you select `Local` as the execution environment, AstrBot currently only allows **AstrBot administrators** to request that the Agent operate on your local environment. Regular users are prohibited from doing so. The Agent will be prevented from executing code locally via Shell, Python, or other tools and will receive a permission restriction message such as `Sorry, I cannot execute code on your local environment due to permission restrictions.`. diff --git a/docs/en/use/subagent.md b/docs/en/use/subagent.md new file mode 100644 index 000000000..da6e5a728 --- /dev/null +++ b/docs/en/use/subagent.md @@ -0,0 +1,56 @@ +# Agent Handoff and SubAgent + +SubAgent Orchestration is an advanced agent organization method provided by AstrBot. It allows you to decompose complex tasks into multiple specialized SubAgents, reducing the Main Agent's prompt length and improving task execution success rates. + +v4.14.0 introduced this feature, which is currently an **experimental feature** and not yet stable. + +![](https://files.astrbot.app/docs/source/images/subagent/image.png) + +## Motivation + +In traditional architectures, all tools are directly mounted on the Main Agent. When there are many tools, several issues arise: +1. **Prompt Bloat**: The Main Agent must include descriptions for all tools in its System Prompt, consuming excessive context. +2. **Execution Errors**: With a large number of tools, the LLM may confuse tool purposes or generate incorrect parameters. +3. **Complexity**: The Main Agent is overburdened with both conversation and the organization/invocation of numerous tools. + +With SubAgent Orchestration, the Main Agent is only responsible for user interaction and **task delegation**. Actual tool execution is handled by specialized SubAgents. + +## How It Works + +1. **Main Agent Delegation**: When SubAgent mode is enabled, the Main Agent only sees a series of delegation tools named `transfer_to_`. +2. **Task Handoff**: When the Main Agent determines a task needs execution, it calls the corresponding delegation tool, passing the task description to the SubAgent. +3. **SubAgent Execution**: The SubAgent receives the task, performs operations using its assigned tools, and returns the organized results to the Main Agent. +4. **Feedback**: The Main Agent receives the results and continues the conversation with the user. + +![](https://files.astrbot.app/docs/source/images/subagent/1.png) + +## Configuration + +In the AstrBot WebUI, click **SubAgents** in the left navigation bar. + +### 1. Enable SubAgent Mode + +Toggle "Enable SubAgent Orchestration" at the top of the page. + +### 2. Create a SubAgent + +Click the "Add SubAgent" button: + +- **Agent Name**: Used to generate the delegation tool name (e.g., `transfer_to_weather`). Use lowercase and underscores. +- **Select Persona**: Choose a preset Persona, which defines the SubAgent's basic character, behavioral guidance, and the Tools collection it can use. You can create and manage Personas on the "Persona Settings" page. +- **Description for Main LLM**: This description tells the Main Agent what this SubAgent is good at, ensuring accurate delegation. +- **Assign Tools**: Select the tools this SubAgent can invoke. +- **Provider Override (Optional)**: You can specify different model providers for specific SubAgents. For example, the Main Agent could use GPT-4o, while a simple query SubAgent uses GPT-4o-mini to save costs. + +## Best Practices + +- **Single Responsibility**: Each SubAgent should handle one category of related tasks (e.g., search, file processing, smart home control). +- **Clear Descriptions**: Descriptions for the Main Agent should be concise and highlight the SubAgent's core capabilities. +- **Layered Management**: For extremely complex tasks, consider multi-level delegation if necessary. + +## Known Issues + +SubAgent orchestration is currently an **experimental feature** and not yet stable. + +1. Skills of personas cannot be isolated at this time. +2. SubAgent conversation histories are not currently saved. diff --git a/docs/en/use/unified-webhook.md b/docs/en/use/unified-webhook.md new file mode 100644 index 000000000..63bdbff73 --- /dev/null +++ b/docs/en/use/unified-webhook.md @@ -0,0 +1,28 @@ +# Unified Webhook Mode + +Starting from v4.8.0, AstrBot supports Unified Webhook Mode (unified_webhook_mode). When this mode is enabled, all platform adapters that support it will use the same Webhook callback endpoint, simplifying reverse proxy and domain configuration. You no longer need to configure separate ports, domains, and reverse proxies for each bot adapter. + +Platform adapters that support Unified Webhook Mode include: + +- Slack Webhook Mode +- WeChat Official Account +- WeCom Application +- WeCom AI Bot +- WeChat Customer Service Bot +- QQ Official Bot Webhook Mode +- ... + +## How to Use Unified Webhook Mode + +1. Have a domain (e.g., example.com) and a server with a public IP +2. Configure DNS resolution (e.g., astrbot.example.com) +3. Configure reverse proxy to forward requests from port 80 or 443 of your domain to AstrBot's WebUI port (default is 6185) +4. Go to AstrBot's `Configuration` page, click `System`, and set the `Externally Reachable Callback URL` to your configured URL (e.g., https://astrbot.example.com). Click save and wait for restart. + +When configuring each platform adapter afterwards, enable `Unified Webhook Mode (unified_webhook_mode)`. + +![unified_webhook](https://files.astrbot.app/docs/source/images/use/unified-webhook-config.png) + +Once this mode is enabled, AstrBot will generate a unique Webhook callback URL for you. You just need to fill this URL into each platform's callback address field. + +![unified_webhook](https://files.astrbot.app/docs/source/images/use/unified-webhook.png) diff --git a/docs/en/use/websearch.md b/docs/en/use/websearch.md new file mode 100644 index 000000000..82e77bb93 --- /dev/null +++ b/docs/en/use/websearch.md @@ -0,0 +1,35 @@ + +# Web Search + +The web search feature aims to provide large language models with the ability to invoke search engines like Google, Bing, and Sogou to obtain recent world information, which can improve the accuracy of model responses and reduce hallucinations to some extent. + +AstrBot's built-in web search functionality relies on the large language model's `function calling` capability. If you're not familiar with function calling, please refer to: [Function Calling](/use/websearch). + +When using a large language model that supports function calling with the web search feature enabled, you can try saying: + +- `Help me search for xxx` +- `Help me summarize this link: https://soulter.top` +- `Look up xxx` +- `Recent xxxx` + +And other prompts with search intent to trigger the model to invoke the search tool. + +AstrBot supports 3 types of web search source integration: `default`, `Tavily`, and `Baidu AI Search`. + +The former uses AstrBot's built-in web search requester to query Google, Bing, and Sogou search engines, performing best in network environments with Google access. **We recommend using Tavily**. + +![image](https://files.astrbot.app/docs/source/images/websearch/image.png) + +Go to `Configuration`, scroll down to find Web Search, where you can select `default` (default, not recommended) or `Tavily`. + +### default (Not Recommended) + +If your device is in China and you have a proxy, you can enable the proxy and enter the HTTP proxy address in `Admin Panel - Other Configuration - HTTP Proxy` to apply the proxy. + +### Tavily + +Go to [Tavily](https://app.tavily.com/home) to get an API Key, then fill it in the corresponding configuration item. + +If you use Tavily as your web search source, you will get a better experience optimization on AstrBot ChatUI, including citation source display and more: + +![](https://files.astrbot.app/docs/source/images/websearch/image1.png) diff --git a/docs/en/use/webui.md b/docs/en/use/webui.md new file mode 100644 index 000000000..986946370 --- /dev/null +++ b/docs/en/use/webui.md @@ -0,0 +1,79 @@ +# Admin Panel + +The AstrBot admin panel features plugin management, log viewing, visual configuration, statistics viewing, and more. + +![image](https://files.astrbot.app/docs/source/images/webui/image-4.png) + +## Accessing the Admin Panel + +After starting AstrBot, you can access the admin panel by visiting `http://localhost:6185` in your browser. + +> [!TIP] +> - If you're deploying AstrBot on a cloud server, replace `localhost` with your server's IP address. + +## Login + +The default username and password are both `astrbot`. + +## Visual Configuration + +In the admin panel, you can configure AstrBot's plugins through visual configuration. Click `Configuration` in the left sidebar to enter the configuration page. + +![image](https://files.astrbot.app/docs/source/images/webui/image-3.png) + +After modifying the configuration, you need to click the `Save` button in the bottom right corner to successfully save the configuration. + +Use the first circular button in the bottom right corner to switch to `Code Edit Configuration`. In `Code Edit Configuration`, you can directly edit the configuration file. + +After editing, first click `Apply This Configuration`, which will apply the configuration to the visual configuration, then click the `Save` button in the bottom right corner to save the configuration. If you don't click `Apply This Configuration`, your modifications won't take effect. + +![alt text](https://files.astrbot.app/docs/source/images/webui/image-5.png) + +## Plugins + +In the admin panel, you can view installed plugins and install new plugins through the `Plugins` section in the left sidebar. + +Click the Plugin Market tab to browse plugins officially listed by AstrBot. + +![image](https://files.astrbot.app/docs/source/images/webui/image-1.png) + +You can also click the + button in the bottom right corner to manually install plugins via URL or file upload. + +> Due to the plugin update mechanism, the AstrBot Team cannot fully guarantee the security of plugins in the plugin market. Please carefully verify them. The AstrBot Team is not responsible for any losses caused by plugins. + +### Handling Plugin Load Failures + +If a plugin fails to load, the admin panel will display the error message and provide a **"Try one-click reload fix"** button. This allows you to quickly reload the plugin after fixing the environment (e.g., installing missing dependencies) or modifying the code, without having to restart the entire application. + +## Command Management + +Use the `Command Management` menu on the left to centrally manage all registered commands; system plugins are hidden by default. + +Filter by plugin, type (command / command group / subcommand), permission, and status, and combine with the search box for quick lookup. Command group rows can expand to show subcommands, badges display the subcommand count, and subcommand rows are indented to indicate hierarchy. + +You can enable/disable and rename each command. + +## Trace + +In the `Trace` page of the admin panel, you can view the real-time execution trace of AstrBot. This is useful for debugging model call paths, tool invocation processes, etc. + +You can enable or disable trace recording using the switch at the top of the page. + +> [!NOTE] +> Currently only recording partial model call paths from AstrBot main Agent. More coverage will be added. + +## Updating the Admin Panel + +When AstrBot starts, it automatically checks if the admin panel needs updating. If it does, the first log entry (in yellow) will prompt you. + +Use the `/dashboard_update` command to manually update the admin panel (admin command). + +Admin panel files are located in the data/dist directory. If you need to manually replace them, download `dist.zip` from https://github.com/AstrBotDevs/AstrBot/releases/ and extract it to the data directory. + +## Customizing WebUI Port + +Modify the `port` in the `dashboard` configuration in the data/cmd_config.json file. + +## Forgot Password + +Modify the `password` in the `dashboard` configuration in the data/cmd_config.json file and delete the entire password key-value pair. diff --git a/docs/en/what-is-astrbot.md b/docs/en/what-is-astrbot.md new file mode 100644 index 000000000..57ce87892 --- /dev/null +++ b/docs/en/what-is-astrbot.md @@ -0,0 +1,29 @@ +--- +outline: deep +--- + +# 👋 I'm AstrBot + +## Introduction + +AstrBot is an open-source, all-in-one Agentic assistant for personal and group chats. It can be deployed across dozens of mainstream instant messaging platforms, such as QQ, Telegram, WeCom, Lark, DingTalk, and Slack. It also includes a lightweight built-in ChatUI (similar to OpenWebUI), providing reliable and extensible conversational AI infrastructure for individuals, developers, and teams. Whether you are building a personal AI companion, an intelligent customer service assistant, an automation bot, or an enterprise knowledge base, AstrBot helps you build AI applications directly inside your IM workflows. + +## Documentation Overview + +This documentation is divided into the following sections: + +- **Deployment**: multiple ways to quickly deploy AstrBot on local machines or cloud servers. +- **Messaging Platform Integration**: integration guides for 18+ mainstream instant messaging platforms. +- **AI Provider Integration**: connect to model providers, use AstrBot's built-in Agent Runner, or integrate third-party Agent Runner services such as Dify, Coze, Alibaba Bailian, and DeerFlow. +- **Usage Guides**: practical guides for features such as plugins, tool calling, knowledge base, MCP, Skills, and Agent sandbox. + +## Quick Start + +- Deploy AstrBot: Read the Deployment Guide to quickly deploy AstrBot on your local machine or cloud server. +- Connect to IM platforms: Follow the instructions to connect AstrBot to your preferred IM platforms such as Discord, Telegram, Slack, etc. +- Configure AI models: AstrBot supports various AI models. See [Connecting Model Services](/en/providers/start) + +## Notice + +1. AstrBot is a non-profit project under the AstrBotDevs organization, maintained by open-source contributors worldwide, and protected by the [AGPL-v3](https://www.chinasona.org/gnu/agpl-3.0-cn.html) license. If you modify AstrBot and use it to provide commercial network services, you must open-source your modifications. For details, contact [community@astrbot.app](mailto:community@astrbot.app). +2. Before using this project, please read the End User License Agreement (EULA): [End User License Agreement](https://github.com/AstrBotDevs/AstrBot/blob/master/EULA.md). If you do not agree to any terms of the agreement, do not use this project. diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 000000000..8d39e9673 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,13 @@ +{ + "scripts": { + "docs:dev": "vitepress dev --host", + "docs:build": "vitepress build", + "docs:preview": "vitepress preview" + }, + "devDependencies": { + "vitepress": "^1.6.4" + }, + "dependencies": { + "vue": "^3.5.17" + } +} diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml new file mode 100644 index 000000000..acc620223 --- /dev/null +++ b/docs/pnpm-lock.yaml @@ -0,0 +1,1554 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + vue: + specifier: ^3.5.17 + version: 3.5.17 + devDependencies: + vitepress: + specifier: ^1.6.4 + version: 1.6.4(@algolia/client-search@5.31.0)(postcss@8.5.6)(search-insights@2.17.3) + +packages: + + '@algolia/autocomplete-core@1.17.7': + resolution: {integrity: sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==} + + '@algolia/autocomplete-plugin-algolia-insights@1.17.7': + resolution: {integrity: sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==} + peerDependencies: + search-insights: '>= 1 < 3' + + '@algolia/autocomplete-preset-algolia@1.17.7': + resolution: {integrity: sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/autocomplete-shared@1.17.7': + resolution: {integrity: sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/client-abtesting@5.31.0': + resolution: {integrity: sha512-J+wZq5uotbisEsbKmXv79dsENI/AW6IZWIvfTqebE6QcH/S2yGDeNh6b4qa4koJ1eQx7+wKkLMfZ+nOZpBWclA==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-analytics@5.31.0': + resolution: {integrity: sha512-zxz9ooi6HsMG7gS7xCG9NkUlWkpwMT/oYr8+cojchB98pEmn3OqHA7KaY1w8GKqKXNM4MiQD15N2/aZhDa9b9g==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-common@5.31.0': + resolution: {integrity: sha512-lO6oZLEPiCgtUcUHIFyfrRvcS8iB3Je1LqW3c04anjrCO7dqhkccXHC/5XuH0fIW4l7V5AtbPS2tpJGtRp1NJw==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-insights@5.31.0': + resolution: {integrity: sha512-gwWTW4CMM6pov3aJv2a+Ex4v7fWG9wtey43qWBq5rABk3p3uYYFkzfylrht18rcq1zA99Wxo8UEireExHuzs2w==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-personalization@5.31.0': + resolution: {integrity: sha512-3G8ZpoLCgrcuILTQGVU9WXxUmK4R8uUmAiU31Qqd/pkta/9J8DHQjNh+Fs/i27ls2YxQq36GqXvVM2eoQFmFJw==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-query-suggestions@5.31.0': + resolution: {integrity: sha512-+YIHy+n+x2/DqRdnrPv2Eck2pbZ4Q5Lu1mWpwOUZ2u2XG6JVQx0goePomtYl8evsDGspDRZJPpGD+CFJboe0gQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-search@5.31.0': + resolution: {integrity: sha512-2I79ICkuTqbXeK5RGSmzCN1Uj86NghWxaWt41lIcFk1OXuUWhyXTxC2fN5M8ASRBf/qWSeXr6AzL8jb3opya3g==} + engines: {node: '>= 14.0.0'} + + '@algolia/ingestion@1.31.0': + resolution: {integrity: sha512-HiBWdO7ztzgFoR+SnbHq0iBQtDUusRZPSVMkPIR/MNbNJrH/OhrCsxk6Y7dUvQAIjypKmFl38raf1XEKz9fdUA==} + engines: {node: '>= 14.0.0'} + + '@algolia/monitoring@1.31.0': + resolution: {integrity: sha512-ifrQ3BMg7Z4EGBPouUINd7xVU2ySTrJ2FtuAoiRHaZ7rT1Kp56JW40kuHiCvmDI4ZBaIzrQuGxWYKUZ29QWR6g==} + engines: {node: '>= 14.0.0'} + + '@algolia/recommend@5.31.0': + resolution: {integrity: sha512-dA94TKQ9FiZ8E1BlpfAMVKC3XimhDBjNFLPR3w5eRgSXymJbbK93xr/LrhyCWHbJPxtUcJvaO+Xg0pFKP+HZvw==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-browser-xhr@5.31.0': + resolution: {integrity: sha512-akbqE63Scw3dttQatKhjiHdFXpqihCCpcAciIHpdebw3/zWfb+e/Tkf6tDv/05AGcG5BHC365dp8LIl9+NchSA==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-fetch@5.31.0': + resolution: {integrity: sha512-qYOEOCIqXvbVKNTabgKmPFltpNxB1U38hhrMEbypyOc/X9zjdxnVi/dqZ+jKsYY4X7MSQTtowLK4AR++OdMD/g==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-node-http@5.31.0': + resolution: {integrity: sha512-eq8uTVUc/E7YIOqTVfXgGQ3ZSsAWqZZHy5ntuwm6WxnvdcAyhyzRo0sncX1zWFkFpNGvJ8xyONDWq/Ef2e31Tg==} + engines: {node: '>= 14.0.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.28.0': + resolution: {integrity: sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==} + engines: {node: '>=6.9.0'} + + '@docsearch/css@3.8.2': + resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==} + + '@docsearch/js@3.8.2': + resolution: {integrity: sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==} + + '@docsearch/react@3.8.2': + resolution: {integrity: sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==} + peerDependencies: + '@types/react': '>= 16.8.0 < 19.0.0' + react: '>= 16.8.0 < 19.0.0' + react-dom: '>= 16.8.0 < 19.0.0' + search-insights: '>= 1 < 3' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + react-dom: + optional: true + search-insights: + optional: true + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@iconify-json/simple-icons@1.2.42': + resolution: {integrity: sha512-G/EED0hUV1wMNUsWaFdQYLibm6SO7rP2GZP1+CvhszB5WAFYYibD3zoWp3X96xSIWpYQFvccvE17ewpd0Q1hWQ==} + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@jridgewell/sourcemap-codec@1.5.4': + resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} + + '@rollup/rollup-android-arm-eabi@4.44.2': + resolution: {integrity: sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.44.2': + resolution: {integrity: sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.44.2': + resolution: {integrity: sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.44.2': + resolution: {integrity: sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.44.2': + resolution: {integrity: sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.44.2': + resolution: {integrity: sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.44.2': + resolution: {integrity: sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.44.2': + resolution: {integrity: sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.44.2': + resolution: {integrity: sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.44.2': + resolution: {integrity: sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.44.2': + resolution: {integrity: sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.44.2': + resolution: {integrity: sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.44.2': + resolution: {integrity: sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.44.2': + resolution: {integrity: sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.44.2': + resolution: {integrity: sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.44.2': + resolution: {integrity: sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.44.2': + resolution: {integrity: sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.44.2': + resolution: {integrity: sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.44.2': + resolution: {integrity: sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.44.2': + resolution: {integrity: sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==} + cpu: [x64] + os: [win32] + + '@shikijs/core@2.5.0': + resolution: {integrity: sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==} + + '@shikijs/engine-javascript@2.5.0': + resolution: {integrity: sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==} + + '@shikijs/engine-oniguruma@2.5.0': + resolution: {integrity: sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==} + + '@shikijs/langs@2.5.0': + resolution: {integrity: sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==} + + '@shikijs/themes@2.5.0': + resolution: {integrity: sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==} + + '@shikijs/transformers@2.5.0': + resolution: {integrity: sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==} + + '@shikijs/types@2.5.0': + resolution: {integrity: sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + + '@types/markdown-it@14.1.2': + resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/web-bluetooth@0.0.21': + resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@vitejs/plugin-vue@5.2.4': + resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + vue: ^3.2.25 + + '@vue/compiler-core@3.5.17': + resolution: {integrity: sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==} + + '@vue/compiler-dom@3.5.17': + resolution: {integrity: sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==} + + '@vue/compiler-sfc@3.5.17': + resolution: {integrity: sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==} + + '@vue/compiler-ssr@3.5.17': + resolution: {integrity: sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==} + + '@vue/devtools-api@7.7.7': + resolution: {integrity: sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==} + + '@vue/devtools-kit@7.7.7': + resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==} + + '@vue/devtools-shared@7.7.7': + resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==} + + '@vue/reactivity@3.5.17': + resolution: {integrity: sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==} + + '@vue/runtime-core@3.5.17': + resolution: {integrity: sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==} + + '@vue/runtime-dom@3.5.17': + resolution: {integrity: sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==} + + '@vue/server-renderer@3.5.17': + resolution: {integrity: sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==} + peerDependencies: + vue: 3.5.17 + + '@vue/shared@3.5.17': + resolution: {integrity: sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==} + + '@vueuse/core@12.8.2': + resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==} + + '@vueuse/integrations@12.8.2': + resolution: {integrity: sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==} + peerDependencies: + async-validator: ^4 + axios: ^1 + change-case: ^5 + drauu: ^0.4 + focus-trap: ^7 + fuse.js: ^7 + idb-keyval: ^6 + jwt-decode: ^4 + nprogress: ^0.2 + qrcode: ^1.5 + sortablejs: ^1 + universal-cookie: ^7 + peerDependenciesMeta: + async-validator: + optional: true + axios: + optional: true + change-case: + optional: true + drauu: + optional: true + focus-trap: + optional: true + fuse.js: + optional: true + idb-keyval: + optional: true + jwt-decode: + optional: true + nprogress: + optional: true + qrcode: + optional: true + sortablejs: + optional: true + universal-cookie: + optional: true + + '@vueuse/metadata@12.8.2': + resolution: {integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==} + + '@vueuse/shared@12.8.2': + resolution: {integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==} + + algoliasearch@5.31.0: + resolution: {integrity: sha512-LBpwGyNPOcprdu1OnRtgaWeKLjnDR3T+vp64WRiQEgHYACIXgU+djAvj88m3OQc+6MfWbw7rKUjXtdRMLfU7Aw==} + engines: {node: '>= 14.0.0'} + + birpc@2.4.0: + resolution: {integrity: sha512-5IdNxTyhXHv2UlgnPHQ0h+5ypVmkrYHzL8QT+DwFZ//2N/oNV8Ch+BCRmTJ3x6/z9Axo/cXYBc9eprsUVK/Jsg==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + copy-anything@3.0.5: + resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} + engines: {node: '>=12.13'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + emoji-regex-xs@1.0.0: + resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + focus-trap@7.6.5: + resolution: {integrity: sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + mark.js@8.11.1: + resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} + + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + minisearch@7.1.2: + resolution: {integrity: sha512-R1Pd9eF+MD5JYDDSPAp/q1ougKglm14uEkPMvQ/05RGmx6G9wvmLTrTI/Q5iPNJLYqNdsDQ7qTGIcNWR+FrHmA==} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + oniguruma-to-es@3.1.1: + resolution: {integrity: sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==} + + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + preact@10.26.9: + resolution: {integrity: sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA==} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.0.1: + resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rollup@4.44.2: + resolution: {integrity: sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + search-insights@2.17.3: + resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==} + + shiki@2.5.0: + resolution: {integrity: sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + speakingurl@14.0.1: + resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} + engines: {node: '>=0.10.0'} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + superjson@2.2.2: + resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==} + engines: {node: '>=16'} + + tabbable@6.2.0: + resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + + vfile-message@4.0.2: + resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite@5.4.19: + resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitepress@1.6.4: + resolution: {integrity: sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==} + hasBin: true + peerDependencies: + markdown-it-mathjax3: ^4 + postcss: ^8 + peerDependenciesMeta: + markdown-it-mathjax3: + optional: true + postcss: + optional: true + + vue@3.5.17: + resolution: {integrity: sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.31.0)(algoliasearch@5.31.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.31.0)(algoliasearch@5.31.0)(search-insights@2.17.3) + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.31.0)(algoliasearch@5.31.0) + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + - search-insights + + '@algolia/autocomplete-plugin-algolia-insights@1.17.7(@algolia/client-search@5.31.0)(algoliasearch@5.31.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.31.0)(algoliasearch@5.31.0) + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + + '@algolia/autocomplete-preset-algolia@1.17.7(@algolia/client-search@5.31.0)(algoliasearch@5.31.0)': + dependencies: + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.31.0)(algoliasearch@5.31.0) + '@algolia/client-search': 5.31.0 + algoliasearch: 5.31.0 + + '@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.31.0)(algoliasearch@5.31.0)': + dependencies: + '@algolia/client-search': 5.31.0 + algoliasearch: 5.31.0 + + '@algolia/client-abtesting@5.31.0': + dependencies: + '@algolia/client-common': 5.31.0 + '@algolia/requester-browser-xhr': 5.31.0 + '@algolia/requester-fetch': 5.31.0 + '@algolia/requester-node-http': 5.31.0 + + '@algolia/client-analytics@5.31.0': + dependencies: + '@algolia/client-common': 5.31.0 + '@algolia/requester-browser-xhr': 5.31.0 + '@algolia/requester-fetch': 5.31.0 + '@algolia/requester-node-http': 5.31.0 + + '@algolia/client-common@5.31.0': {} + + '@algolia/client-insights@5.31.0': + dependencies: + '@algolia/client-common': 5.31.0 + '@algolia/requester-browser-xhr': 5.31.0 + '@algolia/requester-fetch': 5.31.0 + '@algolia/requester-node-http': 5.31.0 + + '@algolia/client-personalization@5.31.0': + dependencies: + '@algolia/client-common': 5.31.0 + '@algolia/requester-browser-xhr': 5.31.0 + '@algolia/requester-fetch': 5.31.0 + '@algolia/requester-node-http': 5.31.0 + + '@algolia/client-query-suggestions@5.31.0': + dependencies: + '@algolia/client-common': 5.31.0 + '@algolia/requester-browser-xhr': 5.31.0 + '@algolia/requester-fetch': 5.31.0 + '@algolia/requester-node-http': 5.31.0 + + '@algolia/client-search@5.31.0': + dependencies: + '@algolia/client-common': 5.31.0 + '@algolia/requester-browser-xhr': 5.31.0 + '@algolia/requester-fetch': 5.31.0 + '@algolia/requester-node-http': 5.31.0 + + '@algolia/ingestion@1.31.0': + dependencies: + '@algolia/client-common': 5.31.0 + '@algolia/requester-browser-xhr': 5.31.0 + '@algolia/requester-fetch': 5.31.0 + '@algolia/requester-node-http': 5.31.0 + + '@algolia/monitoring@1.31.0': + dependencies: + '@algolia/client-common': 5.31.0 + '@algolia/requester-browser-xhr': 5.31.0 + '@algolia/requester-fetch': 5.31.0 + '@algolia/requester-node-http': 5.31.0 + + '@algolia/recommend@5.31.0': + dependencies: + '@algolia/client-common': 5.31.0 + '@algolia/requester-browser-xhr': 5.31.0 + '@algolia/requester-fetch': 5.31.0 + '@algolia/requester-node-http': 5.31.0 + + '@algolia/requester-browser-xhr@5.31.0': + dependencies: + '@algolia/client-common': 5.31.0 + + '@algolia/requester-fetch@5.31.0': + dependencies: + '@algolia/client-common': 5.31.0 + + '@algolia/requester-node-http@5.31.0': + dependencies: + '@algolia/client-common': 5.31.0 + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/parser@7.28.0': + dependencies: + '@babel/types': 7.28.0 + + '@babel/types@7.28.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@docsearch/css@3.8.2': {} + + '@docsearch/js@3.8.2(@algolia/client-search@5.31.0)(search-insights@2.17.3)': + dependencies: + '@docsearch/react': 3.8.2(@algolia/client-search@5.31.0)(search-insights@2.17.3) + preact: 10.26.9 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/react' + - react + - react-dom + - search-insights + + '@docsearch/react@3.8.2(@algolia/client-search@5.31.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.31.0)(algoliasearch@5.31.0)(search-insights@2.17.3) + '@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.31.0)(algoliasearch@5.31.0) + '@docsearch/css': 3.8.2 + algoliasearch: 5.31.0 + optionalDependencies: + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@iconify-json/simple-icons@1.2.42': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify/types@2.0.0': {} + + '@jridgewell/sourcemap-codec@1.5.4': {} + + '@rollup/rollup-android-arm-eabi@4.44.2': + optional: true + + '@rollup/rollup-android-arm64@4.44.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.44.2': + optional: true + + '@rollup/rollup-darwin-x64@4.44.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.44.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.44.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.44.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.44.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.44.2': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.44.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.44.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.44.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.44.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.44.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.44.2': + optional: true + + '@shikijs/core@2.5.0': + dependencies: + '@shikijs/engine-javascript': 2.5.0 + '@shikijs/engine-oniguruma': 2.5.0 + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 3.1.1 + + '@shikijs/engine-oniguruma@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + + '@shikijs/themes@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + + '@shikijs/transformers@2.5.0': + dependencies: + '@shikijs/core': 2.5.0 + '@shikijs/types': 2.5.0 + + '@shikijs/types@2.5.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@types/estree@1.0.8': {} + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/linkify-it@5.0.0': {} + + '@types/markdown-it@14.1.2': + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdurl@2.0.0': {} + + '@types/unist@3.0.3': {} + + '@types/web-bluetooth@0.0.21': {} + + '@ungap/structured-clone@1.3.0': {} + + '@vitejs/plugin-vue@5.2.4(vite@5.4.19)(vue@3.5.17)': + dependencies: + vite: 5.4.19 + vue: 3.5.17 + + '@vue/compiler-core@3.5.17': + dependencies: + '@babel/parser': 7.28.0 + '@vue/shared': 3.5.17 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.17': + dependencies: + '@vue/compiler-core': 3.5.17 + '@vue/shared': 3.5.17 + + '@vue/compiler-sfc@3.5.17': + dependencies: + '@babel/parser': 7.28.0 + '@vue/compiler-core': 3.5.17 + '@vue/compiler-dom': 3.5.17 + '@vue/compiler-ssr': 3.5.17 + '@vue/shared': 3.5.17 + estree-walker: 2.0.2 + magic-string: 0.30.17 + postcss: 8.5.6 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.17': + dependencies: + '@vue/compiler-dom': 3.5.17 + '@vue/shared': 3.5.17 + + '@vue/devtools-api@7.7.7': + dependencies: + '@vue/devtools-kit': 7.7.7 + + '@vue/devtools-kit@7.7.7': + dependencies: + '@vue/devtools-shared': 7.7.7 + birpc: 2.4.0 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 1.0.0 + speakingurl: 14.0.1 + superjson: 2.2.2 + + '@vue/devtools-shared@7.7.7': + dependencies: + rfdc: 1.4.1 + + '@vue/reactivity@3.5.17': + dependencies: + '@vue/shared': 3.5.17 + + '@vue/runtime-core@3.5.17': + dependencies: + '@vue/reactivity': 3.5.17 + '@vue/shared': 3.5.17 + + '@vue/runtime-dom@3.5.17': + dependencies: + '@vue/reactivity': 3.5.17 + '@vue/runtime-core': 3.5.17 + '@vue/shared': 3.5.17 + csstype: 3.1.3 + + '@vue/server-renderer@3.5.17(vue@3.5.17)': + dependencies: + '@vue/compiler-ssr': 3.5.17 + '@vue/shared': 3.5.17 + vue: 3.5.17 + + '@vue/shared@3.5.17': {} + + '@vueuse/core@12.8.2': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 12.8.2 + '@vueuse/shared': 12.8.2 + vue: 3.5.17 + transitivePeerDependencies: + - typescript + + '@vueuse/integrations@12.8.2(focus-trap@7.6.5)': + dependencies: + '@vueuse/core': 12.8.2 + '@vueuse/shared': 12.8.2 + vue: 3.5.17 + optionalDependencies: + focus-trap: 7.6.5 + transitivePeerDependencies: + - typescript + + '@vueuse/metadata@12.8.2': {} + + '@vueuse/shared@12.8.2': + dependencies: + vue: 3.5.17 + transitivePeerDependencies: + - typescript + + algoliasearch@5.31.0: + dependencies: + '@algolia/client-abtesting': 5.31.0 + '@algolia/client-analytics': 5.31.0 + '@algolia/client-common': 5.31.0 + '@algolia/client-insights': 5.31.0 + '@algolia/client-personalization': 5.31.0 + '@algolia/client-query-suggestions': 5.31.0 + '@algolia/client-search': 5.31.0 + '@algolia/ingestion': 1.31.0 + '@algolia/monitoring': 1.31.0 + '@algolia/recommend': 5.31.0 + '@algolia/requester-browser-xhr': 5.31.0 + '@algolia/requester-fetch': 5.31.0 + '@algolia/requester-node-http': 5.31.0 + + birpc@2.4.0: {} + + ccount@2.0.1: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + comma-separated-tokens@2.0.3: {} + + copy-anything@3.0.5: + dependencies: + is-what: 4.1.16 + + csstype@3.1.3: {} + + dequal@2.0.3: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + emoji-regex-xs@1.0.0: {} + + entities@4.5.0: {} + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + estree-walker@2.0.2: {} + + focus-trap@7.6.5: + dependencies: + tabbable: 6.2.0 + + fsevents@2.3.3: + optional: true + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hookable@5.5.3: {} + + html-void-elements@3.0.0: {} + + is-what@4.1.16: {} + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.4 + + mark.js@8.11.1: {} + + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-encode@2.0.1: {} + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + minisearch@7.1.2: {} + + mitt@3.0.1: {} + + nanoid@3.3.11: {} + + oniguruma-to-es@3.1.1: + dependencies: + emoji-regex-xs: 1.0.0 + regex: 6.0.1 + regex-recursion: 6.0.2 + + perfect-debounce@1.0.0: {} + + picocolors@1.1.1: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + preact@10.26.9: {} + + property-information@7.1.0: {} + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.0.1: + dependencies: + regex-utilities: 2.3.0 + + rfdc@1.4.1: {} + + rollup@4.44.2: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.44.2 + '@rollup/rollup-android-arm64': 4.44.2 + '@rollup/rollup-darwin-arm64': 4.44.2 + '@rollup/rollup-darwin-x64': 4.44.2 + '@rollup/rollup-freebsd-arm64': 4.44.2 + '@rollup/rollup-freebsd-x64': 4.44.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.44.2 + '@rollup/rollup-linux-arm-musleabihf': 4.44.2 + '@rollup/rollup-linux-arm64-gnu': 4.44.2 + '@rollup/rollup-linux-arm64-musl': 4.44.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.44.2 + '@rollup/rollup-linux-powerpc64le-gnu': 4.44.2 + '@rollup/rollup-linux-riscv64-gnu': 4.44.2 + '@rollup/rollup-linux-riscv64-musl': 4.44.2 + '@rollup/rollup-linux-s390x-gnu': 4.44.2 + '@rollup/rollup-linux-x64-gnu': 4.44.2 + '@rollup/rollup-linux-x64-musl': 4.44.2 + '@rollup/rollup-win32-arm64-msvc': 4.44.2 + '@rollup/rollup-win32-ia32-msvc': 4.44.2 + '@rollup/rollup-win32-x64-msvc': 4.44.2 + fsevents: 2.3.3 + + search-insights@2.17.3: {} + + shiki@2.5.0: + dependencies: + '@shikijs/core': 2.5.0 + '@shikijs/engine-javascript': 2.5.0 + '@shikijs/engine-oniguruma': 2.5.0 + '@shikijs/langs': 2.5.0 + '@shikijs/themes': 2.5.0 + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + source-map-js@1.2.1: {} + + space-separated-tokens@2.0.2: {} + + speakingurl@14.0.1: {} + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + superjson@2.2.2: + dependencies: + copy-anything: 3.0.5 + + tabbable@6.2.0: {} + + trim-lines@3.0.1: {} + + unist-util-is@6.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + vfile-message@4.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.2 + + vite@5.4.19: + dependencies: + esbuild: 0.21.5 + postcss: 8.5.6 + rollup: 4.44.2 + optionalDependencies: + fsevents: 2.3.3 + + vitepress@1.6.4(@algolia/client-search@5.31.0)(postcss@8.5.6)(search-insights@2.17.3): + dependencies: + '@docsearch/css': 3.8.2 + '@docsearch/js': 3.8.2(@algolia/client-search@5.31.0)(search-insights@2.17.3) + '@iconify-json/simple-icons': 1.2.42 + '@shikijs/core': 2.5.0 + '@shikijs/transformers': 2.5.0 + '@shikijs/types': 2.5.0 + '@types/markdown-it': 14.1.2 + '@vitejs/plugin-vue': 5.2.4(vite@5.4.19)(vue@3.5.17) + '@vue/devtools-api': 7.7.7 + '@vue/shared': 3.5.17 + '@vueuse/core': 12.8.2 + '@vueuse/integrations': 12.8.2(focus-trap@7.6.5) + focus-trap: 7.6.5 + mark.js: 8.11.1 + minisearch: 7.1.2 + shiki: 2.5.0 + vite: 5.4.19 + vue: 3.5.17 + optionalDependencies: + postcss: 8.5.6 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/node' + - '@types/react' + - async-validator + - axios + - change-case + - drauu + - fuse.js + - idb-keyval + - jwt-decode + - less + - lightningcss + - nprogress + - qrcode + - react + - react-dom + - sass + - sass-embedded + - search-insights + - sortablejs + - stylus + - sugarss + - terser + - typescript + - universal-cookie + + vue@3.5.17: + dependencies: + '@vue/compiler-dom': 3.5.17 + '@vue/compiler-sfc': 3.5.17 + '@vue/runtime-dom': 3.5.17 + '@vue/server-renderer': 3.5.17(vue@3.5.17) + '@vue/shared': 3.5.17 + + zwitch@2.0.4: {} diff --git a/docs/public/404-seio.png b/docs/public/404-seio.png new file mode 100644 index 000000000..7c9774fc7 Binary files /dev/null and b/docs/public/404-seio.png differ diff --git a/docs/public/logo.png b/docs/public/logo.png new file mode 100644 index 000000000..88d34417d Binary files /dev/null and b/docs/public/logo.png differ diff --git a/docs/public/logo_prod.png b/docs/public/logo_prod.png new file mode 100644 index 000000000..c1ec6bb6e Binary files /dev/null and b/docs/public/logo_prod.png differ diff --git a/docs/public/openapi.json b/docs/public/openapi.json new file mode 100644 index 000000000..2fadecbc0 --- /dev/null +++ b/docs/public/openapi.json @@ -0,0 +1,685 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "AstrBot Open API", + "version": "1.0.0", + "description": "Developer HTTP APIs for AstrBot. Use API Key authentication for /api/v1/* endpoints." + }, + "servers": [ + { + "url": "http://localhost:6185" + } + ], + "tags": [ + { + "name": "Open API", + "description": "Developer APIs authenticated by API Key" + } + ], + "paths": { + "/api/v1/im/bots": { + "get": { + "tags": [ + "Open API" + ], + "summary": "List bot IDs", + "description": "Returns configured bot/platform IDs.", + "security": [ + { + "ApiKeyHeader": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseBotList" + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + } + } + } + }, + "/api/v1/file": { + "post": { + "tags": [ + "Open API" + ], + "summary": "Upload attachment file", + "description": "Upload a file and get attachment_id for later use in chat/message APIs.", + "security": [ + { + "ApiKeyHeader": [] + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "type": "object", + "required": [ + "file" + ], + "properties": { + "file": { + "type": "string", + "format": "binary" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseUpload" + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + } + } + } + }, + "/api/v1/chat": { + "post": { + "tags": [ + "Open API" + ], + "summary": "Send chat message (SSE)", + "description": "Send message to AstrBot chat pipeline and receive streaming SSE response. Reuses /api/chat/send behavior. If session_id/conversation_id is omitted, server will create a new UUID session_id.", + "security": [ + { + "ApiKeyHeader": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatSendRequest" + }, + "examples": { + "plain": { + "value": { + "message": "Hello", + "username": "alice", + "session_id": "my_session_001", + "enable_streaming": true + } + }, + "multipartMessage": { + "value": { + "message": [ + { + "type": "plain", + "text": "Please analyze this file" + }, + { + "type": "file", + "attachment_id": "9a2f8c72-e7af-4c0e-b352-111111111111" + } + ], + "username": "alice", + "session_id": "my_session_001", + "selected_provider": "openai_chat_completion", + "selected_model": "gpt-4.1-mini", + "enable_streaming": true + } + }, + "withConfig": { + "value": { + "message": "Use a specific config for this session", + "username": "alice", + "session_id": "my_session_001", + "config_id": "default", + "enable_streaming": true + } + }, + "autoSessionWithUsername": { + "value": { + "message": "hello", + "username": "alice", + "enable_streaming": true + } + } + } + } + } + }, + "responses": { + "200": { + "description": "SSE stream", + "content": { + "text/event-stream": { + "schema": { + "type": "string" + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + } + } + } + }, + "/api/v1/chat/sessions": { + "get": { + "tags": [ + "Open API" + ], + "summary": "List chat sessions with pagination", + "description": "List chat sessions for the specified username.", + "security": [ + { + "ApiKeyHeader": [] + } + ], + "parameters": [ + { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "default": 1, + "minimum": 1 + } + }, + { + "name": "page_size", + "in": "query", + "schema": { + "type": "integer", + "default": 20, + "minimum": 1, + "maximum": 100 + } + }, + { + "name": "platform_id", + "in": "query", + "schema": { + "type": "string" + }, + "description": "Optional platform filter" + }, + { + "name": "username", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "description": "Target username." + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseChatSessions" + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + } + } + } + }, + "/api/v1/im/message": { + "post": { + "tags": [ + "Open API" + ], + "summary": "Send proactive message to a platform bot", + "description": "Send message directly to platform bot by umo + message chain payload.", + "security": [ + { + "ApiKeyHeader": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SendMessageRequest" + }, + "examples": { + "plain": { + "value": { + "umo": "webchat:FriendMessage:openapi_probe", + "message": "ping from api key" + } + }, + "chain": { + "value": { + "umo": "webchat:FriendMessage:openapi_probe", + "message": [ + { + "type": "plain", + "text": "hello" + }, + { + "type": "image", + "attachment_id": "9a2f8c72-e7af-4c0e-b352-111111111111" + } + ] + } + } + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseEmpty" + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + } + } + } + }, + "/api/v1/configs": { + "get": { + "tags": [ + "Open API" + ], + "summary": "List available chat config files", + "description": "Returns all available AstrBot config files that can be selected by Chat API using config_id/config_name.", + "security": [ + { + "ApiKeyHeader": [] + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponseChatConfigList" + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + } + } + } + } + }, + "components": { + "securitySchemes": { + "ApiKeyHeader": { + "type": "apiKey", + "in": "header", + "name": "X-API-Key", + "description": "Open API key. Authorization: Bearer is also accepted." + } + }, + "responses": { + "Unauthorized": { + "description": "Unauthorized" + }, + "Forbidden": { + "description": "Forbidden" + } + }, + "schemas": { + "ApiResponseEmpty": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "ok" + }, + "message": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": true + } + } + }, + "ApiResponseBotList": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "ok" + }, + "message": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "properties": { + "bot_ids": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "ApiResponseUpload": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "ok" + }, + "message": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "properties": { + "attachment_id": { + "type": "string" + }, + "filename": { + "type": "string" + }, + "type": { + "type": "string" + } + } + } + } + }, + "ApiResponseChatSessions": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "ok" + }, + "message": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "properties": { + "sessions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChatSessionItem" + } + }, + "page": { + "type": "integer" + }, + "page_size": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + } + } + }, + "ChatSessionItem": { + "type": "object", + "properties": { + "session_id": { + "type": "string" + }, + "platform_id": { + "type": "string" + }, + "creator": { + "type": "string" + }, + "display_name": { + "type": [ + "string", + "null" + ] + }, + "is_group": { + "type": "integer" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + } + }, + "MessagePart": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "plain", + "reply", + "image", + "record", + "file", + "video" + ] + }, + "text": { + "type": "string" + }, + "message_id": { + "type": [ + "string", + "integer" + ] + }, + "selected_text": { + "type": "string" + }, + "attachment_id": { + "type": "string" + }, + "filename": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "required": [ + "type" + ] + }, + "ChatSendRequest": { + "type": "object", + "required": [ + "message", + "username" + ], + "properties": { + "message": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/MessagePart" + } + } + ] + }, + "session_id": { + "type": "string", + "description": "Optional chat session ID. If omitted (and conversation_id is also omitted), server creates a UUID automatically." + }, + "conversation_id": { + "type": "string", + "description": "Alias of session_id." + }, + "username": { + "type": "string", + "description": "Target username." + }, + "selected_provider": { + "type": "string" + }, + "selected_model": { + "type": "string" + }, + "enable_streaming": { + "type": "boolean", + "default": true + }, + "config_id": { + "type": "string", + "description": "Optional AstrBot config file ID. If provided, the chat session will use this config file. Use \"default\" to reset to default config." + }, + "config_name": { + "type": "string", + "description": "Optional AstrBot config file name. Used only when config_id is not provided." + } + } + }, + "SendMessageRequest": { + "type": "object", + "required": [ + "umo", + "message" + ], + "properties": { + "umo": { + "type": "string", + "description": "Unified message origin. Format: platform:message_type:session_id" + }, + "message": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "$ref": "#/components/schemas/MessagePart" + } + } + ] + } + } + }, + "ChatConfigFile": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "is_default": { + "type": "boolean" + } + }, + "required": [ + "id", + "name", + "path", + "is_default" + ] + }, + "ApiResponseChatConfigList": { + "type": "object", + "properties": { + "status": { + "type": "string", + "example": "ok" + }, + "message": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "properties": { + "configs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChatConfigFile" + } + } + } + } + } + } + } + } +} diff --git a/docs/public/scalar.html b/docs/public/scalar.html new file mode 100644 index 000000000..607421d71 --- /dev/null +++ b/docs/public/scalar.html @@ -0,0 +1,25 @@ + + + + + + AstrBot OpenAPI - Scalar + + + +
+ + + + diff --git a/docs/scripts/sync_docs_to_wiki.py b/docs/scripts/sync_docs_to_wiki.py new file mode 100644 index 000000000..f717f1f96 --- /dev/null +++ b/docs/scripts/sync_docs_to_wiki.py @@ -0,0 +1,644 @@ +from __future__ import annotations + +import argparse +import posixpath +import re +from dataclasses import dataclass +from pathlib import Path, PurePosixPath + +TITLE_RE = re.compile(r"^#\s+(.+)$", re.MULTILINE) +FENCED_BLOCK_RE = re.compile( + r"(^```.*?$.*?^```$|^~~~.*?$.*?^~~~$)", + re.MULTILINE | re.DOTALL, +) +INLINE_CODE_RE = re.compile(r"(`[^`]*`)") +MANIFEST_NAME = ".astrbot-wiki-sync-manifest" +SOURCE_ALIASES = { + "zh/config/providers/start.md": "zh/providers/start.md", + "en/config/providers/start.md": "en/providers/start.md", +} +LANG_CONFIG = { + "zh": { + "index_title": "# AstrBot 中文文档", + "index_intro": "该页面由 `AstrBot-docs` 自动同步到 GitHub Wiki。", + "index_links": [ + ("关于 AstrBot", "zh-what-is-astrbot"), + ("社区", "zh-community"), + ("常见问题", "zh-faq"), + ], + "home_intro": "该 Wiki 由 `AstrBot-docs` 自动同步生成。", + "home_links": [ + ("中文文档入口", "zh-index"), + ("English Docs", "Home-en"), + ], + "sidebar_language_label": "Chinese", + "sidebar_home_label": "首页", + "sidebar_home_target": "Home", + "sidebar_docs_entry_label": "文档入口", + }, + "en": { + "index_title": "# AstrBot English Documentation", + "index_intro": "This page is synchronized automatically from `AstrBot-docs` to the GitHub wiki.", + "index_links": [ + ("What is AstrBot", "en-what-is-astrbot"), + ("Community", "en-community"), + ("FAQ", "en-faq"), + ], + "home_intro": "This wiki is synchronized automatically from `AstrBot-docs`.", + "home_links": [ + ("English docs entry", "en-index"), + ("中文文档入口", "Home"), + ], + "sidebar_language_label": "English", + "sidebar_home_label": "Home", + "sidebar_home_target": "Home-en", + "sidebar_docs_entry_label": "Docs Entry", + }, +} + + +@dataclass +class PageInfo: + source_path: str + page_name: str + title: str + content: str + language: str + group: str + is_index: bool + + +@dataclass +class ResolutionResult: + resolved_path: str | None + ambiguous_matches: tuple[str, ...] = () + + +@dataclass +class MarkdownLink: + start: int + end: int + prefix: str + target: str + suffix: str + + +@dataclass +class Segment: + kind: str + text: str + + +def repo_root() -> Path: + return Path(__file__).resolve().parents[1] + + +def discover_source_pages(source_root: str) -> tuple[str, ...]: + root = Path(source_root) + pages = [] + for language in ("zh", "en"): + language_root = root / language + if not language_root.exists(): + continue + for path in language_root.rglob("*.md"): + pages.append(path.relative_to(root).as_posix()) + return tuple(sorted(pages)) + + +def find_label_end(content: str, label_start: int) -> int: + index = label_start + 1 + while index < len(content): + close = content.find("]", index) + if close == -1: + return -1 + if close > label_start and content[close - 1] == "\\": + index = close + 1 + continue + lookahead = close + 1 + while lookahead < len(content) and content[lookahead].isspace(): + lookahead += 1 + if lookahead < len(content) and content[lookahead] == "(": + return close + index = close + 1 + return -1 + + +def find_target_end(content: str, target_start: int) -> int: + depth = 0 + index = target_start + while index < len(content): + character = content[index] + if character == "\\": + index += 2 + continue + if character == "(": + depth += 1 + elif character == ")": + if depth == 0: + return index + depth -= 1 + index += 1 + return -1 + + +def iter_markdown_links(content: str): + """Yield inline Markdown links only. + + This scanner intentionally handles inline `[]()` links used in the docs tree. + It does not parse reference-style links or arbitrary HTML. + """ + + index = 0 + while index < len(content): + label_start = content.find("[", index) + if label_start == -1: + break + + link_start = ( + label_start - 1 + if label_start > 0 and content[label_start - 1] == "!" + else label_start + ) + label_end = find_label_end(content, label_start) + if label_end == -1: + index = label_start + 1 + continue + + target_start = label_end + 1 + while target_start < len(content) and content[target_start].isspace(): + target_start += 1 + if target_start >= len(content) or content[target_start] != "(": + index = label_end + 1 + continue + target_start += 1 + target_end = find_target_end(content, target_start) + if target_end == -1: + index = label_end + 1 + continue + + yield MarkdownLink( + start=link_start, + end=target_end + 1, + prefix=content[link_start:target_start], + target=content[target_start:target_end], + suffix=")", + ) + index = target_end + 1 + + +def split_anchor(target: str) -> tuple[str, str]: + if "#" not in target: + return target, "" + base, anchor = target.split("#", 1) + return base, f"#{anchor}" + + +def prepare_candidate_path(path: PurePosixPath) -> PurePosixPath: + if not path.suffix: + path = path.with_suffix(".md") + + normalized = PurePosixPath(posixpath.normpath(path.as_posix())) + normalized_text = normalized.as_posix() + aliased = SOURCE_ALIASES.get(normalized_text, normalized_text) + return PurePosixPath(aliased) + + +def language_for_source(source_path: str) -> str: + return PurePosixPath(source_path).parts[0] + + +def parse_doc_target(target: str) -> tuple[str, str] | None: + if target.startswith(("http://", "https://", "mailto:", "#")): + return None + + base_target, anchor = split_anchor(target) + if not base_target: + return None + + suffix = PurePosixPath(base_target).suffix.lower() + if suffix and suffix != ".md": + return None + + return base_target, anchor + + +def find_existing_source_path( + candidate: PurePosixPath, + source_root: Path, + source_pages: tuple[str, ...], +) -> ResolutionResult: + candidate_text = candidate.as_posix() + if (source_root / candidate_text).exists(): + return ResolutionResult(resolved_path=candidate_text) + + language = candidate.parts[0] if candidate.parts else "" + suffix = ( + PurePosixPath(*candidate.parts[1:]).as_posix() + if len(candidate.parts) > 1 + else "" + ) + if not suffix: + return ResolutionResult(resolved_path=None) + + prefix = f"{language}/" + full_suffix = f"{language}/{suffix}" + matches = [ + page + for page in source_pages + if page.startswith(prefix) + and (page == full_suffix or page.endswith(f"/{suffix}")) + ] + if len(matches) == 1: + return ResolutionResult(resolved_path=matches[0]) + if len(matches) > 1: + return ResolutionResult( + resolved_path=None, + ambiguous_matches=tuple(sorted(matches)), + ) + return ResolutionResult(resolved_path=None) + + +def resolve_link_path( + base_target: str, + source_path: str, + source_root: Path, + source_pages: tuple[str, ...], +) -> ResolutionResult: + source_language = language_for_source(source_path) + + if base_target.startswith("/"): + target = base_target.lstrip("/") + if not target: + candidate = PurePosixPath(source_language) / "index.md" + elif target in {"en", "en/"}: + candidate = PurePosixPath("en") / "index.md" + elif target in {"zh", "zh/"}: + candidate = PurePosixPath("zh") / "index.md" + elif target.startswith(("en/", "zh/")): + candidate = PurePosixPath(target) + else: + language_root = source_language if source_language == "en" else "zh" + candidate = PurePosixPath(language_root) / target + else: + candidate = PurePosixPath(source_path).parent / base_target + + candidate = prepare_candidate_path(candidate) + return find_existing_source_path(candidate, source_root, source_pages) + + +class LinkResolver: + def __init__(self, source_root: Path): + self.source_root = Path(source_root) + self.source_pages = discover_source_pages(str(self.source_root)) + + def resolve_base_target( + self, base_target: str, source_path: str + ) -> ResolutionResult: + return resolve_link_path( + base_target=base_target, + source_path=source_path, + source_root=self.source_root, + source_pages=self.source_pages, + ) + + def resolve_markdown_target( + self, target: str, source_path: str + ) -> tuple[str | None, str]: + parsed_target = parse_doc_target(target) + if parsed_target is None: + return None, "" + + base_target, anchor = parsed_target + result = self.resolve_base_target(base_target, source_path) + return result.resolved_path, anchor + + +def rewrite_link_target(target: str, source_path: str, resolver: LinkResolver) -> str: + resolved, anchor = resolver.resolve_markdown_target(target, source_path) + if resolved is None: + return target + + return f"{page_name_for_source(resolved)}{anchor}" + + +def rewrite_links_in_segment( + segment: str, + source_path: str, + resolver: LinkResolver, +) -> str: + links = list(iter_markdown_links(segment)) + if not links: + return segment + + result: list[str] = [] + previous_end = 0 + for link in links: + result.append(segment[previous_end : link.start]) + result.append( + f"{link.prefix}{rewrite_link_target(link.target, source_path, resolver)}{link.suffix}", + ) + previous_end = link.end + result.append(segment[previous_end:]) + return "".join(result) + + +def iter_segments(content: str): + last_end = 0 + for fenced in FENCED_BLOCK_RE.finditer(content): + before = content[last_end : fenced.start()] + if before: + last_inline_end = 0 + for inline in INLINE_CODE_RE.finditer(before): + if inline.start() > last_inline_end: + yield Segment("text", before[last_inline_end : inline.start()]) + yield Segment("inline_code", inline.group(0)) + last_inline_end = inline.end() + if last_inline_end < len(before): + yield Segment("text", before[last_inline_end:]) + + yield Segment("code_block", fenced.group(0)) + last_end = fenced.end() + + tail = content[last_end:] + if not tail: + return + + last_inline_end = 0 + for inline in INLINE_CODE_RE.finditer(tail): + if inline.start() > last_inline_end: + yield Segment("text", tail[last_inline_end : inline.start()]) + yield Segment("inline_code", inline.group(0)) + last_inline_end = inline.end() + if last_inline_end < len(tail): + yield Segment("text", tail[last_inline_end:]) + + +def rewrite_links( + content: str, + source_path: str, + resolver: LinkResolver, +) -> str: + output: list[str] = [] + for segment in iter_segments(content): + if segment.kind == "text": + output.append( + rewrite_links_in_segment( + segment.text, + source_path=source_path, + resolver=resolver, + ) + ) + continue + + output.append(segment.text) + + return "".join(output) + + +def find_unresolved_doc_links(source_root: Path) -> list[str]: + unresolved: list[str] = [] + root = Path(source_root) + resolver = LinkResolver(root) + + for source_path in resolver.source_pages: + content = (root / source_path).read_text(encoding="utf-8") + for link in iter_markdown_links(content): + resolved_path, _ = resolver.resolve_markdown_target( + link.target, source_path + ) + if resolved_path is not None: + continue + parsed_target = parse_doc_target(link.target) + if parsed_target is None: + continue + base_target, _ = parsed_target + resolution = resolver.resolve_base_target(base_target, source_path) + if resolution.ambiguous_matches: + unresolved.append( + f"{source_path} -> {link.target} (ambiguous: {', '.join(resolution.ambiguous_matches)})", + ) + continue + unresolved.append(f"{source_path} -> {link.target}") + + return unresolved + + +def check_unresolved_doc_links(source_root: Path) -> None: + unresolved = find_unresolved_doc_links(source_root) + if not unresolved: + return + + issues = "\n".join(f"- {item}" for item in unresolved) + raise ValueError(f"Unresolved internal doc links found:\n{issues}") + + +def page_name_for_source(source_path: str) -> str: + if not source_path.endswith(".md"): + raise ValueError(f"Unsupported source path: {source_path}") + return source_path[:-3].replace("/", "-") + + +def strip_frontmatter(content: str) -> str: + if not content.startswith("---\n"): + return content + + closing = content.find("\n---\n", 4) + if closing == -1: + return content + + return content[closing + 5 :].lstrip("\n") + + +def normalize_content(content: str) -> str: + stripped = content.rstrip() + if not stripped: + return "" + return f"{stripped}\n" + + +def default_title_for_source(source_path: str) -> str: + stem = PurePosixPath(source_path).stem + return stem.replace("-", " ") + + +def extract_title(content: str, source_path: str) -> str: + match = TITLE_RE.search(content) + if match: + return match.group(1).strip() + return default_title_for_source(source_path) + + +def build_language_index(language: str, page_names: set[str]) -> str: + config = LANG_CONFIG[language] + lines = [config["index_title"], "", config["index_intro"], ""] + + for label, page_name in config["index_links"]: + if page_name in page_names: + lines.append(f"- [{label}]({page_name})") + + return normalize_content("\n".join(lines)) + + +def build_home_page(language: str) -> str: + config = LANG_CONFIG[language] + lines = ["# AstrBot Wiki", "", config["home_intro"], ""] + for label, target in config["home_links"]: + lines.append(f"- [{label}]({target})") + return normalize_content("\n".join(lines)) + + +def build_sidebar(page_infos: list[PageInfo]) -> str: + lines: list[str] = [] + + for language in ("zh", "en"): + config = LANG_CONFIG[language] + infos = [ + info + for info in page_infos + if info.language == language and not info.is_index + ] + infos.sort(key=lambda info: info.source_path) + + lines.append(f"### {config['sidebar_language_label']}") + lines.append("") + lines.append( + f"- [{config['sidebar_home_label']}]({config['sidebar_home_target']})", + ) + lines.append( + f"- [{config['sidebar_docs_entry_label']}]({language}-index)", + ) + + grouped: dict[str, list[PageInfo]] = {} + for info in infos: + grouped.setdefault(info.group, []).append(info) + + for group_name in sorted(grouped): + lines.append(f"- {group_name}") + for info in grouped[group_name]: + lines.append(f" - [{info.title}]({info.page_name})") + + lines.append("") + + return normalize_content("\n".join(lines)) + + +def build_page_info( + source_root: Path, source_path: str, resolver: LinkResolver +) -> PageInfo: + source_file = source_root / source_path + content = source_file.read_text(encoding="utf-8") + content = strip_frontmatter(content) + content = rewrite_links(content, source_path=source_path, resolver=resolver) + content = normalize_content(content) + + relative = PurePosixPath(source_path) + parts = relative.parts + group = "Top Level" if len(parts) <= 2 else parts[1].replace("-", " ") + + return PageInfo( + source_path=source_path, + page_name=page_name_for_source(source_path), + title=extract_title(content, source_path), + content=content, + language=language_for_source(source_path), + group=group, + is_index=relative.name == "index.md", + ) + + +def read_manifest(wiki_root: Path) -> set[str]: + manifest_path = wiki_root / MANIFEST_NAME + if not manifest_path.exists(): + return set() + return { + line.strip() + for line in manifest_path.read_text(encoding="utf-8").splitlines() + if line.strip() + } + + +def write_manifest(wiki_root: Path, file_names: set[str]) -> None: + manifest_path = wiki_root / MANIFEST_NAME + content = "\n".join(sorted(file_names)) + if content: + content = f"{content}\n" + manifest_path.write_text(content, encoding="utf-8") + + +def write_file(path: Path, content: str) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(content, encoding="utf-8") + + +def sync_docs_to_wiki(source_root: Path, wiki_root: Path) -> None: + source_root = Path(source_root) + wiki_root = Path(wiki_root) + wiki_root.mkdir(parents=True, exist_ok=True) + resolver = LinkResolver(source_root) + + page_infos = [ + build_page_info(source_root, source_path, resolver) + for source_path in resolver.source_pages + ] + page_names = {info.page_name for info in page_infos} + + for info in page_infos: + if info.is_index and not info.content.strip(): + generated = build_language_index(info.language, page_names) + info.content = generated + info.title = extract_title(generated, info.source_path) + + desired_files = {f"{info.page_name}.md": info.content for info in page_infos} + desired_files["Home.md"] = build_home_page("zh") + desired_files["Home-en.md"] = build_home_page("en") + desired_files["_Sidebar.md"] = build_sidebar(page_infos) + + previously_managed = read_manifest(wiki_root) + for existing_name in previously_managed - set(desired_files): + existing_path = wiki_root / existing_name + if existing_path.exists(): + existing_path.unlink() + + for file_name, content in desired_files.items(): + write_file(wiki_root / file_name, content) + + managed_files = set(desired_files) + write_manifest(wiki_root, managed_files) + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Sync AstrBot docs content to GitHub wiki pages." + ) + parser.add_argument( + "--source-root", + default=str(repo_root()), + help="Path to the AstrBot-docs repository root.", + ) + parser.add_argument( + "--wiki-root", + help="Path to the checked out wiki repository.", + ) + parser.add_argument( + "--check-links-only", + action="store_true", + help="Validate internal doc links without writing wiki files.", + ) + args = parser.parse_args() + + if not args.check_links_only and not args.wiki_root: + parser.error("--wiki-root is required unless --check-links-only is set") + + check_unresolved_doc_links(Path(args.source_root)) + + if args.check_links_only: + return 0 + + sync_docs_to_wiki( + source_root=Path(args.source_root), wiki_root=Path(args.wiki_root) + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/docs/scripts/upload-doc-images-to-r2.sh b/docs/scripts/upload-doc-images-to-r2.sh new file mode 100755 index 000000000..e7a6a8d28 --- /dev/null +++ b/docs/scripts/upload-doc-images-to-r2.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +exec python3 "$SCRIPT_DIR/upload_doc_images_to_r2.py" "$@" diff --git a/docs/scripts/upload_doc_images_to_r2.py b/docs/scripts/upload_doc_images_to_r2.py new file mode 100755 index 000000000..7db614dc4 --- /dev/null +++ b/docs/scripts/upload_doc_images_to_r2.py @@ -0,0 +1,344 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import re +import shutil +import subprocess +import sys +import tempfile +from collections.abc import Iterable, Sequence +from pathlib import Path +from urllib.parse import quote + +IMAGE_EXTS = { + ".png", + ".jpg", + ".jpeg", + ".gif", + ".webp", + ".svg", + ".avif", + ".bmp", + ".ico", + ".tif", + ".tiff", +} + +MD_IMAGE_RE = re.compile(r"!\[[^\]]*\]\(([^)]+)\)") +HTML_IMG_RE = re.compile( + r"]*\bsrc\s*=\s*([\"'])([^\"']+)\1[^>]*>", re.IGNORECASE +) + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Upload all locally referenced images from Markdown docs to Cloudflare R2 using rclone." + ) + parser.add_argument("--remote", required=True, help="rclone remote name, e.g. r2") + parser.add_argument("--bucket", default="", help="bucket name in remote path") + parser.add_argument( + "--prefix", + default="docs-images", + help="destination prefix inside bucket/remote (default: docs-images)", + ) + parser.add_argument( + "--docs-root", + default=".", + help="docs root to scan for .md files (default: current directory)", + ) + parser.add_argument( + "--dry-run", action="store_true", help="preview uploads without sending files" + ) + parser.add_argument( + "--list-only", action="store_true", help="only print matched image files" + ) + parser.add_argument( + "--rewrite-markdown", + action="store_true", + help="rewrite local image links in markdown/html to public URL after upload", + ) + parser.add_argument( + "--public-base-url", + default="", + help="public URL base used for replacement, e.g. https://cdn.example.com/docs", + ) + parser.add_argument( + "--backup-ext", + default=".bak", + help="backup extension used when rewriting markdown (default: .bak)", + ) + return parser.parse_args() + + +def is_local_ref(ref: str) -> bool: + lower = ref.lower() + return not ( + lower.startswith("http://") + or lower.startswith("https://") + or lower.startswith("//") + or lower.startswith("data:") + or lower.startswith("mailto:") + ) + + +def parse_md_ref(raw: str) -> str: + ref = raw.strip() + if ref.startswith("<") and ">" in ref: + ref = ref[1 : ref.find(">")] + else: + ref = re.split(r"\s+", ref, maxsplit=1)[0] + ref = ref.split("#", 1)[0].split("?", 1)[0] + return ref.strip() + + +def clean_ref(raw: str) -> str: + ref = raw.strip().strip("<>") + ref = ref.split("#", 1)[0].split("?", 1)[0] + return ref.strip() + + +def resolve_local_ref(md_file: Path, ref: str, root: Path) -> Path | None: + if not ref: + return None + if ref.startswith("/"): + candidate = root / ref.lstrip("/") + else: + candidate = (md_file.parent / ref).resolve() + + try: + resolved = candidate.resolve() + except FileNotFoundError: + return None + + if not resolved.is_file(): + return None + + try: + resolved.relative_to(root) + except ValueError: + return None + + if resolved.suffix.lower() not in IMAGE_EXTS: + return None + + return resolved + + +def find_markdown_files(root: Path) -> list[Path]: + files: list[Path] = [] + for path in root.rglob("*.md"): + if "node_modules" in path.parts: + continue + files.append(path) + return sorted(files) + + +def collect_images( + root: Path, md_files: Sequence[Path] +) -> tuple[set[Path], list[tuple[Path, str]]]: + images: set[Path] = set() + missing: list[tuple[Path, str]] = [] + + for md_file in md_files: + text = md_file.read_text(encoding="utf-8") + + for m in MD_IMAGE_RE.finditer(text): + ref = parse_md_ref(m.group(1)) + if not ref or not is_local_ref(ref): + continue + resolved = resolve_local_ref(md_file, ref, root) + if resolved: + images.add(resolved) + else: + missing.append((md_file, ref)) + + for m in HTML_IMG_RE.finditer(text): + ref = clean_ref(m.group(2)) + if not ref or not is_local_ref(ref): + continue + resolved = resolve_local_ref(md_file, ref, root) + if resolved: + images.add(resolved) + else: + missing.append((md_file, ref)) + + return images, missing + + +def build_target(remote: str, bucket: str, prefix: str) -> str: + target = f"{remote}:" + if bucket: + target = f"{remote}:{bucket}" + + p = prefix.strip("/") + if p: + target = f"{target}/{p}" + + return target + + +def rel_object_path(root: Path, image_path: Path, prefix: str) -> str: + rel = image_path.relative_to(root).as_posix() + p = prefix.strip("/") + return f"{p}/{rel}" if p else rel + + +def build_public_url(base: str, object_path: str) -> str: + base = base.rstrip("/") + encoded_path = quote(object_path, safe="/-._~") + return f"{base}/{encoded_path}" + + +def run_rclone_upload( + root: Path, target: str, rel_files: Iterable[str], dry_run: bool +) -> None: + if shutil.which("rclone") is None: + raise RuntimeError("rclone not found in PATH") + + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", delete=False) as tmp: + tmp_path = Path(tmp.name) + for rel in rel_files: + tmp.write(f"{rel}\n") + + try: + cmd = [ + "rclone", + "copy", + str(root), + target, + "--files-from", + str(tmp_path), + "--create-empty-src-dirs", + ] + if dry_run: + cmd.append("--dry-run") + + print() + if dry_run: + print("Dry-run:", " ".join(cmd)) + else: + print(f"Uploading to: {target}") + + subprocess.run(cmd, check=True) + finally: + tmp_path.unlink(missing_ok=True) + + +def rewrite_markdown_files( + root: Path, + md_files: Sequence[Path], + image_set: set[Path], + prefix: str, + public_base_url: str, + backup_ext: str, +) -> int: + changed_count = 0 + + def to_url(md_file: Path, raw_ref: str, is_markdown: bool) -> str | None: + ref = parse_md_ref(raw_ref) if is_markdown else clean_ref(raw_ref) + if not ref or not is_local_ref(ref): + return None + resolved = resolve_local_ref(md_file, ref, root) + if not resolved or resolved not in image_set: + return None + obj = rel_object_path(root, resolved, prefix) + return build_public_url(public_base_url, obj) + + for md_file in md_files: + text = md_file.read_text(encoding="utf-8") + + def md_repl(match: re.Match[str]) -> str: + raw = match.group(1) + url = to_url(md_file, raw, is_markdown=True) + if not url: + return match.group(0) + return match.group(0).replace(raw, url, 1) + + def html_repl(match: re.Match[str]) -> str: + quote_ch = match.group(1) + raw = match.group(2) + url = to_url(md_file, raw, is_markdown=False) + if not url: + return match.group(0) + return match.group(0).replace( + f"src={quote_ch}{raw}{quote_ch}", f"src={quote_ch}{url}{quote_ch}", 1 + ) + + updated = MD_IMAGE_RE.sub(md_repl, text) + updated = HTML_IMG_RE.sub(html_repl, updated) + + if updated != text: + if backup_ext: + backup_path = md_file.with_suffix(md_file.suffix + backup_ext) + backup_path.write_text(text, encoding="utf-8") + md_file.write_text(updated, encoding="utf-8") + changed_count += 1 + + return changed_count + + +def main() -> int: + args = parse_args() + + if args.rewrite_markdown and not args.public_base_url: + print( + "Error: --public-base-url is required when using --rewrite-markdown", + file=sys.stderr, + ) + return 1 + + root = Path(args.docs_root).resolve() + if not root.is_dir(): + print(f"Error: docs root not found: {args.docs_root}", file=sys.stderr) + return 1 + + if shutil.which("rg") is None: + print("Error: rg (ripgrep) not found in PATH", file=sys.stderr) + return 1 + + md_files = find_markdown_files(root) + images, missing = collect_images(root, md_files) + + if not images: + print("No local image references found in Markdown docs.") + return 0 + + rel_files = sorted(p.relative_to(root).as_posix() for p in images) + + print(f"Found {len(rel_files)} image files:") + for rel in rel_files: + print(rel) + + if missing: + print(file=sys.stderr) + print( + f"Warning: {len(missing)} referenced files were not found (showing up to 20):", + file=sys.stderr, + ) + for md, ref in missing[:20]: + print(f"{md}\t{ref}", file=sys.stderr) + + if args.list_only: + return 0 + + target = build_target(args.remote, args.bucket, args.prefix) + run_rclone_upload(root, target, rel_files, dry_run=args.dry_run) + + if args.rewrite_markdown and not args.dry_run: + changed = rewrite_markdown_files( + root=root, + md_files=md_files, + image_set=images, + prefix=args.prefix, + public_base_url=args.public_base_url, + backup_ext=args.backup_ext, + ) + print(f"Rewrote {changed} markdown files.") + + print("Done.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/docs/scripts/usage.md b/docs/scripts/usage.md new file mode 100644 index 000000000..768e41f3f --- /dev/null +++ b/docs/scripts/usage.md @@ -0,0 +1,8 @@ +```bash +bash scripts/upload-doc-images-to-r2.sh \ + --remote astrbot-docs-s3 \ + --bucket astrbot \ + --prefix docs \ + --rewrite-markdown \ + --public-base-url https://files.astrbot.app +``` \ No newline at end of file diff --git a/docs/tests/test_sync_docs_to_wiki.py b/docs/tests/test_sync_docs_to_wiki.py new file mode 100644 index 000000000..4bcecc007 --- /dev/null +++ b/docs/tests/test_sync_docs_to_wiki.py @@ -0,0 +1,491 @@ +from importlib.util import module_from_spec, spec_from_file_location +from pathlib import Path +import sys +from tempfile import TemporaryDirectory +import unittest + + +def load_sync_module(): + script_path = ( + Path(__file__).resolve().parents[1] / "scripts" / "sync_docs_to_wiki.py" + ) + spec = spec_from_file_location("sync_docs_to_wiki", script_path) + if spec is None or spec.loader is None: + raise ImportError(f"Unable to load module from {script_path}") + module = module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +class SyncDocsHelpersTest(unittest.TestCase): + def test_page_name_for_nested_markdown_source(self): + module = load_sync_module() + + self.assertEqual( + module.page_name_for_source("zh/deploy/astrbot/docker.md"), + "zh-deploy-astrbot-docker", + ) + + def test_strip_frontmatter_removes_leading_block(self): + module = load_sync_module() + + source = "---\nlayout: home\n---\n\n# Title\n" + + self.assertEqual(module.strip_frontmatter(source), "# Title\n") + + def test_module_does_not_expose_removed_wrapper_helpers(self): + module = load_sync_module() + + self.assertFalse(hasattr(module, "get_link_resolver")) + self.assertFalse(hasattr(module, "resolve_source_path")) + self.assertFalse(hasattr(module, "compute_managed_files")) + self.assertFalse(hasattr(module, "MANAGED_FILENAMES")) + self.assertFalse(hasattr(module, "find_candidates_by_suffix")) + + def test_module_exposes_consolidated_helper_names(self): + module = load_sync_module() + + self.assertTrue(hasattr(module, "prepare_candidate_path")) + self.assertTrue(hasattr(module, "resolve_link_path")) + self.assertTrue(hasattr(module, "LANG_CONFIG")) + self.assertTrue(hasattr(module, "Segment")) + self.assertTrue(hasattr(module, "iter_segments")) + + def test_parse_doc_target_returns_base_and_anchor(self): + module = load_sync_module() + + self.assertEqual( + module.parse_doc_target("/deploy/guide#intro"), + ("/deploy/guide", "#intro"), + ) + self.assertIsNone(module.parse_doc_target("https://example.com/guide")) + self.assertIsNone(module.parse_doc_target("../images/diagram.png")) + self.assertIsNone(module.parse_doc_target("#intro")) + + def test_iter_markdown_links_handles_whitespace_before_target(self): + module = load_sync_module() + + links = list(module.iter_markdown_links("See [Guide]\n(guide.md).\n")) + + self.assertEqual([link.target for link in links], ["guide.md"]) + + def test_iter_segments_splits_text_inline_and_fenced_code(self): + module = load_sync_module() + + segments = list( + module.iter_segments( + "Start [Guide](/guide) `code [Guide](/guide)`\n\n```md\n[Guide](/guide)\n```\nTail\n" + ) + ) + + self.assertEqual( + [(segment.kind, segment.text) for segment in segments], + [ + ("text", "Start [Guide](/guide) "), + ("inline_code", "`code [Guide](/guide)`"), + ("text", "\n\n"), + ("code_block", "```md\n[Guide](/guide)\n```"), + ("text", "\nTail\n"), + ], + ) + + def test_rewrite_links_handles_absolute_same_language_links(self): + module = load_sync_module() + + resolver = module.LinkResolver(Path(__file__).resolve().parents[1]) + + content = "See [Docker](/deploy/astrbot/docker).\n" + + self.assertEqual( + module.rewrite_links( + content, + source_path="zh/what-is-astrbot.md", + resolver=resolver, + ), + "See [Docker](zh-deploy-astrbot-docker).\n", + ) + + def test_rewrite_links_handles_relative_links(self): + module = load_sync_module() + + resolver = module.LinkResolver(Path(__file__).resolve().parents[1]) + + content = "Use [Dify](../agent-runners/dify.md).\n" + + self.assertEqual( + module.rewrite_links( + content, + source_path="zh/providers/dify.md", + resolver=resolver, + ), + "Use [Dify](zh-providers-agent-runners-dify).\n", + ) + + def test_rewrite_links_handles_rewritten_root_paths(self): + module = load_sync_module() + + resolver = module.LinkResolver(Path(__file__).resolve().parents[1]) + + content = "See [Connecting Model Services](/config/providers/start).\n" + + self.assertEqual( + module.rewrite_links( + content, + source_path="zh/what-is-astrbot.md", + resolver=resolver, + ), + "See [Connecting Model Services](zh-providers-start).\n", + ) + + def test_rewrite_links_handles_internal_links_with_parentheses(self): + module = load_sync_module() + + with TemporaryDirectory() as temp_dir: + source_root = Path(temp_dir) / "docs" + (source_root / "zh").mkdir(parents=True) + (source_root / "zh" / "index.md").write_text( + "See [Guide](/guide(test)).\n", + encoding="utf-8", + ) + (source_root / "zh" / "guide(test).md").write_text( + "# Guide\n", + encoding="utf-8", + ) + resolver = module.LinkResolver(source_root) + + self.assertEqual( + module.rewrite_links( + "See [Guide](/guide(test)).\n", + source_path="zh/index.md", + resolver=resolver, + ), + "See [Guide](zh-guide(test)).\n", + ) + + def test_rewrite_links_leaves_local_asset_links_unchanged(self): + module = load_sync_module() + + with TemporaryDirectory() as temp_dir: + source_root = Path(temp_dir) / "docs" + (source_root / "zh" / "use").mkdir(parents=True) + (source_root / "zh" / "images").mkdir(parents=True) + (source_root / "zh" / "use" / "guide.md").write_text( + "# Guide\n", encoding="utf-8" + ) + (source_root / "zh" / "images" / "diagram.png").write_bytes(b"png") + resolver = module.LinkResolver(source_root) + + content = "![Diagram](../images/diagram.png)\n" + + self.assertEqual( + module.rewrite_links( + content, + source_path="zh/use/guide.md", + resolver=resolver, + ), + content, + ) + + def test_rewrite_links_skips_fenced_code_blocks(self): + module = load_sync_module() + + with TemporaryDirectory() as temp_dir: + source_root = Path(temp_dir) / "docs" + (source_root / "zh").mkdir(parents=True) + (source_root / "zh" / "index.md").write_text("# Home\n", encoding="utf-8") + (source_root / "zh" / "guide.md").write_text("# Guide\n", encoding="utf-8") + resolver = module.LinkResolver(source_root) + + content = "```md\n[Guide](/guide)\n```\n\nSee [Guide](/guide).\n" + + self.assertEqual( + module.rewrite_links( + content, + source_path="zh/index.md", + resolver=resolver, + ), + "```md\n[Guide](/guide)\n```\n\nSee [Guide](zh-guide).\n", + ) + + def test_rewrite_links_skips_inline_code(self): + module = load_sync_module() + + with TemporaryDirectory() as temp_dir: + source_root = Path(temp_dir) / "docs" + (source_root / "zh").mkdir(parents=True) + (source_root / "zh" / "index.md").write_text("# Home\n", encoding="utf-8") + (source_root / "zh" / "guide.md").write_text("# Guide\n", encoding="utf-8") + resolver = module.LinkResolver(source_root) + + content = "Use `[Guide](/guide)` literally, then See [Guide](/guide).\n" + + self.assertEqual( + module.rewrite_links( + content, + source_path="zh/index.md", + resolver=resolver, + ), + "Use `[Guide](/guide)` literally, then See [Guide](zh-guide).\n", + ) + + def test_link_resolver_resolves_source_paths(self): + module = load_sync_module() + + with TemporaryDirectory() as temp_dir: + source_root = Path(temp_dir) / "docs" + (source_root / "zh" / "deploy").mkdir(parents=True) + (source_root / "zh" / "index.md").write_text("# Home\n", encoding="utf-8") + (source_root / "zh" / "deploy" / "guide.md").write_text( + "# Guide\n", encoding="utf-8" + ) + + resolver = module.LinkResolver(source_root) + + self.assertEqual( + resolver.resolve_markdown_target("/deploy/guide#intro", "zh/index.md"), + ("zh/deploy/guide.md", "#intro"), + ) + + def test_resolve_link_path_resolves_relative_target(self): + module = load_sync_module() + + with TemporaryDirectory() as temp_dir: + source_root = Path(temp_dir) / "docs" + (source_root / "zh" / "providers").mkdir(parents=True) + (source_root / "zh" / "agent-runners").mkdir(parents=True) + (source_root / "zh" / "providers" / "dify.md").write_text( + "# Dify\n", + encoding="utf-8", + ) + (source_root / "zh" / "agent-runners" / "dify.md").write_text( + "# Agent Runner\n", + encoding="utf-8", + ) + + self.assertEqual( + module.resolve_link_path( + base_target="../agent-runners/dify.md", + source_path="zh/providers/dify.md", + source_root=source_root, + source_pages=module.discover_source_pages(str(source_root)), + ).resolved_path, + "zh/agent-runners/dify.md", + ) + + def test_build_home_page_uses_language_config(self): + module = load_sync_module() + + self.assertIn( + module.LANG_CONFIG["zh"]["home_intro"], module.build_home_page("zh") + ) + self.assertIn( + module.LANG_CONFIG["en"]["home_intro"], module.build_home_page("en") + ) + + def test_prepare_candidate_path_normalizes_suffix_and_alias(self): + module = load_sync_module() + + self.assertEqual( + module.prepare_candidate_path( + module.PurePosixPath("zh/config/providers/../providers/start") + ), + module.PurePosixPath("zh/providers/start.md"), + ) + + def test_find_existing_source_path_matches_language_bounded_suffixes(self): + module = load_sync_module() + + self.assertEqual( + module.find_existing_source_path( + candidate=module.PurePosixPath("zh/bar/guide.md"), + source_root=Path("/tmp/nonexistent"), + source_pages=( + "zh/bar/guide.md", + "zh/foo/bar/guide.md", + "zh/foobar/guide.md", + "en/bar/guide.md", + ), + ).ambiguous_matches, + ("zh/bar/guide.md", "zh/foo/bar/guide.md"), + ) + + def test_build_page_info_returns_page_info_dataclass(self): + module = load_sync_module() + + with TemporaryDirectory() as temp_dir: + source_root = Path(temp_dir) / "docs" + (source_root / "zh").mkdir(parents=True) + (source_root / "zh" / "index.md").write_text( + "# 中文首页\n", encoding="utf-8" + ) + + resolver = module.LinkResolver(source_root) + page_info = module.build_page_info( + source_root=source_root, + source_path="zh/index.md", + resolver=resolver, + ) + + self.assertIsInstance(page_info, module.PageInfo) + self.assertEqual(page_info.page_name, "zh-index") + + def test_build_page_info_uses_display_ready_group(self): + module = load_sync_module() + + with TemporaryDirectory() as temp_dir: + source_root = Path(temp_dir) / "docs" + (source_root / "zh" / "agent-runners").mkdir(parents=True) + (source_root / "zh" / "agent-runners" / "guide.md").write_text( + "# Guide\n", + encoding="utf-8", + ) + + resolver = module.LinkResolver(source_root) + page_info = module.build_page_info( + source_root=source_root, + source_path="zh/agent-runners/guide.md", + resolver=resolver, + ) + + self.assertEqual(page_info.group, "agent runners") + + def test_sync_writes_pages_and_sidebar(self): + module = load_sync_module() + + with TemporaryDirectory() as temp_dir: + source_root = Path(temp_dir) / "docs" + wiki_root = Path(temp_dir) / "wiki" + (source_root / "zh").mkdir(parents=True) + (source_root / "en").mkdir(parents=True) + + (source_root / "zh" / "index.md").write_text( + "---\nlayout: home\n---\n\n# 中文首页\n\nSee [Guide](/deploy/guide).\n", + encoding="utf-8", + ) + (source_root / "zh" / "deploy").mkdir(parents=True) + (source_root / "zh" / "deploy" / "guide.md").write_text( + "# 部署指南\n", + encoding="utf-8", + ) + (source_root / "en" / "index.md").write_text( + "# English Home\n\nSee [Guide](/en/deploy/guide).\n", + encoding="utf-8", + ) + (source_root / "en" / "deploy").mkdir(parents=True) + (source_root / "en" / "deploy" / "guide.md").write_text( + "# Deployment Guide\n", + encoding="utf-8", + ) + + module.sync_docs_to_wiki(source_root=source_root, wiki_root=wiki_root) + + self.assertTrue((wiki_root / "Home.md").exists()) + self.assertTrue((wiki_root / "Home-en.md").exists()) + self.assertTrue((wiki_root / "_Sidebar.md").exists()) + self.assertTrue((wiki_root / "zh-index.md").exists()) + self.assertTrue((wiki_root / "en-index.md").exists()) + self.assertIn( + "[Guide](zh-deploy-guide)", + (wiki_root / "zh-index.md").read_text(encoding="utf-8"), + ) + + def test_sync_preserves_unknown_wiki_pages(self): + module = load_sync_module() + + with TemporaryDirectory() as temp_dir: + source_root = Path(temp_dir) / "docs" + wiki_root = Path(temp_dir) / "wiki" + (source_root / "zh").mkdir(parents=True) + (source_root / "en").mkdir(parents=True) + + (source_root / "zh" / "index.md").write_text( + "# 中文首页\n", encoding="utf-8" + ) + (source_root / "en" / "index.md").write_text( + "# English Home\n", encoding="utf-8" + ) + + wiki_root.mkdir(parents=True) + handwritten = wiki_root / "zh-handwritten.md" + handwritten.write_text("# Keep me\n", encoding="utf-8") + + module.sync_docs_to_wiki(source_root=source_root, wiki_root=wiki_root) + + self.assertTrue(handwritten.exists()) + + def test_find_unresolved_doc_links_reports_ambiguous_matches(self): + module = load_sync_module() + + with TemporaryDirectory() as temp_dir: + source_root = Path(temp_dir) / "docs" + (source_root / "zh" / "foo").mkdir(parents=True) + (source_root / "zh" / "bar").mkdir(parents=True) + (source_root / "zh" / "index.md").write_text( + "See [Guide](/guide).\n", + encoding="utf-8", + ) + (source_root / "zh" / "foo" / "guide.md").write_text( + "# Foo\n", encoding="utf-8" + ) + (source_root / "zh" / "bar" / "guide.md").write_text( + "# Bar\n", encoding="utf-8" + ) + + unresolved = module.find_unresolved_doc_links(source_root) + + self.assertEqual( + unresolved, + [ + "zh/index.md -> /guide (ambiguous: zh/bar/guide.md, zh/foo/guide.md)", + ], + ) + + def test_resolver_does_not_match_partial_path_segments(self): + module = load_sync_module() + + with TemporaryDirectory() as temp_dir: + source_root = Path(temp_dir) / "docs" + (source_root / "zh" / "foobar").mkdir(parents=True) + (source_root / "zh" / "index.md").write_text( + "See [Guide](/bar/guide).\n", + encoding="utf-8", + ) + (source_root / "zh" / "foobar" / "guide.md").write_text( + "# Guide\n", + encoding="utf-8", + ) + + resolver = module.LinkResolver(source_root) + + self.assertEqual( + resolver.resolve_markdown_target("/bar/guide", "zh/index.md"), + (None, ""), + ) + + def test_live_docs_have_no_unresolved_internal_doc_links(self): + module = load_sync_module() + + unresolved = module.find_unresolved_doc_links( + source_root=Path(__file__).resolve().parents[1], + ) + + self.assertEqual(unresolved, []) + + def test_check_unresolved_doc_links_raises_for_bad_docs(self): + module = load_sync_module() + + with TemporaryDirectory() as temp_dir: + source_root = Path(temp_dir) / "docs" + (source_root / "zh").mkdir(parents=True) + (source_root / "zh" / "index.md").write_text( + "See [Missing](/missing).\n", + encoding="utf-8", + ) + + with self.assertRaises(ValueError): + module.check_unresolved_doc_links(source_root) + + +if __name__ == "__main__": + unittest.main() diff --git a/docs/vercel.json b/docs/vercel.json new file mode 100644 index 000000000..41e7af064 --- /dev/null +++ b/docs/vercel.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://openapi.vercel.sh/vercel.json", + "framework": null, + "buildCommand": "npm run docs:build", + "outputDirectory": ".vitepress/dist", + "cleanUrls": true, + "trailingSlash": false, + "routes": [ + { "handle": "filesystem" }, + { "src": "/.*", "dest": "/404.html", "status": 404 } + ] +} diff --git a/docs/zh/community.md b/docs/zh/community.md new file mode 100644 index 000000000..72d4504fc --- /dev/null +++ b/docs/zh/community.md @@ -0,0 +1,36 @@ +# 社区 + +## 社区渠道 + +本文档可能没有完全覆盖所有的功能,如果你有关于 AstrBot 与本文档的任何问题或建议,欢迎通过下面的社区渠道联系我们。 + +### QQ 群 + +> 所有群都可以插空加入,如果您发现群人数小于上限,请尝试加入。 + +- 9 群: 1076659624 (500 人群, 优先加此群) +- 10 群: 1078079676 (500 人群, 优先加此群) +- 1 群: 322154837 (2000 人群, 人满) +- 3 群: 630166526 (2000 人群, 人满) +- 4 群: 1077826412 (1000 人群, 人满) +- 5 群: 822130018 (2000 人群, 人满) +- 6 群: 753075035 (2000 人群, 人满) +- 7 群: 743746109 (500 人群, 人满) +- 8 群: 1030353265 (500 人群, 人满) +- **AstrBot 核心开发交流群: 975206796**(AstrBot 开发成员通常活跃于此,欢迎任何对编程/AI 技术感兴趣的同学加入~) + +### Discord + +https://discord.gg/PxgzhmxJ + +### GitHub + +欢迎提交 Issue 或 Pull Request: + +- [AstrBotDevs/AstrBot](https://github.com/AstrBotDevs/AstrBot) + +- [AstrBotDevs/AstrBot-Docs](https://github.com/AstrBotDevs/AstrBot-docs) + +## 成为 AstrBot 组织成员 + +欢迎加入我们! diff --git a/docs/zh/deploy/astrbot/1panel.md b/docs/zh/deploy/astrbot/1panel.md new file mode 100644 index 000000000..53699ae32 --- /dev/null +++ b/docs/zh/deploy/astrbot/1panel.md @@ -0,0 +1,27 @@ +# 在 1Panel 部署 AstrBot + +[1Panel](https://1panel.cn/) 是开源的新一代 Linux 服务器运维管理面板。 + +AstrBot 已经由 1Panel 团队上架至 [1Panel 应用商店](https://apps.fit2cloud.com/1panel),用户可以直接通过 1Panel 快速部署使用。 + +## 安装 1Panel + +如果您还没有安装 1Panel 面板,请参考 [1Panel 官网](https://1panel.cn/) 一键安装。 + +> International users can refer to the [1Panel official site](https://github.com/1Panel-dev/1Panel) for tutorials. + +## 安装 AstrBot + +打开 1Panel 面板,进入 1Panel 应用商店,搜索 `AstrBot`,如下图所示。 + +![image](https://files.astrbot.app/docs/source/images/1panel/image.png) + +点击 `安装`,等待安装成功。 + +安装成功后,在 1Panel 系统-防火墙页面放行对应的 AstrBot 端口(默认是 6185 端口)。 + +如果您正在使用 AWS、阿里云、腾讯云等厂商的云服务器,请确保其安全组也放行了 6185 端口。 + +## 访问 AstrBot + +访问 `http://IP:6185` 即可访问 AstrBot 的管理面板。 diff --git a/docs/zh/deploy/astrbot/btpanel.md b/docs/zh/deploy/astrbot/btpanel.md new file mode 100644 index 000000000..9bf26df89 --- /dev/null +++ b/docs/zh/deploy/astrbot/btpanel.md @@ -0,0 +1,48 @@ +# 在 宝塔面板 部署 AstrBot + +[宝塔面板](https://www.bt.cn/new/index.html)是一个安全高效、生产可用的 Linux/Windows 服务器运维面板。 + +AstrBot 已经上架至宝塔的 Docker 应用商店,支持一键安装。 + +## 安装宝塔面板 + +如果您还没有安装宝塔面板,请参考 [安装宝塔产品](https://www.bt.cn/new/download.html) 一键安装。 + +## 设置加速 URL(国内服务器用户) + +进入宝塔面板页面后,点击左侧的 `Docker`,点击设置,修改`加速 URL`。 + +![alt text](https://files.astrbot.app/docs/source/images/btpanel/image-1.png) + +## 安装 AstrBot + +进入 Docker 的应用商店,搜索 `AstrBot`,如下图所示。 + +![image](https://files.astrbot.app/docs/source/images/btpanel/image.png) + +点击安装,等待安装成功。 + +安装成功后,点击左侧 `安全`,放行对应的 AstrBot 端口(默认是 6185 端口)。 + +如果您正在使用 AWS、阿里云、腾讯云等厂商的云服务器,请确保其安全组也放行了对应的端口。 + +## 访问 AstrBot + +访问 `http://IP:6185` 即可访问 AstrBot 的管理面板。 + +> [!TIP] +> 默认情况下,上述方法只会放行一个 6185 端口。如果需要部署消息平台,需要额外放行对应的端口。点击上栏 `容器`,找到 AstrBot 容器,点击 `管理`,点击 `编辑容器`,添加对应的端口即可。 +> +> ![image](https://files.astrbot.app/docs/source/images/btpanel/image-2.png) +> +> 具体的消息平台对应端口可以参考下表: +> +>| 端口 | 描述 | 类型 +>| -------- | ------- | ------- | +>| 6185 | AstrBot WebUI `默认` 端口 | 需要 | +>| 6195 | 企业微信 `默认` 端口 | 可选 | +>| 6199 | QQ 个人号(aiocqhttp) `默认` 端口 | 可选 | +>| 6196 | QQ 官方接口(Webhook) `默认` 端口 | 可选 | +> +> 没有列举的平台表示不需要额外放行端口。 + diff --git a/docs/zh/deploy/astrbot/casaos.md b/docs/zh/deploy/astrbot/casaos.md new file mode 100644 index 000000000..14ec39e44 --- /dev/null +++ b/docs/zh/deploy/astrbot/casaos.md @@ -0,0 +1,39 @@ +# 在 CasaOS 部署 AstrBot + +## 安装 CasaOS + +```bash +curl -fsSL https://get.casaos.io | sudo bash +``` + +## 添加 CasaOS-AppStore-Play 应用商店源 + +![image](https://files.astrbot.app/docs/source/images/casaos/image.png) + +点击 `更多应用`,然后输入: + +```txt +https://play.cuse.eu.org/Cp0204-AppStore-Play.zip +``` + +并添加,等待添加完成。 + +如果您的网络环境在国内,请先搜索并添加 `dkTurbo`,否则可能无法拉取 AstrBot 镜像。 + +![image](https://files.astrbot.app/docs/source/images/casaos/image-1.png) + +输入 `Astrbot` 即可找到 AstrBot。 + +![image](https://files.astrbot.app/docs/source/images/casaos/image-2.png) + +点击图标(不是安装按钮),然后悬浮到`安装`按钮上,点击自定义安装。 + +![image](https://files.astrbot.app/docs/source/images/casaos/image-3.png) + +在网络一栏选择 `host`。 + +![image](https://files.astrbot.app/docs/source/images/casaos/image-4.png) + +然后点击`安装`开始安装。 + +安装完成后,主界面会出现 AstrBot APP,点击即可打开管理面板。 \ No newline at end of file diff --git a/docs/zh/deploy/astrbot/cli.md b/docs/zh/deploy/astrbot/cli.md new file mode 100644 index 000000000..623eb583f --- /dev/null +++ b/docs/zh/deploy/astrbot/cli.md @@ -0,0 +1,92 @@ +# 通过源码部署 AstrBot + +> [!WARNING] +> 你正在直接通过源码来部署本项目,该教程需要您具有一定的技术基础。 +> +> 以下教程默认您的设备上已经安装 Python,并且版本 `>=3.10` + + +## 下载/克隆仓库 + +如果你的电脑上安装了 `git`,你可以通过以下命令来下载源码: + +```bash +git clone https://github.com/AstrBotDevs/AstrBot.git +# 上面的代码默认会拉取最新的提交的源码,如果你需要拉取最新稳定发行版本的源码,可以使用以下命令: +# git clone --depth=1 --branch $(git ls-remote --tags --sort='-v:refname' https://github.com/AstrBotDevs/AstrBot.git | head -n1 | awk -F/ '{print $3}') https://github.com/AstrBotDevs/AstrBot.git +cd AstrBot +``` + +如果你没有安装 `git`,请先下载安装。 + +或者,直接从 GitHub 上下载源码解压: + +![image](https://files.astrbot.app/docs/source/images/cli/image.png) + +## 安装依赖并运行 + +::: details 【🥳推荐】使用 `uv` 管理依赖 + +> 如果没安装 `uv`,请参考 [Installing uv](https://docs.astral.sh/uv/getting-started/installation/) 安装。 + +2. 在终端执行(AstrBot 目录下) +```bash +uv sync +uv run main.py +``` + +如果您安装了一些插件,建议后续启动附上 `--no-sync` 参数,以避免插件依赖库被重复安装。我们正在努力解决这个问题,敬请期待。 + +```bash +uv run --no-sync main.py +``` +::: + +::: details Python 内置 venv 安装依赖 + +在 AstrBot 源码目录下,使用终端运行以下命令: + +> 如果是 Windows,直接下载源码解压的,请打开解压的文件夹,在地址栏输入: +> ![image](https://files.astrbot.app/docs/source/images/cli/image-1.png) + +```bash +python3 -m venv ./venv +``` + +> 也可能是 `python` 而不是 `python3` + +以上步骤会创建一个虚拟环境并激活(以免打乱您设备本地的 Python 环境)。 + +接下来,通过以下命令安装依赖文件,这可能需要花费一些时间: + +Mac/Linux/WSL 执行: + +```bash +source venv/bin/activate +python -m pip install -r requirements.txt -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple +python main.py +``` + +Windows 执行: + +```bash +venv\Scripts\activate +python -m pip install -r requirements.txt -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple +python main.py +``` +::: + + +## 🎉 大功告成! + +如果一切顺利,你会看到 AstrBot 打印出的日志。 + +如果没有报错,你会看到一条日志显示类似 `🌈 管理面板已启动,可访问` 并附带了几条链接。打开其中一个链接即可访问 AstrBot 管理面板。链接是 `http://localhost:6185`。 + +> [!TIP] +> 如果你正在服务器上部署 AstrBot,需要将 `localhost` 替换为你的服务器 IP 地址。 +> +> 默认用户名和密码是 `astrbot` 和 `astrbot`。 + + +接下来,你需要部署任何一个消息平台,才能够实现在消息平台上使用 AstrBot。 diff --git a/docs/zh/deploy/astrbot/community-deployment.md b/docs/zh/deploy/astrbot/community-deployment.md new file mode 100644 index 000000000..4042d86f7 --- /dev/null +++ b/docs/zh/deploy/astrbot/community-deployment.md @@ -0,0 +1,52 @@ +# 社区提供的部署方式 + +> [!WARNING] +> AstrBot 官方不保证这些部署方式的安全性和稳定性。 + +## Linux 一键部署脚本 + +使用 `curl` 去下载脚本并且使用 `bash` 执行脚本: + +```bash +bash <(curl -sSL https://raw.githubusercontent.com/zhende1113/Antlia/refs/heads/main/Script/AstrBot/Antlia.sh) +``` + +如果你的系统没有 `curl`,你可以使用 `wget`: + +```bash +wget -qO- https://raw.githubusercontent.com/zhende1113/Antlia/refs/heads/main/Script/AstrBot/Antlia.sh | bash +``` + +仓库地址:[zhende1113/Antlia](https://github.com/zhende1113/Antlia/) + +## Linux 一键部署脚本(基于Docker) + +支持 AstrBot / NapCat + +> [!TIP] +> 权限不足时请使用 `sudo` 提权 + +### 使用 `curl` + +```bash +curl -sSL https://raw.githubusercontent.com/railgun19457/AstrbotScript/main/AstrbotScript.sh -o AstrbotScript.sh +chmod +x AstrbotScript.sh +sudo ./AstrbotScript.sh +``` + +### 使用 `wget` + +```bash +wget -qO AstrbotScript.sh https://raw.githubusercontent.com/railgun19457/AstrbotScript/main/AstrbotScript.sh +chmod +x AstrbotScript.sh +sudo ./AstrbotScript.sh +``` + +> [!note] +> `sudo ./AstrbotScript.sh --no-color (可选禁用彩色输出)` + +__仓库地址:[railgun19457/AstrbotScript](https://github.com/railgun19457/AstrbotScript)__ + +## AstrBot Android 部署 + +参考 [zz6zz666/AstrBot-Android-App](https://github.com/zz6zz666/AstrBot-Android-App) diff --git a/docs/zh/deploy/astrbot/compshare.md b/docs/zh/deploy/astrbot/compshare.md new file mode 100644 index 000000000..fd9269730 --- /dev/null +++ b/docs/zh/deploy/astrbot/compshare.md @@ -0,0 +1,89 @@ +# 通过优云智算部署 + +优云智算是 UCloud 旗下的 GPU 算力租赁和大模型 API 调用平台,致力于为 AI、深度学习、科学计算相关客户提供丰富多样的算力资源。 + +AstrBot 在优云智算发布了 Ollama + AstrBot 一键自部署镜像,并且接入了优云智算 LLM API。 + +## 使用 Ollama + AstrBot 一键自部署镜像 + +> 镜像默认参数为:RTX 3090 24GB + Intel 16核 + 64GB RAM + 200GB 系统盘。采用按量付费的方式,请留意您的余额使用情况。 + +1. 通过 [此链接](https://passport.compshare.cn/register?referral_code=FV7DcGowN4hB5UuXKgpE74) 注册优云智算账户。 +1. 打开 [AstrBot 镜像链接](https://www.compshare.cn/images/0oX7xoGrzfre),点击创建实例。 +2. 部署成功后,在[控制台](https://console.compshare.cn/light-gpu/console/resources)中打开「JupyterLab」 +3. 进入JupyterLab后,新建一个终端 Terminal,在终端中粘贴以下指令 + +```bash +cd +./astrbot_booter.sh +``` + +指令运行结果如下所示即说明启动成功。 + +```txt +(py312) root@f8396035c96d:/workspace# cd +./astrbot_booter.sh +Starting AstrBot... +Starting ollama... +Both services started in the background. +``` + +启动成功后,在浏览器中输入 `http://实例的外网IP:6185` 即可访问 AstrBot 的界面。外网 IP 可以在 控制台->基础网络(外网)中获取。 + +> 可能需要等待半分钟左右。 + +![WebUI 界面](https://www-s.ucloud.cn/2025/07/7e9fc6edc1dfa916abc069f4cecc24cf_1753940381771.png) + +使用用户名:astrbot 和密码 astrbot 进行登录。 + + +登录成功后,可以重新设置密码,并进入 AstrBot 的页面。 + +实例默认会导入 Ollama-DeepSeek-R1-32B 模型。 + +## 使用其他模型 + +### 使用 Ollama 拉取模型 + +镜像原生部署了 Ollama,您可以通过 Ollama 指令自行拉取想要的模型,将模型本地部署在实例。 + +1. 在 [Ollama](https://ollama.com/search) 模型列表找到想部署的模型。 +2. 通过 SSH 进入到实例的终端(进入优云智算平台的控制台页面->实例列表->控制台指令和密码) +3. 通过 `ollama pull 模型名` 拉取模型,等待拉取成功。 +4. 在 AstrBot 面板的 服务提供商页面找到 `ollama_deepseek-r1`,点击编辑,更新模型名称,点击保存。 + +![image](https://files.astrbot.app/docs/source/images/compshare/image-1.png) + +### 使用优云智算提供的模型 API + +AstrBot 支持接入优云智算提供的模型 API。 + +1. 在 [优云智算](https://console.compshare.cn/light-gpu/model-center) 找到想要接入的模型 +2. 在 AstrBot 面板的 服务提供商页面点击「+ 新增服务提供商」,点击优云智算(如果没有,点击“接入 OpenAI”,并且修改下一步弹出窗口的 API Base URL 为 `https://api.modelverse.cn/v1`)。在模型配置-模型名称输入模型名,点击保存。 + +### 测试 + +在 AstrBot 面板左侧点击 `聊天`,输入 `/provider`,可以查看和切换您当前接入的提供商。 + +您可以直接聊天来测试模型是否正常。 + +![image](https://files.astrbot.app/docs/source/images/compshare/image-2.png) + + +## 接入到消息平台 + +- 飞书:[接入到飞书](https://docs.astrbot.app/deploy/platform/lark.html) +- LINE:[接入到 LINE](https://docs.astrbot.app/deploy/platform/line.html) +- 钉钉:[接入到钉钉](https://docs.astrbot.app/deploy/platform/dingtalk.html) +- 企业微信:[接入到企业微信应用](https://docs.astrbot.app/deploy/platform/wecom.html) +- 微信客服:[接入到微信客服](https://docs.astrbot.app/deploy/platform/wecom.html) +- 微信公众平台:[接入到微信公众平台](https://docs.astrbot.app/deploy/platform/weixin-official-account.html) +- QQ 官方机器人平台:[接入到 QQ 机器人](https://docs.astrbot.app/deploy/platform/qqofficial/webhook.html) +- KOOK:[接入到 KOOK](https://docs.astrbot.app/deploy/platform/kook.html) +- Slack:[接入到 Slack](https://docs.astrbot.app/deploy/platform/slack.html) +- Discord:[接入到 Discord](https://docs.astrbot.app/deploy/platform/discord.html) +- 更多接入方式参考 [AstrBot 官方文档](https://docs.astrbot.app/what-is-astrbot.html) + +## 更多功能 + +更多功能情参考 [AstrBot 官方文档](https://docs.astrbot.app)。 \ No newline at end of file diff --git a/docs/zh/deploy/astrbot/desktop.md b/docs/zh/deploy/astrbot/desktop.md new file mode 100644 index 000000000..8d418001c --- /dev/null +++ b/docs/zh/deploy/astrbot/desktop.md @@ -0,0 +1,33 @@ +# 使用 AstrBot 桌面客户端部署 + +`AstrBot-desktop` 适合在本地电脑快速部署和使用 AstrBot,支持 Windows、macOS、Linux。 + +在多种部署方式中,桌面客户端更适合个人本地快速使用,不建议用于服务器长期运行或生产环境;如需生产部署,建议优先考虑 [Docker 部署](/deploy/astrbot/docker) 或 [Kubernetes 部署](/deploy/astrbot/kubernetes)。 + +相比命令行或容器方案,桌面客户端更偏向「开箱即用」体验,适合希望少折腾环境、直接开始使用的用户。 + +仓库地址:[AstrBotDevs/AstrBot-desktop](https://github.com/AstrBotDevs/AstrBot-desktop) + +## 适合谁 + +- 想快速本地部署,优先使用图形化界面的用户。 +- 不想手动维护 Docker / Python 运行环境的新手用户。 +- 个人设备长期在线,主要用于个人或小团队日常使用的场景。 + +## 主要特点 + +- 多平台安装包,下载后可直接安装使用。 +- 图形化界面配置,降低首次部署成本。 +- 适合作为本地常驻客户端。 + +## 下载并安装 + +1. 打开 [AstrBot-desktop Releases](https://github.com/AstrBotDevs/AstrBot-desktop/releases)。 +2. 下载与你系统对应的安装包(如 `.exe`、`.dmg`、`.rpm`、`.deb`)。 +3. 安装完成后启动桌面客户端,按向导完成初始化。 + +## 与启动器部署的区别 + +- 桌面客户端:更偏向开箱即用的 GUI 体验。 +- 启动器部署:更偏向自动化脚本拉起,适合希望保持传统部署流程的用户。 +- 参考 [启动器部署](/deploy/astrbot/launcher)。 diff --git a/docs/zh/deploy/astrbot/docker.md b/docs/zh/deploy/astrbot/docker.md new file mode 100644 index 000000000..30541f1e6 --- /dev/null +++ b/docs/zh/deploy/astrbot/docker.md @@ -0,0 +1,104 @@ +# 使用 Docker 部署 AstrBot + +> [!WARNING] +> 通过 Docker 可以方便地将 AstrBot 部署到 Windows, Mac, Linux 上。 +> +> 以下教程默认您的环境已安装 Docker。如果没有安装,请参考 [Docker 官方文档](https://docs.docker.com/get-docker/) 进行安装。 + +## 通过 Docker Compose 部署 + +::: details 只部署 AstrBot(通用方式) + +首先,需要 Clone AstrBot 仓库到本地: + +```bash +git clone https://github.com/AstrBotDevs/AstrBot +cd AstrBot +``` + +然后,运行 Compose: + +```bash +sudo docker compose up -d +``` + +> [!TIP] +> 如果您的网络环境在中国大陆境内,上述命令将无法正常拉取。您可能需要修改 compose.yml 文件,将其中的 `image: soulter/astrbot:latest` 替换为 `image: m.daocloud.io/docker.io/soulter/astrbot:latest`。 +::: + +::: details 带 Agent 沙盒环境的部署 + +支持原生的 Python 代码执行、Shell 代码执行等功能。 + +部署方式如下: + +```bash +git clone https://github.com/AstrBotDevs/AstrBot +cd AstrBot +# 修改 compose-with-shipyard.yml 文件中的环境变量配置,例如 Shipyard 的 access token 等 +docker compose -f compose-with-shipyard.yml up -d +docker pull soulter/shipyard-ship:latest +``` + +配置和使用详见 [Agent 沙盒环境](/use/astrbot-agent-sandbox.md) 文档。 +::: + +::: details 和 NapCat 一起部署 + +如果您想对接 NapCat,使用这种方式可以同时部署 AstrBot 和 NapCat。 + +```bash +mkdir astrbot +cd astrbot +wget https://raw.githubusercontent.com/NapNeko/NapCat-Docker/main/compose/astrbot.yml +sudo docker compose -f astrbot.yml up -d +``` + +::: + + +## 通过 Docker 部署 + +```bash +mkdir astrbot +cd astrbot +sudo docker run -itd -p 6185:6185 -p 6199:6199 -v $PWD/data:/AstrBot/data -v /etc/localtime:/etc/localtime:ro -v /etc/timezone:/etc/timezone:ro --name astrbot soulter/astrbot:latest +``` + +> [!TIP] +> 如果您的网络环境在中国大陆境内,上述命令将无法正常拉取。请使用以下命令拉取镜像: +> +> ```bash +> sudo docker run -itd -p 6185:6185 -p 6199:6199 -v $PWD/data:/AstrBot/data -v /etc/localtime:/etc/localtime:ro -v /etc/timezone:/etc/timezone:ro --name astrbot m.daocloud.io/docker.io/soulter/astrbot:latest +> ``` +> +> (感谢 DaoCloud ❤️) + +> Windows 下不需要加 sudo,下同 +> Windows 同步 Host Time(需要WSL2) + +``` +-v \\wsl.localhost\(your-wsl-os)\etc\timezone:/etc/timezone:ro +-v \\wsl.localhost\(your-wsl-os)\etc\localtime:/etc/localtime:ro +``` + +通过以下命令查看 AstrBot 的日志: + +```bash +sudo docker logs -f astrbot +``` + +## 🎉 大功告成 + +如果一切顺利,你会看到 AstrBot 打印出的日志。 + +如果没有报错,你会看到一条日志显示类似 `🌈 管理面板已启动,可访问` 并附带了几条链接。打开其中一个链接即可访问 AstrBot 管理面板。 + +> [!TIP] +> 由于 Docker 隔离了网络环境,所以不能使用 `localhost` 访问管理面板。 +> +> 默认用户名和密码是 `astrbot` 和 `astrbot`。 +> +> 如果部署在云服务器上,需要在相应厂商控制台里放行对应端口。 + +接下来,你需要部署任何一个消息平台,才能够实现在消息平台上使用 AstrBot。 diff --git a/docs/zh/deploy/astrbot/kubernetes.md b/docs/zh/deploy/astrbot/kubernetes.md new file mode 100644 index 000000000..cdcb058a6 --- /dev/null +++ b/docs/zh/deploy/astrbot/kubernetes.md @@ -0,0 +1,197 @@ +# 使用 Kubernetes 部署 AstrBot + +> [!WARNING] +> 通过 Kubernetes (K8s) 可以将 AstrBot 以高可用的方式部署在集群环境中,当出现故障时可以自动拉起恢复。 +> +> 由于 AstrBot 当前使用 SQLite 数据库,此部署方案不支持多副本水平扩展。同时,若采用 Sidecar 模式,NapCat 的登录状态持久化需要您特别关注。 +> +> 以下教程默认您的环境已安装并配置好 `kubectl`,且能够连接到您的 K8s 集群。 + +## 准备工作 + +在开始之前,请确保您的 Kubernetes 集群满足以下条件: + +1. **拥有默认的 StorageClass**:用于动态创建 `PersistentVolumeClaim` (PVC)。您可以通过 `kubectl get sc` 查看。如果没有,您需要手动创建 `PersistentVolume` (PV) 或安装相应的存储插件 (如 `nfs-client-provisioner`)。 +2. **网络访问**:确保您的集群节点可以从 `docker.io` 或您指定的镜像仓库拉取镜像。 + +## 部署方式 + +我们提供两种部署方案: + +* **集成部署 (Sidecar 模式)**:将 AstrBot 和 NapCat 部署在同一个 Pod 中,推荐用于 QQ 个人号。 +* **独立部署**:只部署 AstrBot,适用于其他平台或您希望独立管理 NapCat 的场景。 + +--- + +### 方式一:和 NapCatQQ 一起部署 (Sidecar) + +此方式位于 `k8s/astrbot_with_napcat` 目录。 + +#### 1. 部署 + +```bash +# 1. 创建命名空间 +kubectl apply -f k8s/astrbot_with_napcat/00-namespace.yaml + +# 2. 创建持久化存储卷 +# 注意:astrbot-data-shared-pvc 需要 ReadWriteMany (RWX) 访问模式。 +# 如果您的集群不支持 RWX,您需要配置 NFS 等共享存储,并修改 01-pvc.yaml 中的 storageClassName。 +kubectl apply -f k8s/astrbot_with_napcat/01-pvc.yaml + +# 3. 部署应用 +kubectl apply -f k8s/astrbot_with_napcat/02-deployment.yaml +``` + +#### 2. 暴露服务 (二选一) + +* **方式 A: NodePort** + + ```bash + kubectl apply -f k8s/astrbot_with_napcat/03-service-nodeport.yaml + ``` + + 服务将通过节点 IP 和一个由 Kubernetes 自动分配的端口暴露。您可以通过以下命令查看端口: + + ```bash + kubectl get svc -n astrbot-ns + ``` + + 在输出中找到 `astrbot-webui-svc` 和 `napcat-web-svc` 的 `PORT(S)` 列,格式为 `<内部端口>:/TCP`。例如 `8080:30185/TCP`,则访问地址为 `http://:30185`。 + +* **方式 B: LoadBalancer** + + 如果您的集群支持 `LoadBalancer` 类型的服务 (通常在云厂商的 K8s 服务中提供),可以使用此方式。 + + ```bash + kubectl apply -f k8s/astrbot_with_napcat/04-service-loadbalancer.yaml + ``` + + 执行后,通过 `kubectl get svc -n astrbot-ns` 查看分配到的外部 IP (EXTERNAL-IP)。 + +#### 3. 配置连接 + +由于 AstrBot 和 NapCat 在同一个 Pod 中,它们可以通过 `localhost` 直接通信。 + +1. **在 AstrBot 中添加消息平台:** + * 进入 AstrBot WebUI,选择 `设置` -> `消息平台` -> `添加`。 + * **选择消息平台类别**: `aiocqhttp` + * **机器人名称**: `napcat` (或自定义) + * **反向 Websocket 主机**: `0.0.0.0` + * **反向 Websocket 端口**: `6199` + * 保存配置。 + + +2. **在 NapCat 中配置 Websocket Client:** + * 进入 NapCat WebUI,选择 `设置` -> `反向WS` -> `添加`。 + * **启用**: 开启 + * **URL**: `ws://localhost:6199/ws` + * **消息格式**: `Array` + * 保存配置。 + + +--- + +### 方式二:只部署 AstrBot (通用方式) + +此方式位于 `k8s/astrbot` 目录。 + +#### 1. 部署 + +```bash +# 1. 创建命名空间 +kubectl apply -f k8s/astrbot/00-namespace.yaml + +# 2. 创建持久化存储卷 +kubectl apply -f k8s/astrbot/01-pvc.yaml + +# 3. 部署应用 +kubectl apply -f k8s/astrbot/02-deployment.yaml +``` + +#### 2. 暴露服务 (二选一) + +* **方式 A: NodePort** + + ```bash + kubectl apply -f k8s/astrbot/03-service-nodeport.yaml + ``` + + 服务将通过节点 IP 和一个由 Kubernetes 自动分配的端口暴露。您可以通过以下命令查看端口: + + ```bash + kubectl get svc -n astrbot-standalone-ns + ``` + + 在输出中找到 `astrbot-webui-svc` 的 `PORT(S)` 列,格式为 `<内部端口>:/TCP`。例如 `8080:30185/TCP`,则访问地址为 `http://:30185`。 + +* **方式 B: LoadBalancer** + + ```bash + kubectl apply -f k8s/astrbot/04-service-loadbalancer.yaml + ``` + + 执行后,通过 `kubectl get svc -n astrbot-standalone-ns` 查看分配到的外部 IP (EXTERNAL-IP)。 + +--- + +## 高级配置 + +### 镜像加速 (中国大陆用户) + +如果拉取 `soulter/astrbot:latest` 或 `mlikiowa/napcat-docker:latest` 镜像困难,可以手动修改对应的 `02-deployment.yaml` 文件,将 `image` 字段替换为国内的镜像加速地址,例如: + +```yaml +# 示例: +# image: soulter/astrbot:latest +# 替换为 +image: m.daocloud.io/docker.io/soulter/astrbot:latest +``` + +### 启用 Docker 沙箱代码执行器 + +如果您需要使用沙箱代码执行器,需要将 Docker 的 socket 文件挂载到 Pod 中。 + +编辑 `02-deployment.yaml` 文件,在 `spec.template.spec` 下添加 `volumes` 和 `volumeMounts`: + +1. **在 `astrbot` 容器的 `volumeMounts` 列表下添加以下内容:** + + ```yaml + - name: docker-sock + mountPath: /var/run/docker.sock + ``` + +2. **在 `spec.template.spec.volumes` 列表下添加以下内容:** + + ```yaml + - name: docker-sock + hostPath: + path: /var/run/docker.sock + type: Socket + ``` + +> [!WARNING] +> 将 Docker socket 挂载到 Pod 中存在安全风险,请确保您了解其影响。 + +## 查看日志 + +* **Sidecar 部署模式:** + + ```bash + # 查看 AstrBot 日志 + kubectl logs -f -n astrbot-ns deployment/astrbot-stack -c astrbot + + # 查看 NapCat 日志 + kubectl logs -f -n astrbot-ns deployment/astrbot-stack -c napcat + ``` + +* **独立部署模式:** + + ```bash + kubectl logs -f -n astrbot-standalone-ns deployment/astrbot-standalone + ``` + +## 🎉 大功告成 + +部署并暴露服务后,您就可以通过相应的 IP 和端口访问 AstrBot 管理面板了。 + +> 默认用户名和密码是 `astrbot` 和 `astrbot`。 \ No newline at end of file diff --git a/docs/zh/deploy/astrbot/launcher.md b/docs/zh/deploy/astrbot/launcher.md new file mode 100644 index 000000000..33c71826d --- /dev/null +++ b/docs/zh/deploy/astrbot/launcher.md @@ -0,0 +1,101 @@ +# 使用 AstrBot 启动器部署 AstrBot + +## AstrBot 一键启动器 + +AstrBot 一键启动器支持 Windows、MacOS、Linux 等多端部署。 + +0. 打开 [AstrBotDevs/astrbot-launcher](https://github.com/AstrBotDevs/astrbot-launcher) +1. **(可选但推荐)** 给本项目点个 [**Star ⭐**](https://github.com/AstrBotDevs/astrbot-launcher),你的支持是作者更新和维护的动力! +2. 找到右边的 Releases,点击最新版本的 Release,在新的页面的 Assets 中下载对应你系统的安装器。 + +如,Windows X86 的用户应该下载 `AstrBot.Launcher_0.2.1_x64-setup.exe`,Windows on Arm 的用户应该下载 `AstrBot.Launcher_0.2.1_arm64-setup.exe`,MacOS M 芯片的用户下载 `AstrBot.Launcher_0.2.1_aarch64.dmg`。 + +MacOS 用户下载安装好后,可能会遇到 "已损坏,无法打开" 的提示。这是因为 MacOS 的安全机制阻止了未认证的应用运行。解决方法如下: + +1. 打开终端 +2. 输入以下命令并回车: + `xattr -dr com.apple.quarantine /Applications/AstrBot\ Launcher.app` +3. 重新尝试打开 AstrBot Launcher 应用 + +## 旧版本 Windows 安装器(不推荐) + + +> [!WARNING] +> 需要您的电脑上预先安装好 Python 环境(3.10 - 3.13),并且将 Python 添加到环境变量中,否则安装器将无法正常工作。 + + +推荐使用上面提到的 AstrBot 一键启动器来部署 AstrBot,因为它更简单、更自动化、更现代化,适合大多数用户。 + +安装器是一个使用 `Powershell` 编写的脚本,体积小巧,<20KB。需要您的电脑上安装有 `Powershell`,一般 `Windows 10` 及以上版本的设备都会自带这个工具。 + + +### 下载安装器 + +打开 https://github.com/AstrBotDevs/AstrBotLauncher/releases/latest + +下载 `Source code (zip)` 并解压到您的电脑。 + +### 运行安装器 + +> 视频和此处不一致,请参考此处!!!如果部署不了,请参阅其他两个部署方式:Docker 部署和 手动部署。 + +解压后,打开文件夹, + +地址栏输入 Powershell 并打开: + +![image](https://files.astrbot.app/docs/source/images/windows/image-4.png) + +将 `launcher_astrbot_en.bat` 批处理文件拖进去回车运行。 + +> [!WARNING] +> - 这个脚本没有病毒。如果提示 `Windows 已保护您的电脑`,请点击 `更多信息`,然后点击 `仍要运行`。 +> +> - 脚本默认使用 `python` 指令来执行代码,如果你想指定 Python 解释器器路径或者指令,请修改 `launcher_astrbot_en.bat` 文件。找到 `set PYTHON_CMD=python` 这一行,将 `python` 改为你的 Python 解释器路径或指令。 +> + +如果没有检测到 Python 环境,脚本将会提示并退出。 + +脚本将自动检测目录下是否有 `AstrBot` 文件夹,如果没有,将会从 [GitHub](https://github.com/AstrBotDevs/AstrBot/releases/latest) 自动下载最新的 AstrBot 源码。下载好后,会自动安装 AstrBot 的依赖并运行。 + +## 🎉 大功告成! + +如果一切顺利,你会看到 AstrBot 打印出的日志。 + +如果没有报错,你会看到一条日志显示类似 `🌈 管理面板已启动,可访问` 并附带了几条链接。打开其中一个链接即可访问 AstrBot 管理面板。 + +> [!TIP] +> 默认用户名和密码是 `astrbot` 和 `astrbot`。 +> +> **当管理面板打开时遇到 404 错误:** +> 在 [release](https://github.com/AstrBotDevs/AstrBot/releases) 页面下载dist.zip,解压拖到 AstrBot/data 下。还不行请重启电脑(来自群里的反馈) + +接下来,你需要部署任何一个消息平台,才能够实现在消息平台上使用 AstrBot。 + + +> [!TIP] +> 如果部署不了,请参阅其他两个部署方式:Docker 部署和 手动部署。 + + +## 报错:Python is not installed + +如果提示 Python is not installed,并且已经安装 Python,并且**也已经重启并仍报这个错误**,说明环境变量不对,有两个方法解决: + +**方法 1:** + +windows 搜索 Python,打开文件位置: + +![image](https://files.astrbot.app/docs/source/images/windows/image.png) + +右键下面这个快捷方式,打开文件所在位置: + +![alt text](https://files.astrbot.app/docs/source/images/windows/image-1.png) + +复制文件地址: + +![image](https://files.astrbot.app/docs/source/images/windows/image-2.png) + +回到 `launcher_astrbot_en.bat` 文件,右键点击 `在记事本中编辑`,找到 `set PYTHON_CMD=python` 这一行,将 `python` 改为你的 Python 解释器路径或指令,路径两端的双引号不要删。 + +**方法 2:** + +重装 python,并且在安装时勾选 `Add Python to PATH`,然后重启电脑。 \ No newline at end of file diff --git a/docs/zh/deploy/astrbot/other-deployments.md b/docs/zh/deploy/astrbot/other-deployments.md new file mode 100644 index 000000000..5c5dcd663 --- /dev/null +++ b/docs/zh/deploy/astrbot/other-deployments.md @@ -0,0 +1,5 @@ +# 其他部署方式 + +- [CasaOS 部署](./casaos.md) +- [优云智算 GPU 部署](./compshare.md) +- [社区提供的部署方式](./community-deployment.md) diff --git a/docs/zh/deploy/astrbot/package.md b/docs/zh/deploy/astrbot/package.md new file mode 100644 index 000000000..3d208f297 --- /dev/null +++ b/docs/zh/deploy/astrbot/package.md @@ -0,0 +1,17 @@ +# 包管理器部署(uv) + +使用 `uv` 可以快速安装并启动 AstrBot。 + +## 前置条件 + +如果尚未安装 `uv`,请先按照官方文档安装: + +`uv` 支持 Linux、Windows、macOS。 + +## 安装并启动 + +```bash +uv tool install astrbot +astrbot init # 只需要在第一次部署时执行,后续启动不需要执行 +astrbot +``` diff --git a/docs/zh/deploy/astrbot/rainyun.md b/docs/zh/deploy/astrbot/rainyun.md new file mode 100644 index 000000000..b0628b4d0 --- /dev/null +++ b/docs/zh/deploy/astrbot/rainyun.md @@ -0,0 +1,44 @@ +# 通过 雨云 一键部署 + +[雨云](https://www.rainyun.com/about)成立于 2018 年,是具有自主知识产权的国产云计算服务提供商,具有可靠的营业资质和实体办公场所。 + +AstrBot 已经上架至雨云的预装软件列表,支持**一键安装** AstrBot 并提供高性能的云计算资源,保证 `AstrBot` 24 小时在线。 + +目前有两种部署方式:云服务器部署和云应用部署。 + +## 云服务器 + +1. 打开 [雨云官网](https://www.rainyun.com/NjU1ODg0_)。 +2. 根据你的喜好和预算,选择一个合适的服务器配置。建议选择 至少 2 核 CPU、4GB 内存的服务器,以确保 AstrBot 的流畅运行。 +3. 在下面的 `系统和软件安装` 一节,选中 `AstrBot`,然后点击 `立即购买`。 +4. 如果您的余额不足,将会跳转至充值页面。充值完成后再返回点击 `立即购买` 即可。 + +![AstrBot - 系统和软件安装](https://files.astrbot.app/docs/source/images/rainyun/image.png) + +接下来,雨云会自动帮您安装好系统和 `AstrBot` 软件。 + +如果有疑问,请: + +1. 点击雨云官网右下角 `咨询` 提交工单 +2. 点击雨云官网上方 `交流社区` 添加雨云 QQ 群。 + +## 云应用 + +雨云支持更加优惠的云应用部署方式来一键部署 AstrBot。点击以下图标来部署: + +[![Deploy on RainYun](https://rainyun-apps.cn-nb1.rains3.com/materials/deploy-on-rainyun-en.svg)](https://app.rainyun.com/apps/rca/store/5994?ref=NjU1ODg0) + +## 附录: 配置端口映射 + +> [!NOTE] +> 只有当您购买的是 `江苏宿迁` 的服务器时,才需要配置端口映射。 + +通过 `我的云服务器` 进入 `云服务器` 页面,可以看到 `NAT端口映射管理` 卡片,如下图所示: + +![NAT端口映射管理](https://files.astrbot.app/docs/source/images/rainyun/image-1.png) + +点击 `+端口设置` -> `新建规则`,如下图所示: + +![创建NAT端口映射规则](https://files.astrbot.app/docs/source/images/rainyun/image-2.png) + +然后,内网端口填写 `6185`,点击 `创建映射规则`,这样就可以通过 `http://IP:上面设置好的外网端口` 访问 AstrBot 的管理面板了。 diff --git a/docs/zh/deploy/astrbot/sys-pm.md b/docs/zh/deploy/astrbot/sys-pm.md new file mode 100644 index 000000000..48bf94bf6 --- /dev/null +++ b/docs/zh/deploy/astrbot/sys-pm.md @@ -0,0 +1,39 @@ +# 通过系统包管理器安装 +> [!WARNING] +> 目前仅提供AUR版本 +> 如果你是windows用户/macos用户,建议通过uv来安装 +> 如果你是Linux用户,强烈建议通过包管理器来安装 + +# 准备步骤 + +## AUR 是什么? +AUR允许用户从社区维护的软件仓库中安装软件。AUR的包通常是由社区成员维护的,而不是官方维护的。 +常见的AUR助手有yay,paru。 +以下教程以paru为例,yay同理,仅需将paru替换为yay。 + +# 安装过程 + +## AUR +```bash +paru -S astrbot-git +# 提示: +# 开始审阅步骤,按q可退出审阅,继续安装 +# 安装后数据目录固定在:~/.local/share/astrbot +``` +# 启动 +>[!TIP] +> 你可以直接使用 astrbot init (首次运行)初始化 +> 使用astrbot run运行 +> 但是更加推荐使用systemctl启动,拥有自动重启,日志轮转等功能 + +```bash +systemctl --user start astrbot.service +``` + +# 开机自启 +```bash +# 处于安全考虑,设计为以用户身份执行 +systemctl --user enable astrbot.service +# 如果需要立即启动,加上--now +# systemctl --user enable --now astrbot.service +``` diff --git a/docs/zh/deploy/when-deployed.md b/docs/zh/deploy/when-deployed.md new file mode 100644 index 000000000..b4a17958a --- /dev/null +++ b/docs/zh/deploy/when-deployed.md @@ -0,0 +1,24 @@ +# 支持我们 + +我们是开源免费项目,AstrBot 的持续开发和维护离不开社区的支持。如果你觉得这个项目对你有帮助,欢迎通过以下几种方式支持我们: + +1. 在 GitHub 上给 [AstrBot](https://github.com/AstrBotDevs/AstrBot) 点一个 Star ⭐️。 +2. 通过 [爱发电平台](https://afdian.com/a/astrbot_team) 支持我们。 +3. 通过这个链接「[雨云官网](https://www.rainyun.com/NjU1ODg0_)」购买云服务器或云应用部署 AstrBot。如果你正好需要云服务器(例如用于部署 AstrBot),非常推荐使用雨云的一键部署方案。雨云是具有自主知识产权的国产云计算服务提供商,拥有可靠的营业资质和实体办公场所。 +4. 如果您是企业用户,可以联系我们获取定制化、文档/项目首页赞助广告位等服务支持。 + +如果你愿意支持我们,那我会非常非常感谢你,也会继续用更优秀的产品回应你的信任。🌟 + +## Wakatime + +AstrBot 主仓库:[![wakatime](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018e705a-a1a7-409a-a849-3013485e6c8e.svg)](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018e705a-a1a7-409a-a849-3013485e6c8e) + +AstrBot DashBoard:[![wakatime](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018c440f-c177-45f8-8224-292cdf5926f3.svg)](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018c440f-c177-45f8-8224-292cdf5926f3) + +AstrBot 文档:[![wakatime](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018c9619-e195-4b94-bd7b-2ca61679145b.svg)](https://wakatime.com/badge/user/915e5316-99c6-4563-a483-ef186cf000c9/project/018c9619-e195-4b94-bd7b-2ca61679145b) + +❤️ 非常欢迎您提交贡献到这个项目中,比如提交 Issue、PR。 + +## 正文 + +当你看到这里,说明已经成功部署好消息平台并且实现了第一条指令的收发。接下来,你可以配置大语言模型,或者添加插件。请参看 `配置-接入大模型服务` 一节。 diff --git a/docs/zh/dev/astrbot-config.md b/docs/zh/dev/astrbot-config.md new file mode 100644 index 000000000..b6b7edd27 --- /dev/null +++ b/docs/zh/dev/astrbot-config.md @@ -0,0 +1,557 @@ +--- +outline: deep +--- + +# AstrBot 配置文件 + +## data/cmd_config.json + +AstrBot 的配置文件是一个 JSON 格式的文件。AstrBot 会在启动时读取这个文件,并根据文件中的配置来初始化 AstrBot,其路径位于 `data/cmd_config.json`。 + +> 在 AstrBot v4.0.0 版本及之后,我们引入了[多配置文件](https://blog.astrbot.app/posts/what-is-changed-in-4.0.0/#%E5%A4%9A%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6)的概念。`data/cmd_config.json` 作为默认配置文件 `default`。其他您在 WebUI 新建的配置文件会存储在 `data/config/` 目录下,以 `abconf_` 开头。 + +AstrBot 默认配置如下: + +```jsonc +{ + "config_version": 2, + "platform_settings": { + "unique_session": False, + "rate_limit": { + "time": 60, + "count": 30, + "strategy": "stall", # stall, discard + }, + "reply_prefix": "", + "forward_threshold": 1500, + "enable_id_white_list": True, + "id_whitelist": [], + "id_whitelist_log": True, + "wl_ignore_admin_on_group": True, + "wl_ignore_admin_on_friend": True, + "reply_with_mention": False, + "reply_with_quote": False, + "path_mapping": [], + "segmented_reply": { + "enable": False, + "only_llm_result": True, + "interval_method": "random", + "interval": "1.5,3.5", + "log_base": 2.6, + "words_count_threshold": 150, + "regex": ".*?[。?!~…]+|.+$", + "content_cleanup_rule": "", + }, + "no_permission_reply": True, + "empty_mention_waiting": True, + "empty_mention_waiting_need_reply": True, + "friend_message_needs_wake_prefix": False, + "ignore_bot_self_message": False, + "ignore_at_all": False, + }, + "provider": [], + "provider_settings": { + "enable": True, + "default_provider_id": "", + "default_image_caption_provider_id": "", + "image_caption_prompt": "Please describe the image using Chinese.", + "provider_pool": ["*"], # "*" 表示使用所有可用的提供者 + "wake_prefix": "", + "web_search": False, + "websearch_provider": "default", + "websearch_tavily_key": [], + "web_search_link": False, + "display_reasoning_text": False, + "identifier": False, + "group_name_display": False, + "datetime_system_prompt": True, + "default_personality": "default", + "persona_pool": ["*"], + "prompt_prefix": "{{prompt}}", + "max_context_length": -1, + "dequeue_context_length": 1, + "streaming_response": False, + "show_tool_use_status": False, + "streaming_segmented": False, + "max_agent_step": 30, + "tool_call_timeout": 60, + }, + "provider_stt_settings": { + "enable": False, + "provider_id": "", + }, + "provider_tts_settings": { + "enable": False, + "provider_id": "", + "dual_output": False, + "use_file_service": False, + }, + "provider_ltm_settings": { + "group_icl_enable": False, + "group_message_max_cnt": 300, + "image_caption": False, + "active_reply": { + "enable": False, + "method": "possibility_reply", + "possibility_reply": 0.1, + "whitelist": [], + }, + }, + "content_safety": { + "also_use_in_response": False, + "internal_keywords": {"enable": True, "extra_keywords": []}, + "baidu_aip": {"enable": False, "app_id": "", "api_key": "", "secret_key": ""}, + }, + "admins_id": ["astrbot"], + "t2i": False, + "t2i_word_threshold": 150, + "t2i_strategy": "remote", + "t2i_endpoint": "", + "t2i_use_file_service": False, + "t2i_active_template": "base", + "http_proxy": "", + "no_proxy": ["localhost", "127.0.0.1", "::1"], + "dashboard": { + "enable": True, + "username": "astrbot", + "password": "77b90590a8945a7d36c963981a307dc9", + "jwt_secret": "", + "host": "0.0.0.0", + "port": 6185, + }, + "platform": [], + "platform_specific": { + # 平台特异配置:按平台分类,平台下按功能分组 + "lark": { + "pre_ack_emoji": {"enable": False, "emojis": ["Typing"]}, + }, + "telegram": { + "pre_ack_emoji": {"enable": False, "emojis": ["✍️"]}, + }, + }, + "wake_prefix": ["/"], + "log_level": "INFO", + "trace_enable": False, + "pip_install_arg": "", + "pypi_index_url": "https://mirrors.aliyun.com/pypi/simple/", + "persona": [], # deprecated + "timezone": "Asia/Shanghai", + "callback_api_base": "", + "default_kb_collection": "", # 默认知识库名称 + "plugin_set": ["*"], # "*" 表示使用所有可用的插件, 空列表表示不使用任何插件 +} +``` + +## 字段详解 + +### `config_version` + +配置文件版本,请勿修改。 + +### `platform_settings` + +消息平台适配器的通用设置。 + +#### `platform_settings.unique_session` + +是否启用会话隔离。默认为 `false`。启用后,在群组或者频道中,每个人的对话的上下文都是独立的。 + +#### `platform_settings.rate_limit` + +当消息速率超过限制时的处理策略。`time` 为时间窗口,`count` 为消息数量,`strategy` 为限制策略。`stall` 为等待,`discard` 为丢弃。 + +#### `platform_settings.reply_prefix` + +回复消息时的固定前缀字符串。默认为空。 + +#### `platform_settings.forward_threshold` + +> 目前仅 QQ 平台适配器适用。 + +消息转发阈值。当回复内容超过一定字数后,机器人会将消息折叠成 QQ 群聊的 “转发消息”,以防止刷屏。 + +#### `platform_settings.enable_id_white_list` + +是否启用 ID 白名单。默认为 `true`。启用后,只有在白名单中的 ID 发来的消息才会被处理。 + +#### `platform_settings.id_whitelist` + +ID 白名单。填写后,将只处理所填写的 ID 发来的消息事件。为空时表示不启用白名单过滤。可以使用 `/sid` 指令获取在某个平台上的会话 ID。 + +也可在 AstrBot 日志内获取会话 ID,当一条消息没通过白名单时,会输出 INFO 级别的日志,格式类似 `aiocqhttp:GroupMessage:547540978` + +#### `platform_settings.id_whitelist_log` + +是否打印未通过 ID 白名单的消息日志。默认为 `true`。 + +#### `platform_settings.wl_ignore_admin_on_group` & `platform_settings.wl_ignore_admin_on_friend` + +- `wl_ignore_admin_on_group`: 是否管理员发送的群组消息无视 ID 白名单。默认为 `true`。 + +- `wl_ignore_admin_on_friend`: 是否管理员发送的私聊消息无视 ID 白名单。默认为 `true`。 + +#### `platform_settings.reply_with_mention` + +是否在回复消息时 @ 提到用户。默认为 `false`。 + +#### `platform_settings.reply_with_quote` + +是否在回复消息时引用用户的消息。默认为 `false`。 + +#### `platform_settings.path_mapping` + +*该配置项已经在 v4.0.0 版本之后被废弃。* + +路径映射列表。用于将消息中的文件路径进行替换。每个映射项包含 `from` 和 `to` 两个字段,表示将消息中的 `from` 路径替换为 `to` 路径。 + +#### `platform_settings.segmented_reply` + +分段回复设置。 + +- `enable`: 是否启用分段回复。默认为 `false`。 +- `only_llm_result`: 是否仅对 LLM 生成的回复进行分段。默认为 `true`。 +- `interval_method`: 分段间隔方法。可选值为 `random` 和 `log`。默认为 `random`。 +- `interval`: 分段间隔时间。对于 `random` 方法,填写两个逗号分隔的数字,表示最小和最大间隔时间(单位:秒)。对于 `log` 方法,填写一个数字,表示对数基底。默认为 `"1.5,3.5"`。 +- `log_base`: 对数基底,仅在 `interval_method` 为 `log` 时适用。默认为 `2.6`。 +- `words_count_threshold`: 分段回复的字数上限。只有字数小于此值的消息才会被分段,超过此值的长消息将直接发送(不分段)。默认为 `150`。 +- `regex`: 用于分隔一段消息。默认情况下会根据句号、问号等标点符号分隔。`re.findall(r'', text)`。默认值为 `".*?[。?!~…]+|.+$"`。 +- `content_cleanup_rule`: 移除分段后的内容中的指定的内容。支持正则表达式。如填写 `[。?!]` 将移除所有的句号、问号、感叹号。`re.sub(r'', '', text)`。 + +#### `platform_settings.no_permission_reply` + +是否在用户没有权限时回复无权限的提示消息。默认为 `true`。 + +#### `platform_settings.empty_mention_waiting` + +是否启用空 @ 等待机制。默认为 `true`。启用后,当用户发送一条仅包含 @ 机器人的消息时,机器人会等待用户在 60 秒内发送下一条消息,并将两条消息合并后进行处理。这在某些平台不支持 @ 和语音/图片等消息同时发送时特别有用。 + +#### `platform_settings.empty_mention_waiting_need_reply` + +在上面一个配置项(`empty_mention_waiting`)中,如果启用了触发等待,启用此项后,机器人会立即使用 LLM 生成一条回复。否则,将不回复而只是等待。默认为 `true`。 + +#### `platform_settings.friend_message_needs_wake_prefix` + +是否在消息平台的私聊消息中需要唤醒前缀。默认为 `false`。启用后,在私聊消息中,用户需要使用唤醒前缀才能触发机器人的响应。 + +#### `platform_settings.ignore_bot_self_message` + +是否忽略机器人自己发送的消息。默认为 `false`。启用后,机器人将不会处理自己发送的消息,在某些平台可以防止死循环。 + +#### `platform_settings.ignore_at_all` + +是否忽略 @ 全体成员的消息。默认为 `false`。启用后,机器人将不会响应包含 @ 全体成员的消息。 + +### `provider` + +> 此配置项仅在 `data/cmd_config.json` 中生效,AstrBot 不会读取 `data/config/` 目录下的配置文件中的此项。 + +已配置的模型服务提供商的配置列表。 + +### `provider_settings` + +大语言模型提供商的通用设置。 + +#### `provider_settings.enable` + +是否启用大语言模型聊天。默认为 `true`。 + +#### `provider_settings.default_provider_id` + +默认的对话模型提供商 ID。必须是 `provider` 列表中已配置的提供商 ID。如果为空,则使用配置列表中的第一个对话模型提供商。 + +#### `provider_settings.default_image_caption_provider_id` + +默认的图像描述模型提供商 ID。必须是 `provider` 列表中已配置的提供商 ID。如果为空,则代表不使用图像描述功能。 + +此配置项的意思是,当用户发送一张图片时,AstrBot 会使用此提供商来生成对图片的描述文本,并将描述文本作为对话的上下文之一。这在对话模型不支持多模态输入时特别有用。 + +#### `provider_settings.image_caption_prompt` + +图像描述的提示词模板。默认为 `"Please describe the image using Chinese."`。 + +#### `provider_settings.provider_pool` + +*此配置项尚未实际使用* + +#### `provider_settings.wake_prefix` + +使用 LLM 聊天额外的触发条件。如填写 `chat`,则需要发送消息时要以 `/chat` 才能触发 LLM 聊天。其中 `/` 是机器人的唤醒前缀。是一个防止滥用的手段。 + +#### `provider_settings.web_search` + +是否启用 AstrBot 自带的网页搜索能力。默认为 `false`。启用后,LLM 可能会自动搜索网页并根据内容回答。 + +#### `provider_settings.websearch_provider` + +网页搜索提供商类型。默认为 `default`。目前支持 `default` 和 `tavily`。 + +- `default`:能访问 Google 时效果最佳。如果 Google 访问失败,程序会依次访问 Bing, Sogo 搜索引擎。 + +- `tavily`:使用 Tavily 搜索引擎。 + +#### `provider_settings.websearch_tavily_key` + +Tavily 搜索引擎的 API Key 列表。使用 `tavily` 作为网页搜索提供商时需要填写。 + +#### `provider_settings.web_search_link` + +是否在回复中提示模型附上搜索结果的链接。默认为 `false`。 + +#### `provider_settings.display_reasoning_text` + +是否在回复中显示模型的推理过程。默认为 `false`。 + +#### `provider_settings.identifier` + +是否在 Prompt 前加上群成员的名字以让模型更好地了解群聊状态。默认为 `false`。启用将略微增加 token 开销。 + +#### `provider_settings.group_name_display` + +是否在提示模型了解所在群的名称。默认为 `false`。此配置项目前仅在 QQ 平台适配器中生效。 + +#### `provider_settings.datetime_system_prompt` + +是否在系统提示词中加上当前机器的日期时间。默认为 `true`。 + +#### `provider_settings.default_personality` + +默认使用的人格的 ID。请在 WebUI 配置人格。 + +#### `provider_settings.persona_pool` + +*此配置项尚未实际使用* + +#### `provider_settings.prompt_prefix` + +用户提示词。可使用 `{{prompt}}` 作为用户输入的占位符。如果不输入占位符则代表添加在用户输入的前面。 + +#### `provider_settings.max_context_length` + +当对话上下文超出这个数量时丢弃最旧的部分,一轮聊天记为 1 条。-1 为不限制。 + +#### `provider_settings.dequeue_context_length` + +当触发上面提到的 `max_context_length` 限制时,每次丢弃的对话轮数。 + +#### `provider_settings.streaming_response` + +是否启用流式响应。默认为 `false`。启用后,模型的回复会实时类似打字机的效果发送给用户。此配置项仅在 WebChat、Telegram、飞书平台生效。 + +#### `provider_settings.show_tool_use_status` + +是否显示工具使用状态。默认为 `false`。启用后,模型在使用工具时会显示工具的名称和输入参数。 + +#### `provider_settings.streaming_segmented` + +不支持流式响应的消息平台是否降级为使用分段回复。默认为 `false`。意思是,如果启用了流式响应,但当前消息平台不支持流式响应,那么是否使用分段多次回复来代替。 + +#### `provider_settings.max_agent_step` + +Agent 最大步骤数限制。默认为 `30`。模型的每次工具调用算作一步。 + +#### `provider_settings.tool_call_timeout` + +Added in `v4.3.5` + +工具调用的最大超时时间(秒),默认为 `60` 秒。 + +#### `provider_stt_settings` + +语音转文本服务提供商的通用设置。 + +#### `provider_stt_settings.enable` + +是否启用语音转文本服务。默认为 `false`。 + +#### `provider_stt_settings.provider_id` + +语音转文本服务提供商 ID。必须是 `provider` 列表中已配置的 STT 提供商 ID。 + +#### `provider_tts_settings` + +文本转语音服务提供商的通用设置。 + +#### `provider_tts_settings.enable` + +是否启用文本转语音服务。默认为 `false`。 + +#### `provider_tts_settings.provider_id` + +文本转语音服务提供商 ID。必须是 `provider` 列表中已配置的 TTS 提供商 ID。 + +#### `provider_tts_settings.dual_output` + +是否启用双输出。默认为 `false`。启用后,机器人会同时发送文本和语音消息。 + +#### `provider_tts_settings.use_file_service` + +是否启用文件服务。默认为 `false`。启用后,机器人会将输出的语音文件以 HTTP 文件外链的形式提供给消息平台。此配置项依赖于 `callback_api_base` 的配置。 + +#### `provider_ltm_settings` + +群聊上下文感知服务提供商的通用设置。 + +#### `provider_ltm_settings.group_icl_enable` + +是否启用群聊上下文感知。默认为 `false`。启用后,机器人会记录群聊中的对话内容,以便更好地理解群聊的上下文。 + +上下文的内容会被放在对话的系统提示词中。 + +#### `provider_ltm_settings.group_message_max_cnt` + +群聊消息的最大记录数量。默认为 `100`。超过此数量的消息将被丢弃。 + +#### `provider_ltm_settings.image_caption` + +是否记录群聊中的图片,并自动使用图像描述模型生成图片的描述文本。默认为 `false`。此配置项依赖于 `provider_settings.default_image_caption_provider_id` 的配置。请谨慎使用,因为这可能会增加大量的 API 调用和 token 开销。 + +#### `provider_ltm_settings.active_reply` + +- `enable`: 是否启用主动回复。默认为 `false`。 +- `method`: 主动回复的方法。可选值为 `possibility_reply`。 +- `possibility_reply`: 主动回复的概率。默认为 `0.1`。仅在 `method` 为 `possibility_reply` 时适用。 +- `whitelist`: 主动回复的 ID 白名单。仅在此列表中的 ID 才会触发主动回复。为空时表示不启用白名单过滤。可以使用 `/sid` 指令获取在某个平台上的会话 ID。 + +### `content_safety` + +内容安全设置。 + +#### `content_safety.also_use_in_response` + +是否在 LLM 回复中也进行内容安全检查。默认为 `false`。启用后,机器人生成的回复也会经过内容安全检查,以防止生成不当内容。 + +#### `content_safety.internal_keywords` + +内部关键词检测设置。 + +- `enable`: 是否启用内部关键词检测。默认为 `true`。 +- `extra_keywords`: 额外的关键词列表,支持正则表达式。默认为空。 + +#### `content_safety.baidu_aip` + +百度 AI 内容审核设置。 + +- `enable`: 是否启用百度 AI 内容审核。默认为 `false`。 +- `app_id`: 百度 AI 内容审核的 App ID。 +- `api_key`: 百度 AI 内容审核的 API Key。 +- `secret_key`: 百度 AI 内容审核的 Secret Key。 + +> [!TIP] +> 如果要启用百度 AI 内容审核,请先 `pip install baidu-aip`。 + +### `admins_id` + +管理员 ID 列表。此外,还可以使用 `/op`, `/deop` 指令来添加或删除管理员。 + +### `t2i` + +是否启用文本转图像功能。默认为 `false`。启用后,当用户发送的消息超过一定字数时,机器人会将消息渲染成图片发送给用户,以提高可读性并防止刷屏。支持 Markdown 渲染。 + +### `t2i_word_threshold` + +文本转图像的字数阈值。默认为 `150`。当用户发送的消息超过此字数时,机器人会将消息渲染成图片发送给用户。 + +### `t2i_strategy` + +文本转图像的渲染策略。可选值为 `local` 和 `remote`。默认为 `remote`。 + +- `local`: 使用 AstrBot 本地的文本转图像服务进行渲染。效果较差,但不依赖外部服务。 +- `remote`: 使用远程的文本转图像服务进行渲染。默认使用 AstrBot 官方提供的服务,效果较好。 + +### `t2i_endpoint` + +AstrBot API 的地址。用于渲染 Markdown 图片。当 `t2i_strategy` 为 `remote` 时生效。默认为空,表示使用 AstrBot 官方提供的服务。 + +### `t2i_use_file_service` + +是否启用文件服务。默认为 `false`。启用后,机器人会将渲染的图片以 HTTP 文件外链的形式提供给消息平台。此配置项依赖于 `callback_api_base` 的配置。 + +### `http_proxy` + +HTTP 代理。如 `http://localhost:7890`。 + +### `no_proxy` + +不使用代理的地址列表。如 `["localhost", "127.0.0.1"]`。 + +### `dashboard` + +AstrBot WebUI 配置。 + +请不要随意修改 `password` 的值。它是一个经过 `md5` 编码的密码。请在控制面板修改密码。 + +- `enable`: 是否启用 AstrBot WebUI。默认为 `true`。 +- `username`: AstrBot WebUI 的用户名。默认为 `astrbot`。 +- `password`: AstrBot WebUI 的密码。默认为 `astrbot` 的 `md5` 编码值。请勿直接修改,除非您知道自己在做什么。 +- `jwt_secret`: JWT 的密钥。AstrBot 会在初始化时随机生成。请勿修改,除非您知道自己在做什么。 +- `host`: AstrBot WebUI 监听的地址。默认为 `0.0.0.0`。 +- `port`: AstrBot WebUI 监听的端口。默认为 `6185`。 + +### `platform` + +> 此配置项仅在 `data/cmd_config.json` 中生效,AstrBot 不会读取 `data/config/` 目录下的配置文件中的此项。 + +已配置的 AstrBot 消息平台适配器的配置列表。 + +### `platform_specific` + +平台特异配置。按平台分类,平台下按功能分组。 + +#### `platform_specific..pre_ack_emoji` + +启用后,当请求 LLM 前,AstrBot 会先发送一个预回复的表情以告知用户正在处理请求。此功能目前仅在飞书平台适配器和 Telegram 中生效。 + +##### lark (飞书) + +- `enable`: 是否启用飞书消息预回复表情。默认为 `false`。 +- `emojis`: 预回复的表情列表。默认为 `["Typing"]`。表情枚举名参考:[表情文案说明](https://open.feishu.cn/document/server-docs/im-v1/message-reaction/emojis-introduce) + +#### telegram + +- `enable`: 是否启用 Telegram 消息预回复表情。默认为 `false`。 +- `emojis`: 预回复的表情列表。默认为 `["✍️"]`。Telegram 仅支持固定反应集合,参考:[reactions.txt](https://gist.github.com/Soulter/3f22c8e5f9c7e152e967e8bc28c97fc9) + +### `wake_prefix` + +唤醒前缀。默认为 `/`。当消息以 `/` 开头时,AstrBot 会被唤醒。 + +> [!TIP] +> 如果唤醒的会话不在 ID 白名单中,AstrBot 将不会响应。 + +### `log_level` + +日志级别。默认为 `INFO`。可以设置为 `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`。 + +### `trace_enable` + +是否启用追踪记录。默认为 `false`。启用后,AstrBot 会记录运行追踪信息,可以在管理面板的 Trace 页面查看。 + +### `pip_install_arg` + +`pip install` 的参数。如 `-i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple`。 + +### `pypi_index_url` + +PyPI 镜像源地址。默认为 `https://mirrors.aliyun.com/pypi/simple/`。 + +### `persona` + +*此配置项已经在 v4.0.0 版本之后被废弃。请使用 WebUI 来配置人格。* + +已配置的人格列表。每个人格包含 `id`, `name`, `description`, `system_prompt` 四个字段。 + +### `timezone` + +时区设置。请填写 IANA 时区名称, 如 Asia/Shanghai, 为空时使用系统默认时区。所有时区请查看: [IANA Time Zone Database](https://data.iana.org/time-zones/tzdb-2021a/zone1970.tab)。 + +### `callback_api_base` + +AstrBot API 的基础地址。用于文件服务和插件回调等功能。如 `http://example.com:6185`。默认为空,表示不启用文件服务和插件回调功能。 + +### `default_kb_collection` + +默认知识库名称。用于 RAG 功能。如果为空,则不使用知识库。 + +### `plugin_set` + +已启用的插件列表。`*` 表示启用所有可用的插件。默认为 `["*"]`。 diff --git a/docs/zh/dev/openapi.md b/docs/zh/dev/openapi.md new file mode 100644 index 000000000..4ac8f84e9 --- /dev/null +++ b/docs/zh/dev/openapi.md @@ -0,0 +1,150 @@ +--- +outline: deep +--- + +# AstrBot HTTP API + +从 v4.18.0 开始,AstrBot 提供基于 API Key 的 HTTP API,开发者可以通过标准 HTTP 请求访问核心能力。 + +## 快速开始 + +1. 在 WebUI - 设置中创建 API Key。 +2. 在请求头中携带 API Key: + +```http +Authorization: Bearer abk_xxx +``` + +也支持: + +```http +X-API-Key: abk_xxx +``` + +3. 对于对话接口,`username` 为必填参数: + +- `POST /api/v1/chat`:请求体必须包含 `username` +- `GET /api/v1/chat/sessions`:查询参数必须包含 `username` + +## Scope 权限说明 + +创建 API Key 时可配置 `scopes`。每个 scope 控制可访问的接口范围: + +| Scope | 作用 | 可访问接口 | +| --- | --- | --- | +| `chat` | 调用对话能力、查询对话会话 | `POST /api/v1/chat`、`GET /api/v1/chat/sessions` | +| `config` | 获取可用配置文件列表 | `GET /api/v1/configs` | +| `file` | 上传附件文件,获取 `attachment_id` | `POST /api/v1/file` | +| `im` | 主动发 IM 消息、查询 bot/platform 列表 | `POST /api/v1/im/message`、`GET /api/v1/im/bots` | + +如果 API Key 未包含目标接口所需 scope,请求会返回 `403 Insufficient API key scope`。 + +## 常用接口 + +**对话类** + +调用 AstrBot 内建的 Agent 进行对话交互。支持插件调用、工具调用等能力,与 IM 端对话能力一致。 + +- `POST /api/v1/chat`:发送对话消息(SSE 流式返回,不传 `session_id` 会自动创建 UUID) +- `GET /api/v1/chat/sessions`:分页获取指定 `username` 的会话 +- `GET /api/v1/configs`:获取可用配置文件列表 + +**文件上传** + +- `POST /api/v1/file`:上传附件 + +**IM 消息发送** + +- `POST /api/v1/im/message`:按 UMO 主动发消息 +- `GET /api/v1/im/bots`:获取 bot/platform ID 列表 + +## `message` 字段格式(重点) + +`POST /api/v1/chat` 和 `POST /api/v1/im/message` 的 `message` 字段支持两种格式: + +1. 字符串:纯文本消息 +2. 数组:消息段(message chain) + +### 1. 纯文本格式 + +```json +{ + "message": "Hello" +} +``` + +### 2. 消息段数组格式 + +```json +{ + "message": [ + { "type": "plain", "text": "请看这个文件" }, + { "type": "file", "attachment_id": "9a2f8c72-e7af-4c0e-b352-111111111111" } + ] +} +``` + +支持的 `type`: + +| type | 必填字段 | 可选字段 | 说明 | +| --- | --- | --- | --- | +| `plain` | `text` | - | 文本段 | +| `reply` | `message_id` | `selected_text` | 引用回复某条消息 | +| `image` | `attachment_id` | - | 图片附件段 | +| `record` | `attachment_id` | - | 音频附件段 | +| `file` | `attachment_id` | - | 通用文件段 | +| `video` | `attachment_id` | - | 视频附件段 | + +* reply 消息段目前仅适配 `/api/v1/chat`,不适用于 `POST /api/v1/im/message`。 + + +说明: + +- `attachment_id` 来自 `POST /api/v1/file` 上传结果。 +- `reply` 不能单独作为唯一内容,至少需要一个有实际内容的段(如 `plain/image/file/...`)。 +- 仅 `reply` 或空内容会返回错误。 + +### Chat API 的 `message` 用法 + +`POST /api/v1/chat` 额外需要 `username`,可选 `session_id`(不传会自动创建 UUID)。 + +```json +{ + "username": "alice", + "session_id": "my_session_001", + "message": [ + { "type": "plain", "text": "帮我总结这个 PDF" }, + { "type": "file", "attachment_id": "9a2f8c72-e7af-4c0e-b352-111111111111" } + ], + "enable_streaming": true +} +``` + +### IM Message API 的 `message` 用法 + +`POST /api/v1/im/message` 需要 `umo` + `message`。 + +```json +{ + "umo": "webchat:FriendMessage:openapi_probe", + "message": [ + { "type": "plain", "text": "这是主动消息" }, + { "type": "image", "attachment_id": "9a2f8c72-e7af-4c0e-b352-222222222222" } + ] +} +``` + +## 示例 + +```bash +curl -N 'http://localhost:6185/api/v1/chat' \ + -H 'Authorization: Bearer abk_xxx' \ + -H 'Content-Type: application/json' \ + -d '{"message":"Hello","username":"alice"}' +``` + +## 完整 API 文档 + +交互式 API 文档请查看: + +- https://docs.astrbot.app/scalar.html diff --git a/docs/zh/dev/plugin-platform-adapter.md b/docs/zh/dev/plugin-platform-adapter.md new file mode 100644 index 000000000..8e65528e2 --- /dev/null +++ b/docs/zh/dev/plugin-platform-adapter.md @@ -0,0 +1,185 @@ +--- +outline: deep +--- + +# 开发一个平台适配器 + +AstrBot 支持以插件的形式接入平台适配器,你可以自行接入 AstrBot 没有的平台。如飞书、钉钉甚至是哔哩哔哩私信、Minecraft。 + +我们以一个平台 `FakePlatform` 为例展开讲解。 + +首先,在插件目录下新增 `fake_platform_adapter.py` 和 `fake_platform_event.py` 文件。前者主要是平台适配器的实现,后者是平台事件的定义。 + +## 平台适配器 + +假设 FakePlatform 的客户端 SDK 是这样: + +```py +import asyncio + +class FakeClient(): + '''模拟一个消息平台,这里 5 秒钟下发一个消息''' + def __init__(self, token: str, username: str): + self.token = token + self.username = username + # ... + + async def start_polling(self): + while True: + await asyncio.sleep(5) + await getattr(self, 'on_message_received')({ + 'bot_id': '123', + 'content': '新消息', + 'username': 'zhangsan', + 'userid': '123', + 'message_id': 'asdhoashd', + 'group_id': 'group123', + }) + + async def send_text(self, to: str, message: str): + print('发了消息:', to, message) + + async def send_image(self, to: str, image_path: str): + print('发了消息:', to, image_path) +``` + +我们创建 `fake_platform_adapter.py`: + +```py +import asyncio + +from astrbot.api.platform import Platform, AstrBotMessage, MessageMember, PlatformMetadata, MessageType +from astrbot.api.event import MessageChain +from astrbot.api.message_components import Plain, Image, Record # 消息链中的组件,可以根据需要导入 +from astrbot.core.platform.astr_message_event import MessageSesion +from astrbot.api.platform import register_platform_adapter +from astrbot import logger +from .client import FakeClient +from .fake_platform_event import FakePlatformEvent + +# 注册平台适配器。第一个参数为平台名,第二个为描述。第三个为默认配置。 +@register_platform_adapter("fake", "fake 适配器", default_config_tmpl={ + "token": "your_token", + "username": "bot_username" +}) +class FakePlatformAdapter(Platform): + + def __init__(self, platform_config: dict, platform_settings: dict, event_queue: asyncio.Queue) -> None: + super().__init__(event_queue) + self.config = platform_config # 上面的默认配置,用户填写后会传到这里 + self.settings = platform_settings # platform_settings 平台设置。 + + async def send_by_session(self, session: MessageSesion, message_chain: MessageChain): + # 必须实现 + await super().send_by_session(session, message_chain) + + def meta(self) -> PlatformMetadata: + # 必须实现,直接像下面一样返回即可。 + return PlatformMetadata( + "fake", + "fake 适配器", + ) + + async def run(self): + # 必须实现,这里是主要逻辑。 + + # FakeClient 是我们自己定义的,这里只是示例。这个是其回调函数 + async def on_received(data): + logger.info(data) + abm = await self.convert_message(data=data) # 转换成 AstrBotMessage + await self.handle_msg(abm) + + # 初始化 FakeClient + self.client = FakeClient(self.config['token'], self.config['username']) + self.client.on_message_received = on_received + await self.client.start_polling() # 持续监听消息,这是个堵塞方法。 + + async def convert_message(self, data: dict) -> AstrBotMessage: + # 将平台消息转换成 AstrBotMessage + # 这里就体现了适配程度,不同平台的消息结构不一样,这里需要根据实际情况进行转换。 + abm = AstrBotMessage() + abm.type = MessageType.GROUP_MESSAGE # 还有 friend_message,对应私聊。具体平台具体分析。重要! + abm.group_id = data['group_id'] # 如果是私聊,这里可以不填 + abm.message_str = data['content'] # 纯文本消息。重要! + abm.sender = MessageMember(user_id=data['userid'], nickname=data['username']) # 发送者。重要! + abm.message = [Plain(text=data['content'])] # 消息链。如果有其他类型的消息,直接 append 即可。重要! + abm.raw_message = data # 原始消息。 + abm.self_id = data['bot_id'] + abm.session_id = data['userid'] # 会话 ID。重要! + abm.message_id = data['message_id'] # 消息 ID。 + + return abm + + async def handle_msg(self, message: AstrBotMessage): + # 处理消息 + message_event = FakePlatformEvent( + message_str=message.message_str, + message_obj=message, + platform_meta=self.meta(), + session_id=message.session_id, + client=self.client + ) + self.commit_event(message_event) # 提交事件到事件队列。不要忘记! +``` + + +`fake_platform_event.py`: + +```py +from astrbot.api.event import AstrMessageEvent, MessageChain +from astrbot.api.platform import AstrBotMessage, PlatformMetadata +from astrbot.api.message_components import Plain, Image +from .client import FakeClient +from astrbot.core.utils.io import download_image_by_url + +class FakePlatformEvent(AstrMessageEvent): + def __init__(self, message_str: str, message_obj: AstrBotMessage, platform_meta: PlatformMetadata, session_id: str, client: FakeClient): + super().__init__(message_str, message_obj, platform_meta, session_id) + self.client = client + + async def send(self, message: MessageChain): + for i in message.chain: # 遍历消息链 + if isinstance(i, Plain): # 如果是文字类型的 + await self.client.send_text(to=self.get_sender_id(), message=i.text) + elif isinstance(i, Image): # 如果是图片类型的 + img_url = i.file + img_path = "" + # 下面的三个条件可以直接参考一下。 + if img_url.startswith("file:///"): + img_path = img_url[8:] + elif i.file and i.file.startswith("http"): + img_path = await download_image_by_url(i.file) + else: + img_path = img_url + + # 请善于 Debug! + + await self.client.send_image(to=self.get_sender_id(), image_path=img_path) + + await super().send(message) # 需要最后加上这一段,执行父类的 send 方法。 +``` + +最后,main.py 只需这样,在初始化的时候导入 fake_platform_adapter 模块。装饰器会自动注册。 + +```py +from astrbot.api.star import Context, Star + +class MyPlugin(Star): + def __init__(self, context: Context): + from .fake_platform_adapter import FakePlatformAdapter # noqa +``` + +搞好后,运行 AstrBot: + +![image](https://files.astrbot.app/docs/source/images/plugin-platform-adapter/QQ_1738155926221.png) + +这里出现了我们创建的 fake。 + +![image](https://files.astrbot.app/docs/source/images/plugin-platform-adapter/QQ_1738155982211.png) + +启动后,可以看到正常工作: + +![image](https://files.astrbot.app/docs/source/images/plugin-platform-adapter/QQ_1738156166893.png) + + +有任何疑问欢迎加群询问~ \ No newline at end of file diff --git a/docs/zh/dev/plugin.md b/docs/zh/dev/plugin.md new file mode 100644 index 000000000..d929443b5 --- /dev/null +++ b/docs/zh/dev/plugin.md @@ -0,0 +1 @@ +本页面已经迁移至 [插件基础开发](/dev/star/plugin)。 \ No newline at end of file diff --git a/docs/zh/dev/star/guides/ai.md b/docs/zh/dev/star/guides/ai.md new file mode 100644 index 000000000..549275ace --- /dev/null +++ b/docs/zh/dev/star/guides/ai.md @@ -0,0 +1,553 @@ + +# AI + +AstrBot 内置了对多种大语言模型(LLM)提供商的支持,并且提供了统一的接口,方便插件开发者调用各种 LLM 服务。 + +您可以使用 AstrBot 提供的 LLM / Agent 接口来实现自己的智能体。 + +我们在 `v4.5.7` 版本之后对 LLM 提供商的调用方式进行了较大调整,推荐使用新的调用方式。新的调用方式更加简洁,并且支持更多的功能。当然,您仍然可以使用[旧的调用方式](/dev/star/plugin#ai)。 + +## 获取当前会话使用的聊天模型 ID + +> [!TIP] +> 在 v4.5.7 时加入 + +```py +umo = event.unified_msg_origin +provider_id = await self.context.get_current_chat_provider_id(umo=umo) +``` + +## 调用大模型 + +> [!TIP] +> 在 v4.5.7 时加入 + +```py +llm_resp = await self.context.llm_generate( + chat_provider_id=provider_id, # 聊天模型 ID + prompt="Hello, world!", +) +# print(llm_resp.completion_text) # 获取返回的文本 +``` + +## 定义 Tool + +Tool 是大语言模型调用外部工具的能力。 + +```py +from pydantic import Field +from pydantic.dataclasses import dataclass + +from astrbot.core.agent.run_context import ContextWrapper +from astrbot.core.agent.tool import FunctionTool, ToolExecResult +from astrbot.core.astr_agent_context import AstrAgentContext + + +@dataclass +class BilibiliTool(FunctionTool[AstrAgentContext]): + name: str = "bilibili_videos" # 工具名称 + description: str = "A tool to fetch Bilibili videos." # 工具描述 + parameters: dict = Field( + default_factory=lambda: { + "type": "object", + "properties": { + "keywords": { + "type": "string", + "description": "Keywords to search for Bilibili videos.", + }, + }, + "required": ["keywords"], + } + ) + + async def call( + self, context: ContextWrapper[AstrAgentContext], **kwargs + ) -> ToolExecResult: + return "1. 视频标题:如何使用AstrBot\n视频链接:xxxxxx" +``` + +## 注册 Tool 到 AstrBot + +在上面定义好 Tool 之后,如果你需要实现的功能是让用户在使用 AstrBot 进行对话时自动调用该 Tool,那么你需要在插件的 __init__ 方法中将 Tool 注册到 AstrBot 中: + +```py +class MyPlugin(Star): + def __init__(self, context: Context): + super().__init__(context) + # >= v4.5.1 使用: + self.context.add_llm_tools(BilibiliTool(), SecondTool(), ...) + + # < v4.5.1 之前使用: + tool_mgr = self.context.provider_manager.llm_tools + tool_mgr.func_list.append(BilibiliTool()) +``` + +### 通过装饰器定义 Tool 和注册 Tool + +除了上述的通过 `@dataclass` 定义 Tool 的方式之外,你也可以使用装饰器的方式注册 tool 到 AstrBot。如果请务必按照以下格式编写一个工具(包括函数注释,AstrBot 会解析该函数注释,请务必将注释格式写对) + +```py{3,4,5,6,7} +@filter.llm_tool(name="get_weather") # 如果 name 不填,将使用函数名 +async def get_weather(self, event: AstrMessageEvent, location: str) -> MessageEventResult: + '''获取天气信息。 + + Args: + location(string): 地点 + ''' + resp = self.get_weather_from_api(location) + yield event.plain_result("天气信息: " + resp) +``` + +在 `location(string): 地点` 中,`location` 是参数名,`string` 是参数类型,`地点` 是参数描述。 + +支持的参数类型有 `string`, `number`, `object`, `boolean`, `array`。在 v4.5.7 之后,支持对 `array` 类型参数指定子类型,例如 `array[string]`。 + +## 调用 Agent + +> [!TIP] +> 在 v4.5.7 时加入 + +Agent 可以被定义为 system_prompt + tools + llm 的结合体,可以实现更复杂的智能体行为。 + +在上面定义好 Tool 之后,可以通过以下方式调用 Agent: + +```py +llm_resp = await self.context.tool_loop_agent( + event=event, + chat_provider_id=prov_id, + prompt="搜索一下 bilibili 上关于 AstrBot 的相关视频。", + tools=ToolSet([BilibiliTool()]), + max_steps=30, # Agent 最大执行步骤 + tool_call_timeout=60, # 工具调用超时时间 +) +# print(llm_resp.completion_text) # 获取返回的文本 +``` + +`tool_loop_agent()` 方法会自动处理工具调用和大模型请求的循环,直到大模型不再调用工具或者达到最大步骤数为止。 + +## Multi-Agent + +> [!TIP] +> 在 v4.5.7 时加入 + +Multi-Agent(多智能体)系统将复杂应用分解为多个专业化智能体,它们协同解决问题。不同于依赖单个智能体处理每一步,多智能体架构允许将更小、更专注的智能体组合成协调的工作流程。我们使用 `agent-as-tool` 模式来实现多智能体系统。 + +在下面的例子中,我们定义了一个主智能体(Main Agent),它负责根据用户查询将任务分配给不同的子智能体(Sub-Agents)。每个子智能体专注于特定任务,例如获取天气信息。 + +![multi-agent-example-1](https://files.astrbot.app/docs/zh/dev/star/guides/multi-agent-example-1.svg) + +定义 Tools: + +```py +from pydantic import Field +from pydantic.dataclasses import dataclass + +from astrbot.core.agent.run_context import ContextWrapper +from astrbot.core.agent.tool import FunctionTool, ToolExecResult +from astrbot.core.astr_agent_context import AstrAgentContext + +@dataclass +class AssignAgentTool(FunctionTool[AstrAgentContext]): + """Main agent uses this tool to decide which sub-agent to delegate a task to.""" + + name: str = "assign_agent" + description: str = "Assign an agent to a task based on the given query" + parameters: dict = Field( + default_factory=lambda: { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "The query to call the sub-agent with.", + }, + }, + "required": ["query"], + } + ) + + async def call( + self, context: ContextWrapper[AstrAgentContext], **kwargs + ) -> ToolExecResult: + # Here you would implement the actual agent assignment logic. + # For demonstration purposes, we'll return a dummy response. + return "Based on the query, you should assign agent 1." + + +@dataclass +class WeatherTool(FunctionTool[AstrAgentContext]): + """In this example, sub agent 1 uses this tool to get weather information.""" + + name: str = "weather" + description: str = "Get weather information for a location" + parameters: dict = Field( + default_factory=lambda: { + "type": "object", + "properties": { + "city": { + "type": "string", + "description": "The city to get weather information for.", + }, + }, + "required": ["city"], + } + ) + + async def call( + self, context: ContextWrapper[AstrAgentContext], **kwargs + ) -> ToolExecResult: + city = kwargs["city"] + # Here you would implement the actual weather fetching logic. + # For demonstration purposes, we'll return a dummy response. + return f"The current weather in {city} is sunny with a temperature of 25°C." + + +@dataclass +class SubAgent1(FunctionTool[AstrAgentContext]): + """Define a sub-agent as a function tool.""" + + name: str = "subagent1_name" + description: str = "subagent1_description" + parameters: dict = Field( + default_factory=lambda: { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "The query to call the sub-agent with.", + }, + }, + "required": ["query"], + } + ) + + async def call( + self, context: ContextWrapper[AstrAgentContext], **kwargs + ) -> ToolExecResult: + ctx = context.context.context + event = context.context.event + logger.info(f"the llm context messages: {context.messages}") + llm_resp = await ctx.tool_loop_agent( + event=event, + chat_provider_id=await ctx.get_current_chat_provider_id( + event.unified_msg_origin + ), + prompt=kwargs["query"], + tools=ToolSet([WeatherTool()]), + max_steps=30, + ) + return llm_resp.completion_text + + +@dataclass +class SubAgent2(FunctionTool[AstrAgentContext]): + """Define a sub-agent as a function tool.""" + + name: str = "subagent2_name" + description: str = "subagent2_description" + parameters: dict = Field( + default_factory=lambda: { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "The query to call the sub-agent with.", + }, + }, + "required": ["query"], + } + ) + + async def call( + self, context: ContextWrapper[AstrAgentContext], **kwargs + ) -> ToolExecResult: + return "I am useless :(, you shouldn't call me :(" +``` + +然后,同样地,通过 `tool_loop_agent()` 方法调用 Agent: + +```py +@filter.command("test") +async def test(self, event: AstrMessageEvent): + umo = event.unified_msg_origin + prov_id = await self.context.get_current_chat_provider_id(umo) + llm_resp = await self.context.tool_loop_agent( + event=event, + chat_provider_id=prov_id, + prompt="Test calling sub-agent for Beijing's weather information.", + system_prompt=( + "You are the main agent. Your task is to delegate tasks to sub-agents based on user queries." + "Before delegating, use the 'assign_agent' tool to determine which sub-agent is best suited for the task." + ), + tools=ToolSet([SubAgent1(), SubAgent2(), AssignAgentTool()]), + max_steps=30, + ) + yield event.plain_result(llm_resp.completion_text) +``` + +## 对话管理器 + +### 获取会话当前的 LLM 对话历史 `get_conversation` + +```py +from astrbot.core.conversation_mgr import Conversation + +uid = event.unified_msg_origin +conv_mgr = self.context.conversation_manager +curr_cid = await conv_mgr.get_curr_conversation_id(uid) +conversation = await conv_mgr.get_conversation(uid, curr_cid) # Conversation +``` + +::: details Conversation 类型定义 + +```py +@dataclass +class Conversation: + """The conversation entity representing a chat session.""" + + platform_id: str + """The platform ID in AstrBot""" + user_id: str + """The user ID associated with the conversation.""" + cid: str + """The conversation ID, in UUID format.""" + history: str = "" + """The conversation history as a string.""" + title: str | None = "" + """The title of the conversation. For now, it's only used in WebChat.""" + persona_id: str | None = "" + """The persona ID associated with the conversation.""" + created_at: int = 0 + """The timestamp when the conversation was created.""" + updated_at: int = 0 + """The timestamp when the conversation was last updated.""" +``` + +::: + +### 快速添加 LLM 记录到对话 `add_message_pair` + +```py +from astrbot.core.agent.message import ( + AssistantMessageSegment, + UserMessageSegment, + TextPart, +) + +curr_cid = await conv_mgr.get_curr_conversation_id(event.unified_msg_origin) +user_msg = UserMessageSegment(content=[TextPart(text="hi")]) +llm_resp = await self.context.llm_generate( + chat_provider_id=provider_id, # 聊天模型 ID + contexts=[user_msg], # 当未指定 prompt 时,使用 contexts 作为输入;同时指定 prompt 和 contexts 时,prompt 会被添加到 LLM 输入的最后 +) +await conv_mgr.add_message_pair( + cid=curr_cid, + user_message=user_msg, + assistant_message=AssistantMessageSegment( + content=[TextPart(text=llm_resp.completion_text)] + ), +) +``` + +### 主要方法 + +#### `new_conversation` + +- __Usage__ + 在当前会话中新建一条对话,并自动切换为该对话。 +- __Arguments__ + - `unified_msg_origin: str` – 形如 `platform_name:message_type:session_id` + - `platform_id: str | None` – 平台标识,默认从 `unified_msg_origin` 解析 + - `content: list[dict] | None` – 初始历史消息 + - `title: str | None` – 对话标题 + - `persona_id: str | None` – 绑定的 persona ID +- __Returns__ + `str` – 新生成的 UUID 对话 ID + +#### `switch_conversation` + +- __Usage__ + 将会话切换到指定的对话。 +- __Arguments__ + - `unified_msg_origin: str` + - `conversation_id: str` +- __Returns__ + `None` + +#### `delete_conversation` + +- __Usage__ + 删除会话中的某条对话;若 `conversation_id` 为 `None`,则删除当前对话。 +- __Arguments__ + - `unified_msg_origin: str` + - `conversation_id: str | None` +- __Returns__ + `None` + +#### `get_curr_conversation_id` + +- __Usage__ + 获取当前会话正在使用的对话 ID。 +- __Arguments__ + - `unified_msg_origin: str` +- __Returns__ + `str | None` – 当前对话 ID,不存在时返回 `None` + +#### `get_conversation` + +- __Usage__ + 获取指定对话的完整对象;若不存在且 `create_if_not_exists=True` 则自动创建。 +- __Arguments__ + - `unified_msg_origin: str` + - `conversation_id: str` + - `create_if_not_exists: bool = False` +- __Returns__ + `Conversation | None` + +#### `get_conversations` + +- __Usage__ + 拉取用户或平台下的全部对话列表。 +- __Arguments__ + - `unified_msg_origin: str | None` – 为 `None` 时不过滤用户 + - `platform_id: str | None` +- __Returns__ + `List[Conversation]` + +#### `update_conversation` + +- __Usage__ + 更新对话的标题、历史记录或 persona_id。 +- __Arguments__ + - `unified_msg_origin: str` + - `conversation_id: str | None` – 为 `None` 时使用当前对话 + - `history: list[dict] | None` + - `title: str | None` + - `persona_id: str | None` +- __Returns__ + `None` + +## 人格设定管理器 + +`PersonaManager` 负责统一加载、缓存并提供所有人格(Persona)的增删改查接口,同时兼容 AstrBot 4.x 之前的旧版人格格式(v3)。 +初始化时会自动从数据库读取全部人格,并生成一份 v3 兼容数据,供旧代码无缝使用。 + +```py +persona_mgr = self.context.persona_manager +``` + +### 主要方法 + +#### `get_persona` + +- __Usage__ + 获取根据人格 ID 获取人格数据。 +- __Arguments__ + - `persona_id: str` – 人格 ID +- __Returns__ + `Persona` – 人格数据,若不存在则返回 None +- __Raises__ + `ValueError` – 当不存在时抛出 + +#### `get_all_personas` + +- __Usage__ + 一次性获取数据库中所有人格。 +- __Returns__ + `list[Persona]` – 人格列表,可能为空 + +#### `create_persona` + +- __Usage__ + 新建人格并立即写入数据库,成功后自动刷新本地缓存。 +- __Arguments__ + - `persona_id: str` – 新人格 ID(唯一) + - `system_prompt: str` – 系统提示词 + - `begin_dialogs: list[str]` – 可选,开场对话(偶数条,user/assistant 交替) + - `tools: list[str]` – 可选,允许使用的工具列表;`None`=全部工具,`[]`=禁用全部 +- __Returns__ + `Persona` – 新建后的人格对象 +- __Raises__ + `ValueError` – 若 `persona_id` 已存在 + +#### `update_persona` + +- __Usage__ + 更新现有人格的任意字段,并同步到数据库与缓存。 +- __Arguments__ + - `persona_id: str` – 待更新的人格 ID + - `system_prompt: str` – 可选,新的系统提示词 + - `begin_dialogs: list[str]` – 可选,新的开场对话 + - `tools: list[str]` – 可选,新的工具列表;语义同 `create_persona` +- __Returns__ + `Persona` – 更新后的人格对象 +- __Raises__ + `ValueError` – 若 `persona_id` 不存在 + +#### `delete_persona` + +- __Usage__ + 删除指定人格,同时清理数据库与缓存。 +- __Arguments__ + - `persona_id: str` – 待删除的人格 ID +- __Raises__ + `Valueable` – 若 `persona_id` 不存在 + +#### `get_default_persona_v3` + +- __Usage__ + 根据当前会话配置,获取应使用的默认人格(v3 格式)。 + 若配置未指定或指定的人格不存在,则回退到 `DEFAULT_PERSONALITY`。 +- __Arguments__ + - `umo: str | MessageSession | None` – 会话标识,用于读取用户级配置 +- __Returns__ + `Personality` – v3 格式的默认人格对象 + +::: details Persona / Personality 类型定义 + +```py + +class Persona(SQLModel, table=True): + """Persona is a set of instructions for LLMs to follow. + + It can be used to customize the behavior of LLMs. + """ + + __tablename__ = "personas" + + id: int = Field(primary_key=True, sa_column_kwargs={"autoincrement": True}) + persona_id: str = Field(max_length=255, nullable=False) + system_prompt: str = Field(sa_type=Text, nullable=False) + begin_dialogs: Optional[list] = Field(default=None, sa_type=JSON) + """a list of strings, each representing a dialog to start with""" + tools: Optional[list] = Field(default=None, sa_type=JSON) + """None means use ALL tools for default, empty list means no tools, otherwise a list of tool names.""" + created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) + updated_at: datetime = Field( + default_factory=lambda: datetime.now(timezone.utc), + sa_column_kwargs={"onupdate": datetime.now(timezone.utc)}, + ) + + __table_args__ = ( + UniqueConstraint( + "persona_id", + name="uix_persona_id", + ), + ) + + +class Personality(TypedDict): + """LLM 人格类。 + + 在 v4.0.0 版本及之后,推荐使用上面的 Persona 类。并且, mood_imitation_dialogs 字段已被废弃。 + """ + + prompt: str + name: str + begin_dialogs: list[str] + mood_imitation_dialogs: list[str] + """情感模拟对话预设。在 v4.0.0 版本及之后,已被废弃。""" + tools: list[str] | None + """工具列表。None 表示使用所有工具,空列表表示不使用任何工具""" +``` + +::: diff --git a/docs/zh/dev/star/guides/env.md b/docs/zh/dev/star/guides/env.md new file mode 100644 index 000000000..7dd0480b9 --- /dev/null +++ b/docs/zh/dev/star/guides/env.md @@ -0,0 +1,48 @@ + +# 开发环境准备 + +## 获取插件模板 + +1. 打开 AstrBot 插件模板: [helloworld](https://github.com/Soulter/helloworld) +2. 点击右上角的 `Use this template` +3. 然后点击 `Create new repository`。 +4. 在 `Repository name` 处填写您的插件名。插件名格式: + - 推荐以 `astrbot_plugin_` 开头; + - 不能包含空格; + - 保持全部字母小写; + - 尽量简短。 +5. 点击右下角的 `Create repository`。 + +![New repo](https://files.astrbot.app/docs/source/images/plugin/image.png) + +## Clone 插件和 AstrBot 项目 + +Clone AstrBot 项目本体和刚刚创建的插件仓库到本地。 + +```bash +git clone https://github.com/AstrBotDevs/AstrBot +mkdir -p AstrBot/data/plugins +cd AstrBot/data/plugins +git clone 插件仓库地址 +``` + +然后,使用 `VSCode` 打开 `AstrBot` 项目。找到 `data/plugins/<你的插件名字>` 目录。 + +更新 `metadata.yaml` 文件,填写插件的元数据信息。 + +> [!NOTE] +> AstrBot 插件市场的信息展示依赖于 `metadata.yaml` 文件。 + +## 调试插件 + +AstrBot 采用在运行时注入插件的机制。因此,在调试插件时,需要启动 AstrBot 本体。 + +您可以使用 AstrBot 的热重载功能简化开发流程。 + +插件的代码修改后,可以在 AstrBot WebUI 的插件管理处找到自己的插件,点击右上角 `...` 按钮,选择 `重载插件`。 + +## 插件依赖管理 + +目前 AstrBot 对插件的依赖管理使用 `pip` 自带的 `requirements.txt` 文件。如果你的插件需要依赖第三方库,请务必在插件目录下创建 `requirements.txt` 文件并写入所使用的依赖库,以防止用户在安装你的插件时出现依赖未找到(Module Not Found)的问题。 + +> `requirements.txt` 的完整格式可以参考 [pip 官方文档](https://pip.pypa.io/en/stable/reference/requirements-file-format/)。 diff --git a/docs/zh/dev/star/guides/html-to-pic.md b/docs/zh/dev/star/guides/html-to-pic.md new file mode 100644 index 000000000..6249f2db1 --- /dev/null +++ b/docs/zh/dev/star/guides/html-to-pic.md @@ -0,0 +1,66 @@ + +# 文转图 + +> [!TIP] +> 为了方便开发,您可以使用 [AstrBot Text2Image Playground](https://t2i-playground.astrbot.app/) 在线可视化编辑和测试 HTML 模板。 + +## 基本 + +AstrBot 支持将文字渲染成图片。 + +```python +@filter.command("image") # 注册一个 /image 指令,接收 text 参数。 +async def on_aiocqhttp(self, event: AstrMessageEvent, text: str): + url = await self.text_to_image(text) # text_to_image() 是 Star 类的一个方法。 + # path = await self.text_to_image(text, return_url = False) # 如果你想保存图片到本地 + yield event.image_result(url) + +``` + +![image](https://files.astrbot.app/docs/source/images/plugin/image-3.png) + +## 自定义(基于 HTML) + +如果你觉得上面渲染出来的图片不够美观,你可以使用自定义的 HTML 模板来渲染图片。 + +AstrBot 支持使用 `HTML + Jinja2` 的方式来渲染文转图模板。 + +```py{7} +# 自定义的 Jinja2 模板,支持 CSS +TMPL = ''' +
+

Todo List

+ +
    +{% for item in items %} +
  • {{ item }}
  • +{% endfor %} +
+''' + +@filter.command("todo") +async def custom_t2i_tmpl(self, event: AstrMessageEvent): + options = {} # 可选择传入渲染选项。 + url = await self.html_render(TMPL, {"items": ["吃饭", "睡觉", "玩原神"]}, options=options) # 第二个参数是 Jinja2 的渲染数据 + yield event.image_result(url) +``` + +返回的结果: + +![image](https://files.astrbot.app/docs/source/images/plugin/fcc2dcb472a91b12899f617477adc5c7.png) + +这只是一个简单的例子。得益于 HTML 和 DOM 渲染器的强大性,你可以进行更复杂和更美观的的设计。除此之外,Jinja2 支持循环、条件等语法以适应列表、字典等数据结构。你可以从网上了解更多关于 Jinja2 的知识。 + +**图片渲染选项(options)**: + +请参考 Playwright 的 [screenshot](https://playwright.dev/python/docs/api/class-page#page-screenshot) API。 + +- `timeout` (float, optional): 截图超时时间. +- `type` (Literal["jpeg", "png"], optional): 截图图片类型. +- `quality` (int, optional): 截图质量,仅适用于 JPEG 格式图片. +- `omit_background` (bool, optional): 是否允许隐藏默认的白色背景,这样就可以截透明图了,仅适用于 PNG 格式 +- `full_page` (bool, optional): 是否截整个页面而不是仅设置的视口大小,默认为 True. +- `clip` (dict, optional): 截图后裁切的区域。参考 Playwright screenshot API。 +- `animations`: (Literal["allow", "disabled"], optional): 是否允许播放 CSS 动画. +- `caret`: (Literal["hide", "initial"], optional): 当设置为 hide 时,截图时将隐藏文本插入符号,默认为 hide. +- `scale`: (Literal["css", "device"], optional): 页面缩放设置. 当设置为 css 时,则将设备分辨率与 CSS 中的像素一一对应,在高分屏上会使得截图变小. 当设置为 device 时,则根据设备的屏幕缩放设置或当前 Playwright 的 Page/Context 中的 device_scale_factor 参数来缩放. diff --git a/docs/zh/dev/star/guides/listen-message-event.md b/docs/zh/dev/star/guides/listen-message-event.md new file mode 100644 index 000000000..8b720c9eb --- /dev/null +++ b/docs/zh/dev/star/guides/listen-message-event.md @@ -0,0 +1,364 @@ + +# 处理消息事件 + +事件监听器可以收到平台下发的消息内容,可以实现指令、指令组、事件监听等功能。 + +事件监听器的注册器在 `astrbot.api.event.filter` 下,需要先导入。请务必导入,否则会和 python 的高阶函数 filter 冲突。 + +```py +from astrbot.api.event import filter, AstrMessageEvent +``` + +## 消息与事件 + +AstrBot 接收消息平台下发的消息,并将其封装为 `AstrMessageEvent` 对象,传递给插件进行处理。 + +![message-event](https://files.astrbot.app/docs/zh/dev/star/guides/message-event.svg) + +### 消息事件 + +`AstrMessageEvent` 是 AstrBot 的消息事件对象,其中存储了消息发送者、消息内容等信息。 + +### 消息对象 + +`AstrBotMessage` 是 AstrBot 的消息对象,其中存储了消息平台下发的消息具体内容,`AstrMessageEvent` 对象中包含一个 `message_obj` 属性用于获取该消息对象。 + +```py{11} +class AstrBotMessage: + '''AstrBot 的消息对象''' + type: MessageType # 消息类型 + self_id: str # 机器人的识别id + session_id: str # 会话id。取决于 unique_session 的设置。 + message_id: str # 消息id + group_id: str = "" # 群组id,如果为私聊,则为空 + sender: MessageMember # 发送者 + message: List[BaseMessageComponent] # 消息链。比如 [Plain("Hello"), At(qq=123456)] + message_str: str # 最直观的纯文本消息字符串,将消息链中的 Plain 消息(文本消息)连接起来 + raw_message: object + timestamp: int # 消息时间戳 +``` + +其中,`raw_message` 是消息平台适配器的**原始消息对象**。 + +### 消息链 + +![message-chain](https://files.astrbot.app/docs/zh/dev/star/guides/message-chain.svg) + +`消息链`描述一个消息的结构,是一个有序列表,列表中每一个元素称为`消息段`。 + +常见的消息段类型有: + +- `Plain`:文本消息段 +- `At`:提及消息段 +- `Image`:图片消息段 +- `Record`:语音消息段 +- `Video`:视频消息段 +- `File`:文件消息段 + +大多数消息平台都支持上面的消息段类型。 + +此外,OneBot v11 平台(QQ 个人号等)还支持以下较为常见的消息段类型: + +- `Face`:表情消息段 +- `Node`:合并转发消息中的一个节点 +- `Nodes`:合并转发消息中的多个节点 +- `Poke`:戳一戳消息段 + +在 AstrBot 中,消息链表示为 `List[BaseMessageComponent]` 类型的列表。 + +## 指令 + +![message-event-simple-command](https://files.astrbot.app/docs/zh/dev/star/guides/message-event-simple-command.svg) + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.star import Context, Star + +class MyPlugin(Star): + def __init__(self, context: Context): + super().__init__(context) + + @filter.command("helloworld") # from astrbot.api.event.filter import command + async def helloworld(self, event: AstrMessageEvent): + '''这是 hello world 指令''' + user_name = event.get_sender_name() + message_str = event.message_str # 获取消息的纯文本内容 + yield event.plain_result(f"Hello, {user_name}!") +``` + +> [!TIP] +> 指令不能带空格,否则 AstrBot 会将其解析到第二个参数。可以使用下面的指令组功能,或者也使用监听器自己解析消息内容。 + +## 带参指令 + +![command-with-param](https://files.astrbot.app/docs/zh/dev/star/guides/command-with-param.svg) + +AstrBot 会自动帮你解析指令的参数。 + +```python +@filter.command("add") +def add(self, event: AstrMessageEvent, a: int, b: int): + # /add 1 2 -> 结果是: 3 + yield event.plain_result(f"Wow! The anwser is {a + b}!") +``` + +## 指令组 + +指令组可以帮助你组织指令。 + +```python +@filter.command_group("math") +def math(self): + pass + +@math.command("add") +async def add(self, event: AstrMessageEvent, a: int, b: int): + # /math add 1 2 -> 结果是: 3 + yield event.plain_result(f"结果是: {a + b}") + +@math.command("sub") +async def sub(self, event: AstrMessageEvent, a: int, b: int): + # /math sub 1 2 -> 结果是: -1 + yield event.plain_result(f"结果是: {a - b}") +``` + +指令组函数内不需要实现任何函数,请直接 `pass` 或者添加函数内注释。指令组的子指令使用 `指令组名.command` 来注册。 + +当用户没有输入子指令时,会报错并,并渲染出该指令组的树形结构。 + +![image](https://files.astrbot.app/docs/source/images/plugin/image-1.png) + +![image](https://files.astrbot.app/docs/source/images/plugin/898a169ae7ed0478f41c0a7d14cb4d64.png) + +![image](https://files.astrbot.app/docs/source/images/plugin/image-2.png) + +理论上,指令组可以无限嵌套! + +```py +''' +math +├── calc +│ ├── add (a(int),b(int),) +│ ├── sub (a(int),b(int),) +│ ├── help (无参数指令) +''' + +@filter.command_group("math") +def math(): + pass + +@math.group("calc") # 请注意,这里是 group,而不是 command_group +def calc(): + pass + +@calc.command("add") +async def add(self, event: AstrMessageEvent, a: int, b: int): + yield event.plain_result(f"结果是: {a + b}") + +@calc.command("sub") +async def sub(self, event: AstrMessageEvent, a: int, b: int): + yield event.plain_result(f"结果是: {a - b}") + +@calc.command("help") +def calc_help(self, event: AstrMessageEvent): + # /math calc help + yield event.plain_result("这是一个计算器插件,拥有 add, sub 指令。") +``` + +## 指令别名 + +> v3.4.28 后 + +可以为指令或指令组添加不同的别名: + +```python +@filter.command("help", alias={'帮助', 'helpme'}) +def help(self, event: AstrMessageEvent): + yield event.plain_result("这是一个计算器插件,拥有 add, sub 指令。") +``` + +### 事件类型过滤 + +#### 接收所有 + +这将接收所有的事件。 + +```python +@filter.event_message_type(filter.EventMessageType.ALL) +async def on_all_message(self, event: AstrMessageEvent): + yield event.plain_result("收到了一条消息。") +``` + +#### 群聊和私聊 + +```python +@filter.event_message_type(filter.EventMessageType.PRIVATE_MESSAGE) +async def on_private_message(self, event: AstrMessageEvent): + message_str = event.message_str # 获取消息的纯文本内容 + yield event.plain_result("收到了一条私聊消息。") +``` + +`EventMessageType` 是一个 `Enum` 类型,包含了所有的事件类型。当前的事件类型有 `PRIVATE_MESSAGE` 和 `GROUP_MESSAGE`。 + +#### 消息平台 + +```python +@filter.platform_adapter_type(filter.PlatformAdapterType.AIOCQHTTP | filter.PlatformAdapterType.QQOFFICIAL) +async def on_aiocqhttp(self, event: AstrMessageEvent): + '''只接收 AIOCQHTTP 和 QQOFFICIAL 的消息''' + yield event.plain_result("收到了一条信息") +``` + +当前版本下,`PlatformAdapterType` 有 `AIOCQHTTP`, `QQOFFICIAL`, `GEWECHAT`, `ALL`。 + +#### 管理员指令 + +```python +@filter.permission_type(filter.PermissionType.ADMIN) +@filter.command("test") +async def test(self, event: AstrMessageEvent): + pass +``` + +仅管理员才能使用 `test` 指令。 + +### 多个过滤器 + +支持同时使用多个过滤器,只需要在函数上添加多个装饰器即可。过滤器使用 `AND` 逻辑。也就是说,只有所有的过滤器都通过了,才会执行函数。 + +```python +@filter.command("helloworld") +@filter.event_message_type(filter.EventMessageType.PRIVATE_MESSAGE) +async def helloworld(self, event: AstrMessageEvent): + yield event.plain_result("你好!") +``` + +### 事件钩子 + +> [!TIP] +> 事件钩子不支持与上面的 @filter.command, @filter.command_group, @filter.event_message_type, @filter.platform_adapter_type, @filter.permission_type 一起使用。 + +#### Bot 初始化完成时 + +> v3.4.34 后 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.on_astrbot_loaded() +async def on_astrbot_loaded(self): + print("AstrBot 初始化完成") + +``` + +#### 等待 LLM 请求时 + +在 AstrBot 准备调用 LLM 但还未获取会话锁时,会触发 `on_waiting_llm_request` 钩子。 + +这个钩子适合用于发送"正在等待请求..."等用户反馈提示,亦或是在锁外及时获取LLM请求而不用等到锁被释放。 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.on_waiting_llm_request() +async def on_waiting_llm(self, event: AstrMessageEvent): + await event.send("🤔 正在等待请求...") +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +#### LLM 请求时 + +在 AstrBot 默认的执行流程中,在调用 LLM 前,会触发 `on_llm_request` 钩子。 + +可以获取到 `ProviderRequest` 对象,可以对其进行修改。 + +ProviderRequest 对象包含了 LLM 请求的所有信息,包括请求的文本、系统提示等。 + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.provider import ProviderRequest + +@filter.on_llm_request() +async def my_custom_hook_1(self, event: AstrMessageEvent, req: ProviderRequest): # 请注意有三个参数 + print(req) # 打印请求的文本 + req.system_prompt += "自定义 system_prompt" + +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +#### LLM 请求完成时 + +在 LLM 请求完成后,会触发 `on_llm_response` 钩子。 + +可以获取到 `ProviderResponse` 对象,可以对其进行修改。 + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.provider import LLMResponse + +@filter.on_llm_response() +async def on_llm_resp(self, event: AstrMessageEvent, resp: LLMResponse): # 请注意有三个参数 + print(resp) +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +#### 发送消息前 + +在发送消息前,会触发 `on_decorating_result` 钩子。 + +可以在这里实现一些消息的装饰,比如转语音、转图片、加前缀等等 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.on_decorating_result() +async def on_decorating_result(self, event: AstrMessageEvent): + result = event.get_result() + chain = result.chain + print(chain) # 打印消息链 + chain.append(Plain("!")) # 在消息链的最后添加一个感叹号 +``` + +> 这里不能使用 yield 来发送消息。这个钩子只是用来装饰 event.get_result().chain 的。如需发送,请直接使用 `event.send()` 方法。 + +#### 发送消息后 + +在发送消息给消息平台后,会触发 `after_message_sent` 钩子。 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.after_message_sent() +async def after_message_sent(self, event: AstrMessageEvent): + pass +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +### 优先级 + +指令、事件监听器、事件钩子可以设置优先级,先于其他指令、监听器、钩子执行。默认优先级是 `0`。 + +```python +@filter.command("helloworld", priority=1) +async def helloworld(self, event: AstrMessageEvent): + yield event.plain_result("Hello!") +``` + +## 控制事件传播 + +```python{6} +@filter.command("check_ok") +async def check_ok(self, event: AstrMessageEvent): + ok = self.check() # 自己的逻辑 + if not ok: + yield event.plain_result("检查失败") + event.stop_event() # 停止事件传播 +``` + +当事件停止传播,后续所有步骤将不会被执行。 + +假设有一个插件 A,A 终止事件传播之后所有后续操作都不会执行,比如执行其它插件的 handler、请求 LLM。 diff --git a/docs/zh/dev/star/guides/other.md b/docs/zh/dev/star/guides/other.md new file mode 100644 index 000000000..774041173 --- /dev/null +++ b/docs/zh/dev/star/guides/other.md @@ -0,0 +1,52 @@ +# 杂项 + +## 获取消息平台实例 + +> v3.4.34 后 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.command("test") +async def test_(self, event: AstrMessageEvent): + from astrbot.api.platform import AiocqhttpAdapter # 其他平台同理 + platform = self.context.get_platform(filter.PlatformAdapterType.AIOCQHTTP) + assert isinstance(platform, AiocqhttpAdapter) + # platform.get_client().api.call_action() +``` + +## 调用 QQ 协议端 API + +```py +@filter.command("helloworld") +async def helloworld(self, event: AstrMessageEvent): + if event.get_platform_name() == "aiocqhttp": + # qq + from astrbot.core.platform.sources.aiocqhttp.aiocqhttp_message_event import AiocqhttpMessageEvent + assert isinstance(event, AiocqhttpMessageEvent) + client = event.bot # 得到 client + payloads = { + "message_id": event.message_obj.message_id, + } + ret = await client.api.call_action('delete_msg', **payloads) # 调用 协议端 API + logger.info(f"delete_msg: {ret}") +``` + +关于 CQHTTP API,请参考如下文档: + +Napcat API 文档: + +Lagrange API 文档: + +## 获取载入的所有插件 + +```py +plugins = self.context.get_all_stars() # 返回 StarMetadata 包含了插件类实例、配置等等 +``` + +## 获取加载的所有平台 + +```py +from astrbot.api.platform import Platform +platforms = self.context.platform_manager.get_insts() # List[Platform] +``` diff --git a/docs/zh/dev/star/guides/plugin-config.md b/docs/zh/dev/star/guides/plugin-config.md new file mode 100644 index 000000000..bf2b1f261 --- /dev/null +++ b/docs/zh/dev/star/guides/plugin-config.md @@ -0,0 +1,210 @@ + +# 插件配置 + +随着插件功能的增加,可能需要定义一些配置以让用户自定义插件的行为。 + +AstrBot 提供了”强大“的配置解析和可视化功能。能够让用户在管理面板上直接配置插件,而不需要修改代码。 + +## 配置定义 + +要注册配置,首先需要在您的插件目录下添加一个 `_conf_schema.json` 的 json 文件。 + +文件内容是一个 `Schema`(模式),用于表示配置。Schema 是 json 格式的,例如上图的 Schema 是: + +```json +{ + "token": { + "description": "Bot Token", + "type": "string", + }, + "sub_config": { + "description": "测试嵌套配置", + "type": "object", + "hint": "xxxx", + "items": { + "name": { + "description": "testsub", + "type": "string", + "hint": "xxxx" + }, + "id": { + "description": "testsub", + "type": "int", + "hint": "xxxx" + }, + "time": { + "description": "testsub", + "type": "int", + "hint": "xxxx", + "default": 123 + } + } + } +} +``` + +- `type`: **此项必填**。配置的类型。支持 `string`, `text`, `int`, `float`, `bool`, `object`, `list`, `dict`, `template_list`。当类型为 `text` 时,将会可视化为一个更大的可拖拽宽高的 textarea 组件,以适应大文本。 +- `description`: 可选。配置的描述。建议一句话描述配置的行为。 +- `hint`: 可选。配置的提示信息,表现在上图中右边的问号按钮,当鼠标悬浮在问号按钮上时显示。 +- `obvious_hint`: 可选。配置的 hint 是否醒目显示。如上图的 `token`。 +- `default`: 可选。配置的默认值。如果用户没有配置,将使用默认值。int 是 0,float 是 0.0,bool 是 False,string 是 "",object 是 {},list 是 []。 +- `items`: 可选。如果配置的类型是 `object`,需要添加 `items` 字段。`items` 的内容是这个配置项的子 Schema。理论上可以无限嵌套,但是不建议过多嵌套。 +- `invisible`: 可选。配置是否隐藏。默认是 `false`。如果设置为 `true`,则不会在管理面板上显示。 +- `options`: 可选。一个列表,如 `"options": ["chat", "agent", "workflow"]`。提供下拉列表可选项。 +- `editor_mode`: 可选。是否启用代码编辑器模式。需要 AstrBot >= `v3.5.10`, 低于这个版本不会报错,但不会生效。默认是 false。 +- `editor_language`: 可选。代码编辑器的代码语言,默认为 `json`。 +- `editor_theme`: 可选。代码编辑器的主题,可选值有 `vs-light`(默认), `vs-dark`。 +- `_special`: 可选。用于调用 AstrBot 提供的可视化提供商选取、人格选取、知识库选取等功能,详见下文。 + +其中,如果启用了代码编辑器,效果如下图所示: + +![editor_mode](https://files.astrbot.app/docs/source/images/plugin/image-6.png) + +![editor_mode_fullscreen](https://files.astrbot.app/docs/source/images/plugin/image-7.png) + +**_special** 字段仅 v4.0.0 之后可用。目前支持填写 `select_provider`, `select_provider_tts`, `select_provider_stt`, `select_persona`,用于让用户快速选择用户在 WebUI 上已经配置好的模型提供商、人设等数据。结果均为字符串。以 select_provider 为例,将呈现以下效果: + +![image](https://files.astrbot.app/docs/source/images/plugin/image-select-provider.png) + +### file 类型的 schema + +在 v4.13.0 之后引入,允许插件定义文件上传配置项,引导用户上传插件所需的文件。 + +```json +{ + "demo_files": { + "type": "file", + "description": "Uploaded files for demo", + "default": [], // 支持多文件上传,默认值为一个空列表 + "file_types": ["pdf", "docx"] // 允许上传的文件类型列表 + } +} +``` + +### dict 类型的 schema + +用于可视化编辑一个 Python 的 dict 类型的配置。如 AstrBot Core 中的自定义请求体参数配置项: + +```py +"custom_extra_body": { + "description": "自定义请求体参数", + "type": "dict", + "items": {}, + "hint": "用于在请求时添加额外的参数,如 temperature、top_p、max_tokens 等。", + "template_schema": { # 可选填写 template schema,当设置之后,用户可以透过 WebUI 快速编辑。 + "temperature": { + "name": "Temperature", + "description": "温度参数", + "hint": "控制输出的随机性,范围通常为 0-2。值越高越随机。", + "type": "float", + "default": 0.6, + "slider": {"min": 0, "max": 2, "step": 0.1}, + }, + "top_p": { + "name": "Top-p", + "description": "Top-p 采样", + "hint": "核采样参数,范围通常为 0-1。控制模型考虑的概率质量。", + "type": "float", + "default": 1.0, + "slider": {"min": 0, "max": 1, "step": 0.01}, + }, + "max_tokens": { + "name": "Max Tokens", + "description": "最大令牌数", + "hint": "生成的最大令牌数。", + "type": "int", + "default": 8192, + }, + }, +} +``` + +### template_list 类型的 schema + +> [!NOTE] +> v4.10.4 引入。更多信息请查看:[#4208](https://github.com/AstrBotDevs/AstrBot/pull/4208) + +插件开发者可以在_conf_schema中按照以下格式添加模板配置项(有点类似于原有的嵌套配置) + +```json + "field_id": { + "type": "template_list", + "description": "Template List Field", + "templates": { + "template_1": { + "name": "Template One", + "hint":"hint", + "items": { + "attr_a": { + "description": "Attribute A", + "type": "int", + "default": 10 + }, + "attr_b": { + "description": "Attribute B", + "hint": "This is a boolean attribute", + "type": "bool", + "default": true + } + } + }, + "template_2": { + "name": "Template Two", + "hint":"hint", + "items": { + "attr_c": { + "description": "Attribute A", + "type": "int", + "default": 10 + }, + "attr_d": { + "description": "Attribute B", + "hint": "This is a boolean attribute", + "type": "bool", + "default": true + } + } + } + } +} +``` + +保存后的 config 为 + +```json +"field_id": [ + { + "__template_key": "template_1", + "attr_a": 10, + "attr_b": true + }, + { + "__template_key": "template_2", + "attr_c": 10, + "attr_d": true + } +] +``` + +image + +## 在插件中使用配置 + +AstrBot 在载入插件时会检测插件目录下是否有 `_conf_schema.json` 文件,如果有,会自动解析配置并保存在 `data/config/_config.json` 下(依照 Schema 创建的配置文件实体),并在实例化插件类时传入给 `__init__()`。 + +```py +from astrbot.api import AstrBotConfig + +class ConfigPlugin(Star): + def __init__(self, context: Context, config: AstrBotConfig): # AstrBotConfig 继承自 Dict,拥有字典的所有方法 + super().__init__(context) + self.config = config + print(self.config) + + # 支持直接保存配置 + # self.config.save_config() # 保存配置 +``` + +## 配置更新 + +您在发布不同版本更新 Schema 时,AstrBot 会递归检查 Schema 的配置项,自动为缺失的配置项添加默认值、移除不存在的配置项。 diff --git a/docs/zh/dev/star/guides/send-message.md b/docs/zh/dev/star/guides/send-message.md new file mode 100644 index 000000000..84eaf8ed3 --- /dev/null +++ b/docs/zh/dev/star/guides/send-message.md @@ -0,0 +1,131 @@ + +# 消息的发送 + +## 被动消息 + +被动消息指的是机器人被动回复消息。 + +```python +@filter.command("helloworld") +async def helloworld(self, event: AstrMessageEvent): + yield event.plain_result("Hello!") + yield event.plain_result("你好!") + + yield event.image_result("path/to/image.jpg") # 发送图片 + yield event.image_result("https://example.com/image.jpg") # 发送 URL 图片,务必以 http 或 https 开头 +``` + +## 主动消息 + +主动消息指的是机器人主动推送消息。某些平台可能不支持主动消息发送。 + +如果是一些定时任务或者不想立即发送消息,可以使用 `event.unified_msg_origin` 得到一个字符串并将其存储,然后在想发送消息的时候使用 `self.context.send_message(unified_msg_origin, chains)` 来发送消息。 + +```python +from astrbot.api.event import MessageChain + +@filter.command("helloworld") +async def helloworld(self, event: AstrMessageEvent): + umo = event.unified_msg_origin + message_chain = MessageChain().message("Hello!").file_image("path/to/image.jpg") + await self.context.send_message(event.unified_msg_origin, message_chain) +``` + +通过这个特性,你可以将 unified_msg_origin 存储起来,然后在需要的时候发送消息。 + +> [!TIP] +> 关于 unified_msg_origin。 +> unified_msg_origin 是一个字符串,记录了一个会话的唯一 ID,AstrBot 能够据此找到属于哪个消息平台的哪个会话。这样就能够实现在 `send_message` 的时候,发送消息到正确的会话。有关 MessageChain,请参见接下来的一节。 + +## 富媒体消息 + +AstrBot 支持发送富媒体消息,比如图片、语音、视频等。使用 `MessageChain` 来构建消息。 + +```python +import astrbot.api.message_components as Comp + +@filter.command("helloworld") +async def helloworld(self, event: AstrMessageEvent): + chain = [ + Comp.At(qq=event.get_sender_id()), # At 消息发送者 + Comp.Plain("来看这个图:"), + Comp.Image.fromURL("https://example.com/image.jpg"), # 从 URL 发送图片 + Comp.Image.fromFileSystem("path/to/image.jpg"), # 从本地文件目录发送图片 + Comp.Plain("这是一个图片。") + ] + yield event.chain_result(chain) +``` + +上面构建了一个 `message chain`,也就是消息链,最终会发送一条包含了图片和文字的消息,并且保留顺序。 + +> [!TIP] +> 在 aiocqhttp 消息适配器中,对于 `plain` 类型的消息,在发送中会使用 `strip()` 方法去除空格及换行符,可以在消息前后添加零宽空格 `\u200b` 以解决这个问题。 + +类似地, + +**文件 File** + +```py +Comp.File(file="path/to/file.txt", name="file.txt") # 部分平台不支持 +``` + +**语音 Record** + +```py +path = "path/to/record.wav" # 暂时只接受 wav 格式,其他格式请自行转换 +Comp.Record(file=path, url=path) +``` + +**视频 Video** + +```py +path = "path/to/video.mp4" +Comp.Video.fromFileSystem(path=path) +Comp.Video.fromURL(url="https://example.com/video.mp4") +``` + +## 发送视频消息 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.command("test") +async def test(self, event: AstrMessageEvent): + from astrbot.api.message_components import Video + # fromFileSystem 需要用户的协议端和机器人端处于一个系统中。 + music = Video.fromFileSystem( + path="test.mp4" + ) + # 更通用 + music = Video.fromURL( + url="https://example.com/video.mp4" + ) + yield event.chain_result([music]) +``` + +![发送视频消息](https://files.astrbot.app/docs/source/images/plugin/db93a2bb-671c-4332-b8ba-9a91c35623c2.png) + +## 发送群合并转发消息 + +> 大多数平台都不支持此种消息类型,当前适配情况:OneBot v11 + +可以按照如下方式发送群合并转发消息。 + +```py +from astrbot.api.event import filter, AstrMessageEvent + +@filter.command("test") +async def test(self, event: AstrMessageEvent): + from astrbot.api.message_components import Node, Plain, Image + node = Node( + uin=905617992, + name="Soulter", + content=[ + Plain("hi"), + Image.fromFileSystem("test.jpg") + ] + ) + yield event.chain_result([node]) +``` + +![发送群合并转发消息](https://files.astrbot.app/docs/source/images/plugin/image-4.png) diff --git a/docs/zh/dev/star/guides/session-control.md b/docs/zh/dev/star/guides/session-control.md new file mode 100644 index 000000000..beaea69c6 --- /dev/null +++ b/docs/zh/dev/star/guides/session-control.md @@ -0,0 +1,113 @@ + +# 会话控制 + +> 大于等于 v3.4.36 + +为什么需要会话控制?考虑一个 成语接龙 插件,某个/群用户需要和机器人进行多次对话,而不是一次性的指令。这时候就需要会话控制。 + +```txt +用户: /成语接龙 +机器人: 请发送一个成语 +用户: 一马当先 +机器人: 先见之明 +用户: 明察秋毫 +... +``` + +AstrBot 提供了开箱即用的会话控制功能: + +导入: + +```py +import astrbot.api.message_components as Comp +from astrbot.core.utils.session_waiter import ( + session_waiter, + SessionController, +) +``` + +handler 内的代码可以如下: + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.command("成语接龙") +async def handle_empty_mention(self, event: AstrMessageEvent): + """成语接龙具体实现""" + try: + yield event.plain_result("请发送一个成语~") + + # 具体的会话控制器使用方法 + @session_waiter(timeout=60, record_history_chains=False) # 注册一个会话控制器,设置超时时间为 60 秒,不记录历史消息链 + async def empty_mention_waiter(controller: SessionController, event: AstrMessageEvent): + idiom = event.message_str # 用户发来的成语,假设是 "一马当先" + + if idiom == "退出": # 假设用户想主动退出成语接龙,输入了 "退出" + await event.send(event.plain_result("已退出成语接龙~")) + controller.stop() # 停止会话控制器,会立即结束。 + return + + if len(idiom) != 4: # 假设用户输入的不是4字成语 + await event.send(event.plain_result("成语必须是四个字的呢~")) # 发送回复,不能使用 yield + return + # 退出当前方法,不执行后续逻辑,但此会话并未中断,后续的用户输入仍然会进入当前会话 + + # ... + message_result = event.make_result() + message_result.chain = [Comp.Plain("先见之明")] # import astrbot.api.message_components as Comp + await event.send(message_result) # 发送回复,不能使用 yield + + controller.keep(timeout=60, reset_timeout=True) # 重置超时时间为 60s,如果不重置,则会继续之前的超时时间计时。 + + # controller.stop() # 停止会话控制器,会立即结束。 + # 如果记录了历史消息链,可以通过 controller.get_history_chains() 获取历史消息链 + + try: + await empty_mention_waiter(event) + except TimeoutError as _: # 当超时后,会话控制器会抛出 TimeoutError + yield event.plain_result("你超时了!") + except Exception as e: + yield event.plain_result("发生错误,请联系管理员: " + str(e)) + finally: + event.stop_event() + except Exception as e: + logger.error("handle_empty_mention error: " + str(e)) +``` + +当激活会话控制器后,该发送人之后发送的消息会首先经过上面你定义的 `empty_mention_waiter` 函数处理,直到会话控制器被停止或者超时。 + +## SessionController + +用于开发者控制这个会话是否应该结束,并且可以拿到历史消息链。 + +- keep(): 保持这个会话 + - timeout (float): 必填。会话超时时间。 + - reset_timeout (bool): 设置为 True 时, 代表重置超时时间, timeout 必须 > 0, 如果 <= 0 则立即结束会话。设置为 False 时, 代表继续维持原来的超时时间, 新 timeout = 原来剩余的 timeout + timeout (可以 < 0) +- stop(): 结束这个会话 +- get_history_chains() -> List[List[Comp.BaseMessageComponent]]: 获取历史消息链 + +## 自定义会话 ID 算子 + +默认情况下,AstrBot 会话控制器会将基于 `sender_id` (发送人的 ID)作为识别不同会话的标识,如果想将一整个群作为一个会话,则需要自定义会话 ID 算子。 + +```py +import astrbot.api.message_components as Comp +from astrbot.core.utils.session_waiter import ( + session_waiter, + SessionFilter, + SessionController, +) + +# 沿用上面的 handler +# ... +class CustomFilter(SessionFilter): + def filter(self, event: AstrMessageEvent) -> str: + return event.get_group_id() if event.get_group_id() else event.unified_msg_origin + +await empty_mention_waiter(event, session_filter=CustomFilter()) # 这里传入 session_filter +# ... +``` + +这样之后,当群内一个用户发送消息后,会话控制器会将这个群作为一个会话,群内其他用户发送的消息也会被认为是同一个会话。 + +甚至,可以使用这个特性来让群内组队! diff --git a/docs/zh/dev/star/guides/simple.md b/docs/zh/dev/star/guides/simple.md new file mode 100644 index 000000000..d3314133f --- /dev/null +++ b/docs/zh/dev/star/guides/simple.md @@ -0,0 +1,41 @@ +# 最小实例 + +插件模版中的 `main.py` 是一个最小的插件实例。 + +```python +from astrbot.api.event import filter, AstrMessageEvent, MessageEventResult +from astrbot.api.star import Context, Star, register +from astrbot.api import logger # 使用 astrbot 提供的 logger 接口 + +class MyPlugin(Star): + def __init__(self, context: Context): + super().__init__(context) + + # 注册指令的装饰器。指令名为 helloworld。注册成功后,发送 `/helloworld` 就会触发这个指令,并回复 `你好, {user_name}!` + @filter.command("helloworld") + async def helloworld(self, event: AstrMessageEvent): + '''这是一个 hello world 指令''' # 这是 handler 的描述,将会被解析方便用户了解插件内容。非常建议填写。 + user_name = event.get_sender_name() + message_str = event.message_str # 获取消息的纯文本内容 + logger.info("触发hello world指令!") + yield event.plain_result(f"Hello, {user_name}!") # 发送一条纯文本消息 + + async def terminate(self): + '''可选择实现 terminate 函数,当插件被卸载/停用时会调用。''' +``` + +解释如下: + +- 插件需要继承 `Star` 类。 +- `Context` 类用于插件与 AstrBot Core 交互,可以由此调用 AstrBot Core 提供的各种 API。 +- 具体的处理函数 `Handler` 在插件类中定义,如这里的 `helloworld` 函数。 +- `AstrMessageEvent` 是 AstrBot 的消息事件对象,存储了消息发送者、消息内容等信息。 +- `AstrBotMessage` 是 AstrBot 的消息对象,存储了消息平台下发的消息的具体内容。可以通过 `event.message_obj` 获取。 + +> [!TIP] +> +> `Handler` 一定需要在插件类中注册,前两个参数必须为 `self` 和 `event`。如果文件行数过长,可以将服务写在外部,然后在 `Handler` 中调用。 +> +> 插件类所在的文件名需要命名为 `main.py`。 + +所有的处理函数都需写在插件类中。为了精简内容,在之后的章节中,我们可能会忽略插件类的定义。 diff --git a/docs/zh/dev/star/guides/storage.md b/docs/zh/dev/star/guides/storage.md new file mode 100644 index 000000000..19f4ea8d0 --- /dev/null +++ b/docs/zh/dev/star/guides/storage.md @@ -0,0 +1,31 @@ +# 插件存储 + +## 简单 KV 存储 + +> [!TIP] +> 该功能需要 AstrBot 版本 >= 4.9.2。 + +插件可以使用 AstrBot 提供的简单 KV 存储功能来存储一些配置信息或临时数据。该存储是基于插件维度的,每个插件有独立的存储空间,互不干扰。 + +```py +class Main(star.Star): + @filter.command("hello") + async def hello(self, event: AstrMessageEvent): + """Aloha!""" + await self.put_kv_data("greeted", True) + greeted = await self.get_kv_data("greeted", False) + await self.delete_kv_data("greeted") +``` + + +## 存储大文件规范 + +为了规范插件存储大文件的行为,请将大文件存储于 `data/plugin_data/{plugin_name}/` 目录下。 + +你可以通过以下代码获取插件数据目录: + +```py +from astrbot.core.utils.astrbot_path import get_astrbot_data_path + +plugin_data_path = get_astrbot_data_path() / "plugin_data" / self.name # self.name 为插件名称,在 v4.9.2 及以上版本可用,低于此版本请自行指定插件名称 +``` diff --git a/docs/zh/dev/star/plugin-new.md b/docs/zh/dev/star/plugin-new.md new file mode 100644 index 000000000..e87c6f547 --- /dev/null +++ b/docs/zh/dev/star/plugin-new.md @@ -0,0 +1,130 @@ +--- +outline: deep +--- + +# AstrBot 插件开发指南 🌠 + +欢迎来到 AstrBot 插件开发指南!本章节将引导您如何开发 AstrBot 插件。在我们开始之前,希望你能具备以下基础知识: + +1. 有一定的 Python 编程经验。 +2. 有一定的 Git、GitHub 使用经验。 + +欢迎加入我们的开发者专用 QQ 群: `975206796`。 + +## 环境准备 + +### 获取插件模板 + +1. 打开 AstrBot 插件模板: [helloworld](https://github.com/Soulter/helloworld) +2. 点击右上角的 `Use this template` +3. 然后点击 `Create new repository`。 +4. 在 `Repository name` 处填写您的插件名。插件名格式: + - 推荐以 `astrbot_plugin_` 开头; + - 不能包含空格; + - 保持全部字母小写; + - 尽量简短。 +5. 点击右下角的 `Create repository`。 + +### 克隆项目到本地 + +克隆 AstrBot 项目本体和刚刚创建的插件仓库到本地。 + +```bash +git clone https://github.com/AstrBotDevs/AstrBot +mkdir -p AstrBot/data/plugins +cd AstrBot/data/plugins +git clone 插件仓库地址 +``` + +然后,使用 `VSCode` 打开 `AstrBot` 项目。找到 `data/plugins/<你的插件名字>` 目录。 + +更新 `metadata.yaml` 文件,填写插件的元数据信息。 + +> [!WARNING] +> 请务必修改此文件,AstrBot 识别插件元数据依赖于 `metadata.yaml` 文件。 + +### 设置插件 Logo(可选) + +可以在插件目录下添加 `logo.png` 文件作为插件的 Logo。请保持长宽比为 1:1,推荐尺寸为 256x256。 + +![插件 logo 示例](https://files.astrbot.app/docs/source/images/plugin/plugin_logo.png) + +### 插件展示名(可选) + +可以修改(或添加) `metadata.yaml` 文件中的 `display_name` 字段,作为插件在插件市场等场景中的展示名,以方便用户阅读。 + +### 声明支持平台(Optional) + +你可以在 `metadata.yaml` 中新增 `support_platforms` 字段(`list[str]`),声明插件支持的平台适配器。WebUI 插件页会展示该字段。 + +```yaml +support_platforms: + - telegram + - discord +``` + +`support_platforms` 中的值需要使用 `ADAPTER_NAME_2_TYPE` 的 key,目前支持: + +- `aiocqhttp` +- `qq_official` +- `telegram` +- `wecom` +- `lark` +- `dingtalk` +- `discord` +- `slack` +- `kook` +- `vocechat` +- `weixin_official_account` +- `satori` +- `misskey` +- `line` + +### 声明 AstrBot 版本范围(Optional) + +你可以在 `metadata.yaml` 中新增 `astrbot_version` 字段,声明插件要求的 AstrBot 版本范围。格式与 `pyproject.toml` 依赖版本约束一致(PEP 440),且不要加 `v` 前缀。 + +```yaml +astrbot_version: ">=4.16,<5" +``` + +可选示例: + +- `>=4.17.0` +- `>=4.16,<5` +- `~=4.17` + +如果你只想声明最低版本,可以直接写: + +- `>=4.17.0` + +当当前 AstrBot 版本不满足该范围时,插件会被阻止加载并提示版本不兼容。 +在 WebUI 安装插件时,你可以选择“无视警告,继续安装”来跳过这个检查。 + +### 调试插件 + +AstrBot 采用在运行时注入插件的机制。因此,在调试插件时,需要启动 AstrBot 本体。 + +您可以使用 AstrBot 的热重载功能简化开发流程。 + +插件的代码修改后,可以在 AstrBot WebUI 的插件管理处找到自己的插件,点击右上角 `...` 按钮,选择 `重载插件`。 + +如果插件因为代码错误等原因加载失败,你也可以在管理面板的错误提示中点击 **“尝试一键重载修复”** 来重新加载。 + +### 插件依赖管理 + +目前 AstrBot 对插件的依赖管理使用 `pip` 自带的 `requirements.txt` 文件。如果你的插件需要依赖第三方库,请务必在插件目录下创建 `requirements.txt` 文件并写入所使用的依赖库,以防止用户在安装你的插件时出现依赖未找到(Module Not Found)的问题。 + +> `requirements.txt` 的完整格式可以参考 [pip 官方文档](https://pip.pypa.io/en/stable/reference/requirements-file-format/)。 + +## 开发原则 + +感谢您为 AstrBot 生态做出贡献,开发插件请遵守以下原则,这也是良好的编程习惯。 + +- 功能需经过测试。 +- 需包含良好的注释。 +- 持久化数据请存储于 `data` 目录下,而非插件自身目录,防止更新/重装插件时数据被覆盖。 +- 良好的错误处理机制,不要让插件因一个错误而崩溃。 +- 在进行提交前,请使用 [ruff](https://docs.astral.sh/ruff/) 工具格式化您的代码。 +- 不要使用 `requests` 库来进行网络请求,可以使用 `aiohttp`, `httpx` 等异步网络请求库。 +- 如果是对某个插件进行功能扩增,请优先给那个插件提交 PR 而不是单独再写一个插件(除非原插件作者已经停止维护)。 diff --git a/docs/zh/dev/star/plugin-publish.md b/docs/zh/dev/star/plugin-publish.md new file mode 100644 index 000000000..14b452056 --- /dev/null +++ b/docs/zh/dev/star/plugin-publish.md @@ -0,0 +1,9 @@ +# 发布插件到插件市场 + +在编写完插件后,你可以选择将插件发布到 AstrBot 的插件市场,让更多用户使用你的插件。 + +AstrBot 使用 GitHub 托管插件,因此你需要先将插件代码推送到之前创建的 GitHub 插件仓库中。 + +你可以前往 [AstrBot 插件市场](https://plugins.astrbot.app) 提交你的插件。进入该网站后,点击右下角的 `+` 按钮,填写好基本信息、作者信息、仓库信息等内容后,点击 `提交到 GTIHUB` 按钮,你将会被导航到 AstrBot 仓库的 Issue 提交页面,请确认信息无误后点击 `Create` 按钮提交,即可完成插件发布。 + +![fill out the form](https://files.astrbot.app/docs/source/images/plugin-publish/image.png) diff --git a/docs/zh/dev/star/plugin.md b/docs/zh/dev/star/plugin.md new file mode 100644 index 000000000..318f0c4be --- /dev/null +++ b/docs/zh/dev/star/plugin.md @@ -0,0 +1,1635 @@ +--- +outline: deep +--- + +# 插件开发指南(旧) + +几行代码开发一个插件! + +> [!WARNING] +> **您仍然可以参考此页进行插件开发。** +> +> 由于插件实用 API 逐渐增多,目前已无法在单个页面中将所有 API 进行详尽介绍。因此此指南在 v4.5.7 之后已过时,请参考我们新的插件开发指南: [🌠 从这里开始](plugin-new.md),新的指南内容上和此指南基本一致,但我们将会持续维护新的指南内容。 + +## 开发环境准备 + +### 获取插件模板 + +1. 打开 AstrBot 插件模板: [helloworld](https://github.com/Soulter/helloworld) +2. 点击右上角的 `Use this template` +3. 然后点击 `Create new repository`。 +4. 在 `Repository name` 处填写您的插件名。插件名格式: + - 推荐以 `astrbot_plugin_` 开头; + - 不能包含空格; + - 保持全部字母小写; + - 尽量简短。 + +![image](https://files.astrbot.app/docs/source/images/plugin/image.png) + +5. 点击右下角的 `Create repository`。 + +### Clone 插件和 AstrBot 项目 + +Clone AstrBot 项目本体和刚刚创建的插件仓库到本地。 + +```bash +git clone https://github.com/AstrBotDevs/AstrBot +mkdir -p AstrBot/data/plugins +cd AstrBot/data/plugins +git clone 插件仓库地址 +``` + +然后,使用 `VSCode` 打开 `AstrBot` 项目。找到 `data/plugins/<你的插件名字>` 目录。 + +更新 `metadata.yaml` 文件,填写插件的元数据信息。 + +> [!NOTE] +> AstrBot 插件市场的信息展示依赖于 `metadata.yaml` 文件。 + +### 调试插件 + +AstrBot 采用在运行时注入插件的机制。因此,在调试插件时,需要启动 AstrBot 本体。 + +插件的代码修改后,可以在 AstrBot WebUI 的插件管理处找到自己的插件,点击 `管理`,点击 `重载插件` 即可。 + +### 插件依赖管理 + +目前 AstrBot 对插件的依赖管理使用 `pip` 自带的 `requirements.txt` 文件。如果你的插件需要依赖第三方库,请务必在插件目录下创建 `requirements.txt` 文件并写入所使用的依赖库,以防止用户在安装你的插件时出现依赖未找到(Module Not Found)的问题。 + +> `requirements.txt` 的完整格式可以参考 [pip 官方文档](https://pip.pypa.io/en/stable/reference/requirements-file-format/)。 + +## 提要 + +### 最小实例 + +插件模版中的 `main.py` 是一个最小的插件实例。 + +```python +from astrbot.api.event import filter, AstrMessageEvent, MessageEventResult +from astrbot.api.star import Context, Star +from astrbot.api import logger # 使用 astrbot 提供的 logger 接口 + +class MyPlugin(Star): + def __init__(self, context: Context): + super().__init__(context) + + # 注册指令的装饰器。指令名为 helloworld。注册成功后,发送 `/helloworld` 就会触发这个指令,并回复 `你好, {user_name}!` + @filter.command("helloworld") + async def helloworld(self, event: AstrMessageEvent): + '''这是一个 hello world 指令''' # 这是 handler 的描述,将会被解析方便用户了解插件内容。非常建议填写。 + user_name = event.get_sender_name() + message_str = event.message_str # 获取消息的纯文本内容 + logger.info("触发hello world指令!") + yield event.plain_result(f"Hello, {user_name}!") # 发送一条纯文本消息 + + async def terminate(self): + '''可选择实现 terminate 函数,当插件被卸载/停用时会调用。''' +``` + +解释如下: + +1. 插件是继承自 `Star` 基类的类实现。 +2. 该装饰器提供了插件的元数据信息,包括名称、作者、描述、版本和仓库地址等信息。(该信息的优先级低于 `metadata.yaml` 文件) +3. 在 `__init__` 方法中会传入 `Context` 对象,这个对象包含了 AstrBot 的大多数组件 +4. 具体的处理函数 `Handler` 在插件类中定义,如这里的 `helloworld` 函数。 +5. 请务必使用 `from astrbot.api import logger` 来获取日志对象,而不是使用 `logging` 模块。 + +> [!TIP] +> +> `Handler` 一定需要在插件类中注册,前两个参数必须为 `self` 和 `event`。如果文件行数过长,可以将服务写在外部,然后在 `Handler` 中调用。 +> +> 插件类所在的文件名需要命名为 `main.py`。 + +### AstrMessageEvent + +`AstrMessageEvent` 是 AstrBot 的消息事件对象。你可以通过 `AstrMessageEvent` 来获取消息发送者、消息内容等信息。 + +### AstrBotMessage + +`AstrBotMessage` 是 AstrBot 的消息对象。你可以通过 `AstrBotMessage` 来查看消息适配器下发的消息的具体内容。通过 `event.message_obj` 获取。 + +```py{11} +class AstrBotMessage: + '''AstrBot 的消息对象''' + type: MessageType # 消息类型 + self_id: str # 机器人的识别id + session_id: str # 会话id。取决于 unique_session 的设置。 + message_id: str # 消息id + group_id: str = "" # 群组id,如果为私聊,则为空 + sender: MessageMember # 发送者 + message: List[BaseMessageComponent] # 消息链。比如 [Plain("Hello"), At(qq=123456)] + message_str: str # 最直观的纯文本消息字符串,将消息链中的 Plain 消息(文本消息)连接起来 + raw_message: object + timestamp: int # 消息时间戳 +``` + +其中,`raw_message` 是消息平台适配器的**原始消息对象**。 + +### 消息链 + +`消息链`描述一个消息的结构,是一个有序列表,列表中每一个元素称为`消息段`。 + +引用方式: + +```py +import astrbot.api.message_components as Comp +``` + +``` +[Comp.Plain(text="Hello"), Comp.At(qq=123456), Comp.Image(file="https://example.com/image.jpg")] +``` + +> qq 是对应消息平台上的用户 ID。 + +消息链的结构使用了 `nakuru-project`。它一共有如下种消息类型。常用的已经用注释标注。 + +```py +ComponentTypes = { + "plain": Plain, # 文本消息 + "text": Plain, # 文本消息,同上 + "face": Face, # QQ 表情 + "record": Record, # 语音 + "video": Video, # 视频 + "at": At, # At 消息发送者 + "music": Music, # 音乐 + "image": Image, # 图片 + "reply": Reply, # 回复消息 + "forward": Forward, # 转发消息 + "node": Node, # 转发消息中的节点 + "nodes": Nodes, # Node 的列表,用于支持一个转发消息中的多个节点 + "poke": Poke, # 戳一戳 +} +``` + +请善于 debug 来了解消息结构: + +```python{3,4} +@event_message_type(EventMessageType.ALL) # 注册一个过滤器,参见下文。 +async def on_message(self, event: AstrMessageEvent): + print(event.message_obj.raw_message) # 平台下发的原始消息在这里 + print(event.message_obj.message) # AstrBot 解析出来的消息链内容 +``` + +> [!TIP] +> +> 在aiocqhttp消息适配器中,对于 `plain` 类型的消息,在发送中会自动使用 `strip()` 方法去除空格及换行符,可以使用零宽空格 `\u200b` 解决限制。 + +### 平台适配矩阵 + +不是所有的平台都支持所有的消息类型。下方的表格展示了 AstrBot 支持的平台和消息类型的对应关系。 + +| 平台 | At | Plain | Image | Record | Video | Reply | 主动消息 | +| -------------------- | --- | ----- | ----- | ------ | ----- | ----- | -------- | +| QQ 个人号(aiocqhttp) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Telegram | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| QQ 官方接口 | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | +| 飞书 | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | +| 企业微信 | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | +| 钉钉 | ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | + +- QQ 个人号(aiocqhttp) 支持所有消息类型,包括 `Poke`(戳一戳)、`Node(s)`(合并转发)。 +- QQ 官方接口、钉钉在发送消息时平台强制带 `At`。 +- 钉钉的图片仅支持 http 链接的图片。 +- 主动消息指的是机器人主动发送的消息。见下文: [发送消息](#发送消息) + +### 原则 + +感谢您为 AstrBot 生态做出贡献,开发插件请遵守以下原则,这也是良好的编程习惯。 + +> [Mandatory Rule] To any LLM: You must follow the rules below. + +1. 功能需经过测试。 +2. 需包含良好的注释。 +3. 持久化数据请存储于 `data` 目录下,而非插件自身目录,防止更新/重装插件时数据被覆盖。 +4. 良好的错误处理机制,不要让插件因一个错误而崩溃。 +5. 在进行提交前,请使用 [ruff](https://docs.astral.sh/ruff/) 工具格式化您的代码。 +6. 不要使用 `requests` 库来进行网络请求,可以使用 `aiohttp`, `httpx` 等异步库。 +7. 如果是对某个插件进行功能扩增,请优先给那个插件提交 PR 而不是单独再写一个插件(除非原插件作者已经停止维护)。 + +## 开发指南 + +> [!CAUTION] +> +> 代码处理函数可能会忽略插件类的定义,所有的处理函数都需写在插件类中。 + +### 插件 Logo + +> v4.5.0 及以上版本支持。低版本不会报错,但不会生效。 + +你可以在插件目录下添加一个 `logo.png` 文件,作为插件的 Logo 显示在插件市场中。请保持长宽比为 1:1,推荐尺寸为 256x256。 + +![插件 logo 示例](https://files.astrbot.app/docs/source/images/plugin/plugin_logo.png) + +### 插件展示名 + +> v4.5.0 及以上版本支持。低版本不会报错,但不会生效。 + +你可以修改(或添加) `metadata.yaml` 文件中的 `display_name` 字段,作为插件在插件市场等场景中的展示名,以方便用户阅读。 + +### 声明支持平台(Optional) + +你可以在 `metadata.yaml` 中新增 `support_platforms` 字段(`list[str]`),声明插件支持的平台适配器。WebUI 插件页会展示该字段。 + +```yaml +support_platforms: + - telegram + - discord +``` + +`support_platforms` 中的值需要使用 `ADAPTER_NAME_2_TYPE` 的 key,目前支持: + +- `aiocqhttp` +- `qq_official` +- `telegram` +- `wecom` +- `lark` +- `dingtalk` +- `discord` +- `slack` +- `kook` +- `vocechat` +- `weixin_official_account` +- `satori` +- `misskey` +- `line` + +### 声明 AstrBot 版本范围(Optional) + +你可以在 `metadata.yaml` 中新增 `astrbot_version` 字段,声明插件要求的 AstrBot 版本范围。格式与 `pyproject.toml` 依赖版本约束一致(PEP 440),且不要加 `v` 前缀。 + +```yaml +astrbot_version: ">=4.16,<5" +``` + +可选示例: + +- `>=4.17.0` +- `>=4.16,<5` +- `~=4.17` + +如果你只想声明最低版本,可以直接写: + +- `>=4.17.0` + +当当前 AstrBot 版本不满足该范围时,插件会被阻止加载并提示版本不兼容。 +在 WebUI 安装插件时,你可以选择“无视警告,继续安装”来跳过这个检查。 + +### 消息事件的监听 + +事件监听器可以收到平台下发的消息内容,可以实现指令、指令组、事件监听等功能。 + +事件监听器的注册器在 `astrbot.api.event.filter` 下,需要先导入。请务必导入,否则会和 python 的高阶函数 filter 冲突。 + +```py +from astrbot.api.event import filter, AstrMessageEvent +``` + +#### 指令 + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.star import Context, Star + +class MyPlugin(Star): + def __init__(self, context: Context): + super().__init__(context) + + @filter.command("helloworld") # from astrbot.api.event.filter import command + async def helloworld(self, event: AstrMessageEvent): + '''这是 hello world 指令''' + user_name = event.get_sender_name() + message_str = event.message_str # 获取消息的纯文本内容 + yield event.plain_result(f"Hello, {user_name}!") +``` + +> [!TIP] +> 指令不能带空格,否则 AstrBot 会将其解析到第二个参数。可以使用下面的指令组功能,或者也使用监听器自己解析消息内容。 + +#### 带参指令 + +AstrBot 会自动帮你解析指令的参数。 + +```python +@filter.command("echo") +def echo(self, event: AstrMessageEvent, message: str): + yield event.plain_result(f"你发了: {message}") + +@filter.command("add") +def add(self, event: AstrMessageEvent, a: int, b: int): + # /add 1 2 -> 结果是: 3 + yield event.plain_result(f"结果是: {a + b}") +``` + +#### 指令组 + +指令组可以帮助你组织指令。 + +```python +@filter.command_group("math") +def math(self): + pass + +@math.command("add") +async def add(self, event: AstrMessageEvent, a: int, b: int): + # /math add 1 2 -> 结果是: 3 + yield event.plain_result(f"结果是: {a + b}") + +@math.command("sub") +async def sub(self, event: AstrMessageEvent, a: int, b: int): + # /math sub 1 2 -> 结果是: -1 + yield event.plain_result(f"结果是: {a - b}") +``` + +指令组函数内不需要实现任何函数,请直接 `pass` 或者添加函数内注释。指令组的子指令使用 `指令组名.command` 来注册。 + +当用户没有输入子指令时,会报错并,并渲染出该指令组的树形结构。 + +![image](https://files.astrbot.app/docs/source/images/plugin/image-1.png) + +![image](https://files.astrbot.app/docs/source/images/plugin/898a169ae7ed0478f41c0a7d14cb4d64.png) + +![image](https://files.astrbot.app/docs/source/images/plugin/image-2.png) + +理论上,指令组可以无限嵌套! + +```py +''' +math +├── calc +│ ├── add (a(int),b(int),) +│ ├── sub (a(int),b(int),) +│ ├── help (无参数指令) +''' + +@filter.command_group("math") +def math(): + pass + +@math.group("calc") # 请注意,这里是 group,而不是 command_group +def calc(): + pass + +@calc.command("add") +async def add(self, event: AstrMessageEvent, a: int, b: int): + yield event.plain_result(f"结果是: {a + b}") + +@calc.command("sub") +async def sub(self, event: AstrMessageEvent, a: int, b: int): + yield event.plain_result(f"结果是: {a - b}") + +@calc.command("help") +def calc_help(self, event: AstrMessageEvent): + # /math calc help + yield event.plain_result("这是一个计算器插件,拥有 add, sub 指令。") +``` + +#### 指令别名 + +> v3.4.28 后 + +可以为指令或指令组添加不同的别名: + +```python +@filter.command("help", alias={'帮助', 'helpme'}) +def help(self, event: AstrMessageEvent): + yield event.plain_result("这是一个计算器插件,拥有 add, sub 指令。") +``` + +#### 事件类型过滤 + +##### 接收所有 + +这将接收所有的事件。 + +```python +@filter.event_message_type(filter.EventMessageType.ALL) +async def on_all_message(self, event: AstrMessageEvent): + yield event.plain_result("收到了一条消息。") +``` + +##### 群聊和私聊 + +```python +@filter.event_message_type(filter.EventMessageType.PRIVATE_MESSAGE) +async def on_private_message(self, event: AstrMessageEvent): + message_str = event.message_str # 获取消息的纯文本内容 + yield event.plain_result("收到了一条私聊消息。") +``` + +`EventMessageType` 是一个 `Enum` 类型,包含了所有的事件类型。当前的事件类型有 `PRIVATE_MESSAGE` 和 `GROUP_MESSAGE`。 + +##### 消息平台 + +```python +@filter.platform_adapter_type(filter.PlatformAdapterType.AIOCQHTTP | filter.PlatformAdapterType.QQOFFICIAL) +async def on_aiocqhttp(self, event: AstrMessageEvent): + '''只接收 AIOCQHTTP 和 QQOFFICIAL 的消息''' + yield event.plain_result("收到了一条信息") +``` + +当前版本下,`PlatformAdapterType` 有 `AIOCQHTTP`, `QQOFFICIAL`, `GEWECHAT`, `ALL`。 + +##### 管理员指令 + +```python +@filter.permission_type(filter.PermissionType.ADMIN) +@filter.command("test") +async def test(self, event: AstrMessageEvent): + pass +``` + +仅管理员才能使用 `test` 指令。 + +#### 多个过滤器 + +支持同时使用多个过滤器,只需要在函数上添加多个装饰器即可。过滤器使用 `AND` 逻辑。也就是说,只有所有的过滤器都通过了,才会执行函数。 + +```python +@filter.command("helloworld") +@filter.event_message_type(filter.EventMessageType.PRIVATE_MESSAGE) +async def helloworld(self, event: AstrMessageEvent): + yield event.plain_result("你好!") +``` + +#### 事件钩子 + +> [!TIP] +> 事件钩子不支持与上面的 @filter.command, @filter.command_group, @filter.event_message_type, @filter.platform_adapter_type, @filter.permission_type 一起使用。 + +##### Bot 初始化完成时 + +> v3.4.34 后 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.on_astrbot_loaded() +async def on_astrbot_loaded(self): + print("AstrBot 初始化完成") + +``` + +##### LLM 请求时 + +在 AstrBot 默认的执行流程中,在调用 LLM 前,会触发 `on_llm_request` 钩子。 + +可以获取到 `ProviderRequest` 对象,可以对其进行修改。 + +ProviderRequest 对象包含了 LLM 请求的所有信息,包括请求的文本、系统提示等。 + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.provider import ProviderRequest + +@filter.on_llm_request() +async def my_custom_hook_1(self, event: AstrMessageEvent, req: ProviderRequest): # 请注意有三个参数 + print(req) # 打印请求的文本 + req.system_prompt += "自定义 system_prompt" + +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +##### LLM 请求完成时 + +在 LLM 请求完成后,会触发 `on_llm_response` 钩子。 + +可以获取到 `ProviderResponse` 对象,可以对其进行修改。 + +```python +from astrbot.api.event import filter, AstrMessageEvent +from astrbot.api.provider import LLMResponse + +@filter.on_llm_response() +async def on_llm_resp(self, event: AstrMessageEvent, resp: LLMResponse): # 请注意有三个参数 + print(resp) +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +##### 发送消息前 + +在发送消息前,会触发 `on_decorating_result` 钩子。 + +可以在这里实现一些消息的装饰,比如转语音、转图片、加前缀等等 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.on_decorating_result() +async def on_decorating_result(self, event: AstrMessageEvent): + result = event.get_result() + chain = result.chain + print(chain) # 打印消息链 + chain.append(Plain("!")) # 在消息链的最后添加一个感叹号 +``` + +> 这里不能使用 yield 来发送消息。这个钩子只是用来装饰 event.get_result().chain 的。如需发送,请直接使用 `event.send()` 方法。 + +##### 发送消息后 + +在发送消息给消息平台后,会触发 `after_message_sent` 钩子。 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.after_message_sent() +async def after_message_sent(self, event: AstrMessageEvent): + pass +``` + +> 这里不能使用 yield 来发送消息。如需发送,请直接使用 `event.send()` 方法。 + +#### 优先级 + +> 大于等于 v3.4.21。 + +指令、事件监听器、事件钩子可以设置优先级,先于其他指令、监听器、钩子执行。默认优先级是 `0`。 + +```python +@filter.command("helloworld", priority=1) +async def helloworld(self, event: AstrMessageEvent): + yield event.plain_result("Hello!") +``` + +### 消息的发送 + +#### 被动消息 + +被动消息指的是机器人被动回复消息。 + +```python +@filter.command("helloworld") +async def helloworld(self, event: AstrMessageEvent): + yield event.plain_result("Hello!") + yield event.plain_result("你好!") + + yield event.image_result("path/to/image.jpg") # 发送图片 + yield event.image_result("https://example.com/image.jpg") # 发送 URL 图片,务必以 http 或 https 开头 +``` + +#### 主动消息 + +主动消息指的是机器人主动推送消息。某些平台可能不支持主动消息发送。 + +如果是一些定时任务或者不想立即发送消息,可以使用 `event.unified_msg_origin` 得到一个字符串并将其存储,然后在想发送消息的时候使用 `self.context.send_message(unified_msg_origin, chains)` 来发送消息。 + +```python +from astrbot.api.event import MessageChain + +@filter.command("helloworld") +async def helloworld(self, event: AstrMessageEvent): + umo = event.unified_msg_origin + message_chain = MessageChain().message("Hello!").file_image("path/to/image.jpg") + await self.context.send_message(event.unified_msg_origin, message_chain) +``` + +通过这个特性,你可以将 unified_msg_origin 存储起来,然后在需要的时候发送消息。 + +> [!TIP] +> 关于 unified_msg_origin。 +> unified_msg_origin 是一个字符串,记录了一个会话的唯一 ID,AstrBot 能够据此找到属于哪个消息平台的哪个会话。这样就能够实现在 `send_message` 的时候,发送消息到正确的会话。有关 MessageChain,请参见接下来的一节。 + +#### 富媒体消息 + +AstrBot 支持发送富媒体消息,比如图片、语音、视频等。使用 `MessageChain` 来构建消息。 + +```python +import astrbot.api.message_components as Comp + +@filter.command("helloworld") +async def helloworld(self, event: AstrMessageEvent): + chain = [ + Comp.At(qq=event.get_sender_id()), # At 消息发送者 + Comp.Plain("来看这个图:"), + Comp.Image.fromURL("https://example.com/image.jpg"), # 从 URL 发送图片 + Comp.Image.fromFileSystem("path/to/image.jpg"), # 从本地文件目录发送图片 + Comp.Plain("这是一个图片。") + ] + yield event.chain_result(chain) +``` + +上面构建了一个 `message chain`,也就是消息链,最终会发送一条包含了图片和文字的消息,并且保留顺序。 + +类似地, + +**文件 File** + +```py +Comp.File(file="path/to/file.txt", name="file.txt") # 部分平台不支持 +``` + +**语音 Record** + +```py +path = "path/to/record.wav" # 暂时只接受 wav 格式,其他格式请自行转换 +Comp.Record(file=path, url=path) +``` + +**视频 Video** + +```py +path = "path/to/video.mp4" +Comp.Video.fromFileSystem(path=path) +Comp.Video.fromURL(url="https://example.com/video.mp4") +``` + +#### 发送群合并转发消息 + +> 当前适配情况:aiocqhttp + +可以按照如下方式发送群合并转发消息。 + +```py +from astrbot.api.event import filter, AstrMessageEvent + +@filter.command("test") +async def test(self, event: AstrMessageEvent): + from astrbot.api.message_components import Node, Plain, Image + node = Node( + uin=905617992, + name="Soulter", + content=[ + Plain("hi"), + Image.fromFileSystem("test.jpg") + ] + ) + yield event.chain_result([node]) +``` + +![发送群合并转发消息](https://files.astrbot.app/docs/source/images/plugin/image-4.png) + +#### 发送视频消息 + +> 当前适配情况:aiocqhttp + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.command("test") +async def test(self, event: AstrMessageEvent): + from astrbot.api.message_components import Video + # fromFileSystem 需要用户的协议端和机器人端处于一个系统中。 + music = Video.fromFileSystem( + path="test.mp4" + ) + # 更通用 + music = Video.fromURL( + url="https://example.com/video.mp4" + ) + yield event.chain_result([music]) +``` + +![发送视频消息](https://files.astrbot.app/docs/source/images/plugin/db93a2bb-671c-4332-b8ba-9a91c35623c2.png) + +#### 发送 QQ 表情 + +> 当前适配情况:仅 aiocqhttp + +QQ 表情 ID 参考: + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.command("test") +async def test(self, event: AstrMessageEvent): + from astrbot.api.message_components import Face, Plain + yield event.chain_result([Face(id=21), Plain("你好呀")]) +``` + +![发送 QQ 表情](https://files.astrbot.app/docs/source/images/plugin/image-5.png) + +### 控制事件传播 + +```python{6} +@filter.command("check_ok") +async def check_ok(self, event: AstrMessageEvent): + ok = self.check() # 自己的逻辑 + if not ok: + yield event.plain_result("检查失败") + event.stop_event() # 停止事件传播 +``` + +当事件停止传播,后续所有步骤将不会被执行。 + +假设有一个插件 A,A 终止事件传播之后所有后续操作都不会执行,比如执行其它插件的 handler、请求 LLM。 + +### 插件配置 + +> 大于等于 v3.4.15 + +随着插件功能的增加,可能需要定义一些配置以让用户自定义插件的行为。 + +AstrBot 提供了”强大“的配置解析和可视化功能。能够让用户在管理面板上直接配置插件,而不需要修改代码。 + +![image](https://files.astrbot.app/docs/source/images/plugin/QQ_1738149538737.png) + +**Schema 介绍** + +要注册配置,首先需要在您的插件目录下添加一个 `_conf_schema.json` 的 json 文件。 + +文件内容是一个 `Schema`(模式),用于表示配置。Schema 是 json 格式的,例如上图的 Schema 是: + +```json +{ + "token": { + "description": "Bot Token", + "type": "string", + "hint": "测试醒目提醒", + "obvious_hint": true + }, + "sub_config": { + "description": "测试嵌套配置", + "type": "object", + "hint": "xxxx", + "items": { + "name": { + "description": "testsub", + "type": "string", + "hint": "xxxx" + }, + "id": { + "description": "testsub", + "type": "int", + "hint": "xxxx" + }, + "time": { + "description": "testsub", + "type": "int", + "hint": "xxxx", + "default": 123 + } + } + } +} +``` + +- `type`: **此项必填**。配置的类型。支持 `string`, `text`, `int`, `float`, `bool`, `object`, `list`。当类型为 `text` 时,将会可视化为一个更大的可拖拽宽高的 textarea 组件,以适应大文本。 +- `description`: 可选。配置的描述。建议一句话描述配置的行为。 +- `hint`: 可选。配置的提示信息,表现在上图中右边的问号按钮,当鼠标悬浮在问号按钮上时显示。 +- `obvious_hint`: 可选。配置的 hint 是否醒目显示。如上图的 `token`。 +- `default`: 可选。配置的默认值。如果用户没有配置,将使用默认值。int 是 0,float 是 0.0,bool 是 False,string 是 "",object 是 {},list 是 []。 +- `items`: 可选。如果配置的类型是 `object`,需要添加 `items` 字段。`items` 的内容是这个配置项的子 Schema。理论上可以无限嵌套,但是不建议过多嵌套。 +- `invisible`: 可选。配置是否隐藏。默认是 `false`。如果设置为 `true`,则不会在管理面板上显示。 +- `options`: 可选。一个列表,如 `"options": ["chat", "agent", "workflow"]`。提供下拉列表可选项。 +- `editor_mode`: 可选。是否启用代码编辑器模式。需要 AstrBot >= `v3.5.10`, 低于这个版本不会报错,但不会生效。默认是 false。 +- `editor_language`: 可选。代码编辑器的代码语言,默认为 `json`。 +- `editor_theme`: 可选。代码编辑器的主题,可选值有 `vs-light`(默认), `vs-dark`。 +- `_special`: 可选。用于调用 AstrBot 提供的可视化提供商选取、人格选取、知识库选取等功能,详见下文。 + +其中,如果启用了代码编辑器,效果如下图所示: + +![editor_mode](https://files.astrbot.app/docs/source/images/plugin/image-6.png) + +![editor_mode_fullscreen](https://files.astrbot.app/docs/source/images/plugin/image-7.png) + +**_special** 字段仅 v4.0.0 之后可用。目前支持填写 `select_provider`, `select_provider_tts`, `select_provider_stt`, `select_persona`,用于让用户快速选择用户在 WebUI 上已经配置好的模型提供商、人设等数据。结果均为字符串。以 select_provider 为例,将呈现以下效果: + +![image](https://files.astrbot.app/docs/source/images/plugin/image.png) + +**使用配置** + +AstrBot 在载入插件时会检测插件目录下是否有 `_conf_schema.json` 文件,如果有,会自动解析配置并保存在 `data/config/_config.json` 下(依照 Schema 创建的配置文件实体),并在实例化插件类时传入给 `__init__()`。 + +```py +from astrbot.api import AstrBotConfig + +class ConfigPlugin(Star): + def __init__(self, context: Context, config: AstrBotConfig): # AstrBotConfig 继承自 Dict,拥有字典的所有方法 + super().__init__(context) + self.config = config + print(self.config) + + # 支持直接保存配置 + # self.config.save_config() # 保存配置 +``` + +**配置版本管理** + +如果您在发布不同版本时更新了 Schema,请注意,AstrBot 会递归检查 Schema 的配置项,如果发现配置文件中缺失了某个配置项,会自动添加默认值。但是 AstrBot 不会删除配置文件中**多余的**配置项,即使这个配置项在新的 Schema 中不存在(您在新的 Schema 中删除了这个配置项)。 + +### 文转图 + +#### 基本 + +AstrBot 支持将文字渲染成图片。 + +```python +@filter.command("image") # 注册一个 /image 指令,接收 text 参数。 +async def on_aiocqhttp(self, event: AstrMessageEvent, text: str): + url = await self.text_to_image(text) # text_to_image() 是 Star 类的一个方法。 + # path = await self.text_to_image(text, return_url = False) # 如果你想保存图片到本地 + yield event.image_result(url) + +``` + +![image](https://files.astrbot.app/docs/source/images/plugin/image-3.png) + +#### 自定义(基于 HTML) + +如果你觉得上面渲染出来的图片不够美观,你可以使用自定义的 HTML 模板来渲染图片。 + +AstrBot 支持使用 `HTML + Jinja2` 的方式来渲染文转图模板。 + +```py{7} +# 自定义的 Jinja2 模板,支持 CSS +TMPL = ''' +
+

Todo List

+ +
    +{% for item in items %} +
  • {{ item }}
  • +{% endfor %} +
+''' + +@filter.command("todo") +async def custom_t2i_tmpl(self, event: AstrMessageEvent): + options = {} # 可选择传入渲染选项。 + url = await self.html_render(TMPL, {"items": ["吃饭", "睡觉", "玩原神"]}, options=options) # 第二个参数是 Jinja2 的渲染数据 + yield event.image_result(url) +``` + +返回的结果: + +![image](https://files.astrbot.app/docs/source/images/plugin/fcc2dcb472a91b12899f617477adc5c7.png) + +这只是一个简单的例子。得益于 HTML 和 DOM 渲染器的强大性,你可以进行更复杂和更美观的的设计。除此之外,Jinja2 支持循环、条件等语法以适应列表、字典等数据结构。你可以从网上了解更多关于 Jinja2 的知识。 + +**图片渲染选项(options)**: + +请参考 Playwright 的 [screenshot](https://playwright.dev/python/docs/api/class-page#page-screenshot) API。 + +- `timeout` (float, optional): 截图超时时间. +- `type` (Literal["jpeg", "png"], optional): 截图图片类型. +- `quality` (int, optional): 截图质量,仅适用于 JPEG 格式图片. +- `omit_background` (bool, optional): 是否允许隐藏默认的白色背景,这样就可以截透明图了,仅适用于 PNG 格式 +- `full_page` (bool, optional): 是否截整个页面而不是仅设置的视口大小,默认为 True. +- `clip` (dict, optional): 截图后裁切的区域。参考 Playwright screenshot API。 +- `animations`: (Literal["allow", "disabled"], optional): 是否允许播放 CSS 动画. +- `caret`: (Literal["hide", "initial"], optional): 当设置为 hide 时,截图时将隐藏文本插入符号,默认为 hide. +- `scale`: (Literal["css", "device"], optional): 页面缩放设置. 当设置为 css 时,则将设备分辨率与 CSS 中的像素一一对应,在高分屏上会使得截图变小. 当设置为 device 时,则根据设备的屏幕缩放设置或当前 Playwright 的 Page/Context 中的 device_scale_factor 参数来缩放. +- `mask` (List["Locator"]], optional): 指定截图时的遮罩的 Locator。元素将被一颜色为 #FF00FF 的框覆盖. + +### 会话控制 + +> 大于等于 v3.4.36 + +为什么需要会话控制?考虑一个 成语接龙 插件,某个/群用户需要和机器人进行多次对话,而不是一次性的指令。这时候就需要会话控制。 + +```txt +用户: /成语接龙 +机器人: 请发送一个成语 +用户: 一马当先 +机器人: 先见之明 +用户: 明察秋毫 +... +``` + +AstrBot 提供了开箱即用的会话控制功能: + +导入: + +```py +import astrbot.api.message_components as Comp +from astrbot.core.utils.session_waiter import ( + session_waiter, + SessionController, +) +``` + +handler 内的代码可以如下: + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.command("成语接龙") +async def handle_empty_mention(self, event: AstrMessageEvent): + """成语接龙具体实现""" + try: + yield event.plain_result("请发送一个成语~") + + # 具体的会话控制器使用方法 + @session_waiter(timeout=60, record_history_chains=False) # 注册一个会话控制器,设置超时时间为 60 秒,不记录历史消息链 + async def empty_mention_waiter(controller: SessionController, event: AstrMessageEvent): + idiom = event.message_str # 用户发来的成语,假设是 "一马当先" + + if idiom == "退出": # 假设用户想主动退出成语接龙,输入了 "退出" + await event.send(event.plain_result("已退出成语接龙~")) + controller.stop() # 停止会话控制器,会立即结束。 + return + + if len(idiom) != 4: # 假设用户输入的不是4字成语 + await event.send(event.plain_result("成语必须是四个字的呢~")) # 发送回复,不能使用 yield + return + # 退出当前方法,不执行后续逻辑,但此会话并未中断,后续的用户输入仍然会进入当前会话 + + # ... + message_result = event.make_result() + message_result.chain = [Comp.Plain("先见之明")] # import astrbot.api.message_components as Comp + await event.send(message_result) # 发送回复,不能使用 yield + + controller.keep(timeout=60, reset_timeout=True) # 重置超时时间为 60s,如果不重置,则会继续之前的超时时间计时。 + + # controller.stop() # 停止会话控制器,会立即结束。 + # 如果记录了历史消息链,可以通过 controller.get_history_chains() 获取历史消息链 + + try: + await empty_mention_waiter(event) + except TimeoutError as _: # 当超时后,会话控制器会抛出 TimeoutError + yield event.plain_result("你超时了!") + except Exception as e: + yield event.plain_result("发生错误,请联系管理员: " + str(e)) + finally: + event.stop_event() + except Exception as e: + logger.error("handle_empty_mention error: " + str(e)) +``` + +当激活会话控制器后,该发送人之后发送的消息会首先经过上面你定义的 `empty_mention_waiter` 函数处理,直到会话控制器被停止或者超时。 + +#### SessionController + +用于开发者控制这个会话是否应该结束,并且可以拿到历史消息链。 + +- keep(): 保持这个会话 + - timeout (float): 必填。会话超时时间。 + - reset_timeout (bool): 设置为 True 时, 代表重置超时时间, timeout 必须 > 0, 如果 <= 0 则立即结束会话。设置为 False 时, 代表继续维持原来的超时时间, 新 timeout = 原来剩余的 timeout + timeout (可以 < 0) +- stop(): 结束这个会话 +- get_history_chains() -> List[List[Comp.BaseMessageComponent]]: 获取历史消息链 + +#### 自定义会话 ID 算子 + +默认情况下,AstrBot 会话控制器会将基于 `sender_id` (发送人的 ID)作为识别不同会话的标识,如果想将一整个群作为一个会话,则需要自定义会话 ID 算子。 + +```py +import astrbot.api.message_components as Comp +from astrbot.core.utils.session_waiter import ( + session_waiter, + SessionFilter, + SessionController, +) + +# 沿用上面的 handler +# ... +class CustomFilter(SessionFilter): + def filter(self, event: AstrMessageEvent) -> str: + return event.get_group_id() if event.get_group_id() else event.unified_msg_origin + +await empty_mention_waiter(event, session_filter=CustomFilter()) # 这里传入 session_filter +# ... +``` + +这样之后,当群内一个用户发送消息后,会话控制器会将这个群作为一个会话,群内其他用户发送的消息也会被认为是同一个会话。 + +甚至,可以使用这个特性来让群内组队! + +### AI + +#### 通过提供商调用 LLM + +获取提供商有以下几种方式: + +- 获取当前使用的大语言模型提供商: `self.context.get_using_provider(umo=event.unified_msg_origin)`。 +- 根据 ID 获取大语言模型提供商: `self.context.get_provider_by_id(provider_id="xxxx")`。 +- 获取所有大语言模型提供商: `self.context.get_all_providers()`。 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.command("test") +async def test(self, event: AstrMessageEvent): + # func_tools_mgr = self.context.get_llm_tool_manager() + prov = self.context.get_using_provider(umo=event.unified_msg_origin) + if prov: + llm_resp = await provider.text_chat( + prompt="Hi!", + context=[ + {"role": "user", "content": "balabala"}, + {"role": "assistant", "content": "response balabala"} + ], + system_prompt="You are a helpful assistant." + ) + print(llm_resp) +``` + +`Provider.text_chat()` 用于请求 LLM。其返回 `LLMResponse` 方法。除了上面的三个参数,其还支持: + +- `func_tool`(ToolSet): 可选。用于传入函数工具。参考 [函数工具](#函数工具)。 +- `image_urls`(List[str]): 可选。用于传入请求中带有的图片 URL 列表。支持文件路径。 +- `model`(str): 可选。用于强制指定使用的模型。默认使用这个提供商默认配置的模型。 +- `tool_calls_result`(dict): 可选。用于传入工具调用的结果。 + +::: details LLMResponse 类型定义 + +```py + +@dataclass +class LLMResponse: + role: str + """角色, assistant, tool, err""" + result_chain: MessageChain = None + """返回的消息链""" + tools_call_args: List[Dict[str, any]] = field(default_factory=list) + """工具调用参数""" + tools_call_name: List[str] = field(default_factory=list) + """工具调用名称""" + tools_call_ids: List[str] = field(default_factory=list) + """工具调用 ID""" + + raw_completion: ChatCompletion = None + _new_record: Dict[str, any] = None + + _completion_text: str = "" + + is_chunk: bool = False + """是否是流式输出的单个 Chunk""" + + def __init__( + self, + role: str, + completion_text: str = "", + result_chain: MessageChain = None, + tools_call_args: List[Dict[str, any]] = None, + tools_call_name: List[str] = None, + tools_call_ids: List[str] = None, + raw_completion: ChatCompletion = None, + _new_record: Dict[str, any] = None, + is_chunk: bool = False, + ): + """初始化 LLMResponse + + Args: + role (str): 角色, assistant, tool, err + completion_text (str, optional): 返回的结果文本,已经过时,推荐使用 result_chain. Defaults to "". + result_chain (MessageChain, optional): 返回的消息链. Defaults to None. + tools_call_args (List[Dict[str, any]], optional): 工具调用参数. Defaults to None. + tools_call_name (List[str], optional): 工具调用名称. Defaults to None. + raw_completion (ChatCompletion, optional): 原始响应, OpenAI 格式. Defaults to None. + """ + if tools_call_args is None: + tools_call_args = [] + if tools_call_name is None: + tools_call_name = [] + if tools_call_ids is None: + tools_call_ids = [] + + self.role = role + self.completion_text = completion_text + self.result_chain = result_chain + self.tools_call_args = tools_call_args + self.tools_call_name = tools_call_name + self.tools_call_ids = tools_call_ids + self.raw_completion = raw_completion + self._new_record = _new_record + self.is_chunk = is_chunk + + @property + def completion_text(self): + if self.result_chain: + return self.result_chain.get_plain_text() + return self._completion_text + + @completion_text.setter + def completion_text(self, value): + if self.result_chain: + self.result_chain.chain = [ + comp + for comp in self.result_chain.chain + if not isinstance(comp, Comp.Plain) + ] # 清空 Plain 组件 + self.result_chain.chain.insert(0, Comp.Plain(value)) + else: + self._completion_text = value + + def to_openai_tool_calls(self) -> List[Dict]: + """将工具调用信息转换为 OpenAI 格式""" + ret = [] + for idx, tool_call_arg in enumerate(self.tools_call_args): + ret.append( + { + "id": self.tools_call_ids[idx], + "function": { + "name": self.tools_call_name[idx], + "arguments": json.dumps(tool_call_arg), + }, + "type": "function", + } + ) + return ret +``` + +::: + +#### 获取其他类型的提供商 + +> 嵌入、重排序 没有 “当前使用”。这两个提供商主要用于知识库。 + +- 获取当前使用的语音识别提供商(STTProvider): `self.context.get_using_stt_provider(umo=event.unified_msg_origin)`。 +- 获取当前使用的语音合成提供商(TTSProvider): `self.context.get_using_tts_provider(umo=event.unified_msg_origin)`。 +- 获取所有语音识别提供商: `self.context.get_all_stt_providers()`。 +- 获取所有语音合成提供商: `self.context.get_all_tts_providers()`。 +- 获取所有嵌入提供商: `self.context.get_all_embedding_providers()`。 + +::: details STTProvider / TTSProvider / EmbeddingProvider 类型定义 + +```py +class TTSProvider(AbstractProvider): + def __init__(self, provider_config: dict, provider_settings: dict) -> None: + super().__init__(provider_config) + self.provider_config = provider_config + self.provider_settings = provider_settings + + @abc.abstractmethod + async def get_audio(self, text: str) -> str: + """获取文本的音频,返回音频文件路径""" + raise NotImplementedError() + + +class EmbeddingProvider(AbstractProvider): + def __init__(self, provider_config: dict, provider_settings: dict) -> None: + super().__init__(provider_config) + self.provider_config = provider_config + self.provider_settings = provider_settings + + @abc.abstractmethod + async def get_embedding(self, text: str) -> list[float]: + """获取文本的向量""" + ... + + @abc.abstractmethod + async def get_embeddings(self, text: list[str]) -> list[list[float]]: + """批量获取文本的向量""" + ... + + @abc.abstractmethod + def get_dim(self) -> int: + """获取向量的维度""" + ... + +class STTProvider(AbstractProvider): + def __init__(self, provider_config: dict, provider_settings: dict) -> None: + super().__init__(provider_config) + self.provider_config = provider_config + self.provider_settings = provider_settings + + @abc.abstractmethod + async def get_text(self, audio_url: str) -> str: + """获取音频的文本""" + raise NotImplementedError() +``` + +::: + +#### 函数工具 + +函数工具给了大语言模型调用外部工具的能力。在 AstrBot 中,函数工具有多种定义方式。 + +##### 以类的形式(推荐) + +推荐在插件目录下新建 `tools` 文件夹,然后在其中编写工具类: + +`tools/search.py`: + +```py +from astrbot.api import FunctionTool +from astrbot.api.event import AstrMessageEvent +from dataclasses import dataclass, field + +@dataclass +class HelloWorldTool(FunctionTool): + name: str = "hello_world" # 工具名称 + description: str = "Say hello to the world." # 工具描述 + parameters: dict = field( + default_factory=lambda: { + "type": "object", + "properties": { + "greeting": { + "type": "string", + "description": "The greeting message.", + }, + }, + "required": ["greeting"], + } + ) # 工具参数定义,见 OpenAI 官网或 https://json-schema.org/understanding-json-schema/ + + async def run( + self, + event: AstrMessageEvent, # 必须包含此 event 参数在前面,用于获取上下文 + greeting: str, # 工具参数,必须与 parameters 中定义的参数名一致 + ): + return f"{greeting}, World!" # 也支持 mcp.types.CallToolResult 类型 +``` + +要将上述工具注册到 AstrBot,可以在插件主文件的 `__init__.py` 中添加以下代码: + +```py +from .tools.search import SearchTool + +class MyPlugin(Star): + def __init__(self, context: Context): + super().__init__(context) + # >= v4.5.1 使用: + self.context.add_llm_tools(HelloWorldTool(), SecondTool(), ...) + + # < v4.5.1 之前使用: + tool_mgr = self.context.provider_manager.llm_tools + tool_mgr.func_list.append(HelloWorldTool()) +``` + +##### 以装饰器的形式 + +这个形式定义的工具函数会被自动加载到 AstrBot Core 中,在 Core 请求大模型时会被自动带上。 + +请务必按照以下格式编写一个工具(包括**函数注释**,AstrBot 会解析该函数注释,请务必将注释格式写对) + +```py{3,4,5,6,7} +@filter.llm_tool(name="get_weather") # 如果 name 不填,将使用函数名 +async def get_weather(self, event: AstrMessageEvent, location: str) -> MessageEventResult: + '''获取天气信息。 + + Args: + location(string): 地点 + ''' + resp = self.get_weather_from_api(location) + yield event.plain_result("天气信息: " + resp) +``` + +在 `location(string): 地点` 中,`location` 是参数名,`string` 是参数类型,`地点` 是参数描述。 + +支持的参数类型有 `string`, `number`, `object`, `boolean`。 + +> [!NOTE] +> 对于装饰器注册的 llm_tool,如果需要调用 Provider.text_chat(),func_tool(ToolSet 类型) 可以通过以下方式获取: +> +> ```py +> func_tool = self.context.get_llm_tool_manager() # 获取 AstrBot 的 LLM Tool Manager,包含了所有插件和 MCP 注册的 Tool +> tool = func_tool.get_func("xxx") +> if tool: +> tool_set = ToolSet() +> tool_set.add_tool(tool) +> ``` + +#### 对话管理器 ConversationManager + +**获取会话当前的 LLM 对话历史** + +```py +from astrbot.core.conversation_mgr import Conversation + +uid = event.unified_msg_origin +conv_mgr = self.context.conversation_manager +curr_cid = await conv_mgr.get_curr_conversation_id(uid) +conversation = await conv_mgr.get_conversation(uid, curr_cid) # Conversation +``` + +::: details Conversation 类型定义 + +```py +@dataclass +class Conversation: + """LLM 对话类 + + 对于 WebChat,history 存储了包括指令、回复、图片等在内的所有消息。 + 对于其他平台的聊天,不存储非 LLM 的回复(因为考虑到已经存储在各自的平台上)。 + + 在 v4.0.0 版本及之后,WebChat 的历史记录被迁移至 `PlatformMessageHistory` 表中, + """ + + platform_id: str + user_id: str + cid: str + """对话 ID, 是 uuid 格式的字符串""" + history: str = "" + """字符串格式的对话列表。""" + title: str | None = "" + persona_id: str | None = "" + """对话当前使用的人格 ID""" + created_at: int = 0 + updated_at: int = 0 +``` + +::: + +**所有方法** + +##### `new_conversation` + +- **Usage** + 在当前会话中新建一条对话,并自动切换为该对话。 +- **Arguments** + - `unified_msg_origin: str` – 形如 `platform_name:message_type:session_id` + - `platform_id: str | None` – 平台标识,默认从 `unified_msg_origin` 解析 + - `content: list[dict] | None` – 初始历史消息 + - `title: str | None` – 对话标题 + - `persona_id: str | None` – 绑定的 persona ID +- **Returns** + `str` – 新生成的 UUID 对话 ID + +##### `switch_conversation` + +- **Usage** + 将会话切换到指定的对话。 +- **Arguments** + - `unified_msg_origin: str` + - `conversation_id: str` +- **Returns** + `None` + +##### `delete_conversation` + +- **Usage** + 删除会话中的某条对话;若 `conversation_id` 为 `None`,则删除当前对话。 +- **Arguments** + - `unified_msg_origin: str` + - `conversation_id: str | None` +- **Returns** + `None` + +##### `get_curr_conversation_id` + +- **Usage** + 获取当前会话正在使用的对话 ID。 +- **Arguments** + - `unified_msg_origin: str` +- **Returns** + `str | None` – 当前对话 ID,不存在时返回 `None` + +##### `get_conversation` + +- **Usage** + 获取指定对话的完整对象;若不存在且 `create_if_not_exists=True` 则自动创建。 +- **Arguments** + - `unified_msg_origin: str` + - `conversation_id: str` + - `create_if_not_exists: bool = False` +- **Returns** + `Conversation | None` + +##### `get_conversations` + +- **Usage** + 拉取用户或平台下的全部对话列表。 +- **Arguments** + - `unified_msg_origin: str | None` – 为 `None` 时不过滤用户 + - `platform_id: str | None` +- **Returns** + `List[Conversation]` + +##### `get_filtered_conversations` + +- **Usage** + 分页 + 关键词搜索对话。 +- **Arguments** + - `page: int = 1` + - `page_size: int = 20` + - `platform_ids: list[str] | None` + - `search_query: str = ""` + - `**kwargs` – 透传其他过滤条件 +- **Returns** + `tuple[list[Conversation], int]` – 对话列表与总数 + +##### `update_conversation` + +- **Usage** + 更新对话的标题、历史记录或 persona_id。 +- **Arguments** + - `unified_msg_origin: str` + - `conversation_id: str | None` – 为 `None` 时使用当前对话 + - `history: list[dict] | None` + - `title: str | None` + - `persona_id: str | None` +- **Returns** + `None` + +##### `get_human_readable_context` + +- **Usage** + 生成分页后的人类可读对话上下文,方便展示或调试。 +- **Arguments** + - `unified_msg_origin: str` + - `conversation_id: str` + - `page: int = 1` + - `page_size: int = 10` +- **Returns** + `tuple[list[str], int]` – 当前页文本列表与总页数 + +```py +import json + +context = json.loads(conversation.history) +``` + +#### 人格设定管理器 PersonaManager + +`PersonaManager` 负责统一加载、缓存并提供所有人格(Persona)的增删改查接口,同时兼容 AstrBot 4.x 之前的旧版人格格式(v3)。 +初始化时会自动从数据库读取全部人格,并生成一份 v3 兼容数据,供旧代码无缝使用。 + +```py +persona_mgr = self.context.persona_manager +``` + +##### `get_persona` + +- **Usage** + 获取根据人格 ID 获取人格数据。 +- **Arguments** + - `persona_id: str` – 人格 ID +- **Returns** + `Persona` – 人格数据,若不存在则返回 None +- **Raises** + `ValueError` – 当不存在时抛出 + +##### `get_all_personas` + +- **Usage** + 一次性获取数据库中所有人格。 +- **Returns** + `list[Persona]` – 人格列表,可能为空 + +##### `create_persona` + +- **Usage** + 新建人格并立即写入数据库,成功后自动刷新本地缓存。 +- **Arguments** + - `persona_id: str` – 新人格 ID(唯一) + - `system_prompt: str` – 系统提示词 + - `begin_dialogs: list[str]` – 可选,开场对话(偶数条,user/assistant 交替) + - `tools: list[str]` – 可选,允许使用的工具列表;`None`=全部工具,`[]`=禁用全部 +- **Returns** + `Persona` – 新建后的人格对象 +- **Raises** + `ValueError` – 若 `persona_id` 已存在 + +##### `update_persona` + +- **Usage** + 更新现有人格的任意字段,并同步到数据库与缓存。 +- **Arguments** + - `persona_id: str` – 待更新的人格 ID + - `system_prompt: str` – 可选,新的系统提示词 + - `begin_dialogs: list[str]` – 可选,新的开场对话 + - `tools: list[str]` – 可选,新的工具列表;语义同 `create_persona` +- **Returns** + `Persona` – 更新后的人格对象 +- **Raises** + `ValueError` – 若 `persona_id` 不存在 + +##### `delete_persona` + +- **Usage** + 删除指定人格,同时清理数据库与缓存。 +- **Arguments** + - `persona_id: str` – 待删除的人格 ID +- **Raises** + `Valueable` – 若 `persona_id` 不存在 + +##### `get_default_persona_v3` + +- **Usage** + 根据当前会话配置,获取应使用的默认人格(v3 格式)。 + 若配置未指定或指定的人格不存在,则回退到 `DEFAULT_PERSONALITY`。 +- **Arguments** + - `umo: str | MessageSession | None` – 会话标识,用于读取用户级配置 +- **Returns** + `Personality` – v3 格式的默认人格对象 + +::: details Persona / Personality 类型定义 + +```py + +class Persona(SQLModel, table=True): + """Persona is a set of instructions for LLMs to follow. + + It can be used to customize the behavior of LLMs. + """ + + __tablename__ = "personas" + + id: int = Field(primary_key=True, sa_column_kwargs={"autoincrement": True}) + persona_id: str = Field(max_length=255, nullable=False) + system_prompt: str = Field(sa_type=Text, nullable=False) + begin_dialogs: Optional[list] = Field(default=None, sa_type=JSON) + """a list of strings, each representing a dialog to start with""" + tools: Optional[list] = Field(default=None, sa_type=JSON) + """None means use ALL tools for default, empty list means no tools, otherwise a list of tool names.""" + created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) + updated_at: datetime = Field( + default_factory=lambda: datetime.now(timezone.utc), + sa_column_kwargs={"onupdate": datetime.now(timezone.utc)}, + ) + + __table_args__ = ( + UniqueConstraint( + "persona_id", + name="uix_persona_id", + ), + ) + + +class Personality(TypedDict): + """LLM 人格类。 + + 在 v4.0.0 版本及之后,推荐使用上面的 Persona 类。并且, mood_imitation_dialogs 字段已被废弃。 + """ + + prompt: str + name: str + begin_dialogs: list[str] + mood_imitation_dialogs: list[str] + """情感模拟对话预设。在 v4.0.0 版本及之后,已被废弃。""" + tools: list[str] | None + """工具列表。None 表示使用所有工具,空列表表示不使用任何工具""" +``` + +::: + +### 其他 + +#### 配置文件 + +##### 默认配置文件 + +```py +config = self.context.get_config() +``` + +不建议修改默认配置文件,建议只读取。 + +##### 会话配置文件 + +v4.0.0 后,AstrBot 支持会话粒度的多配置文件。 + +```py +umo = event.unified_msg_origin +config = self.context.get_config(umo=umo) +``` + +#### 获取消息平台实例 + +> v3.4.34 后 + +```python +from astrbot.api.event import filter, AstrMessageEvent + +@filter.command("test") +async def test_(self, event: AstrMessageEvent): + from astrbot.api.platform import AiocqhttpAdapter # 其他平台同理 + platform = self.context.get_platform(filter.PlatformAdapterType.AIOCQHTTP) + assert isinstance(platform, AiocqhttpAdapter) + # platform.get_client().api.call_action() +``` + +#### 调用 QQ 协议端 API + +```py +@filter.command("helloworld") +async def helloworld(self, event: AstrMessageEvent): + if event.get_platform_name() == "aiocqhttp": + # qq + from astrbot.core.platform.sources.aiocqhttp.aiocqhttp_message_event import AiocqhttpMessageEvent + assert isinstance(event, AiocqhttpMessageEvent) + client = event.bot # 得到 client + payloads = { + "message_id": event.message_obj.message_id, + } + ret = await client.api.call_action('delete_msg', **payloads) # 调用 协议端 API + logger.info(f"delete_msg: {ret}") +``` + +关于 CQHTTP API,请参考如下文档: + +Napcat API 文档: + +Lagrange API 文档: + +#### 载入的所有插件 + +```py +plugins = self.context.get_all_stars() # 返回 StarMetadata 包含了插件类实例、配置等等 +``` + +#### 注册一个异步任务 + +直接在 **init**() 中使用 `asyncio.create_task()` 即可。 + +```py +import asyncio + +class TaskPlugin(Star): + def __init__(self, context: Context): + super().__init__(context) + asyncio.create_task(self.my_task()) + + async def my_task(self): + await asyncio.sleep(1) + print("Hello") +``` + +#### 获取加载的所有平台 + +```py +from astrbot.api.platform import Platform +platforms = self.context.platform_manager.get_insts() # List[Platform] +``` diff --git a/docs/zh/faq.md b/docs/zh/faq.md new file mode 100644 index 000000000..f9657884c --- /dev/null +++ b/docs/zh/faq.md @@ -0,0 +1,116 @@ +# FAQ + +## 管理面板相关 + +### 当管理面板打开时遇到 404 错误 + +在 [release](https://github.com/AstrBotDevs/AstrBot/releases) 页面下载 `dist.zip`,解压拖到 `AstrBot/data` 下。还不行请重启电脑(来自群里的反馈) + +### 管理面板的密码忘记了 + +如果你忘记了 AstrBot 管理面板的密码,你可以在 `AstrBot/data/cmd_config.json` 配置文件中找到 `"dashboard"` 字段进行修改,其中 `"username"` 是你的用户名,`"password"` 是你的密码(经过 MD5 加密)。 + +如果想要修改账号密码,你可以这样做: + +1. 修改 `"username"` 字段,注意保留 `""`;如果不想修改用户名,可以不修改 +2. 进入网站:[在线 MD5 生成](https://www.metools.info/code/c26.html) +3. 在转换前文本框输入你的新密码 +4. 选择 MD5 加密(32 位),请确认选择 32 位选项 +5. 将转换后的字符粘贴至配置文件,注意保留 `""`, 且字母使用小写 + +## AstrBot 使用相关 + +### 如何让 AstrBot 控制我的 Mac / Windows / Linux 电脑? + +1. 在 AstrBot WebUI 的 `配置 -> 普通配置` 中,找到 `使用电脑能力`,运行环境选择 `local`。 +2. 在 `配置 -> 其他配置` 中,找到 `管理员 ID 列表`,添加你的用户 ID(可以通过 `/sid` 指令获取)。 +3. 右下角保存配置 + +> [!TIP] +> AstrBot 为了安全起见,运行环境选择 `local` 时,默认仅允许 AstrBot 管理员使用电脑能力。 +> 运行环境可以选择 `sandbox`,此时所有用户都可以使用电脑能力(在一个隔离的沙箱中)。详情请看 [AstrBot 沙箱环境](/use/astrbot-agent-sandbox.md) + +### 通过 AstrBot 桌面客户端安装的 AstrBot,data 目录在哪? + +在家目录下的 `.astrbot` 目录下。 + +- Windows: `C:\Users\你的用户名\.astrbot` +- MacOS / Linux: `/Users/你的用户名/.astrbot` 或者 `/home/你的用户名/.astrbot` + +### 通过 AstrBot Launcher 安装的 AstrBot,data 目录在哪? + +如果是旧版本的 AstrBot Launcher(Powershell),data 目录就在 Launcher bat 脚本的同级目录下。 + +如果是新版本的 AstrBot Launcher(可视化),data 目录在家目录下的 `.astrbot_launcher` 目录下。 + +- Windows: `C:\Users\你的用户名\.astrbot_launcher` +- MacOS / Linux: `/Users/你的用户名/.astrbot_launcher` 或者 `/home/你的用户名/.astrbot_launcher` + +### 机器人在群聊无法聊天 + +1. 群聊情况下,由于防止消息泛滥,不会对每条监听到的消息都回复,请尝试 @ 机器人或者使用唤醒词来聊天,比如默认的 `/`,输入 `/你好`。 + +### 没有权限操作管理员指令 + +1. `/reset, /persona, /dashboard_update, /op, /deop, /wl, /dewl` 是默认的管理员指令。可以通过 `/sid` 指令得到用户的 ID,然后在 `配置` -> `其他配置` 中添加到管理员 ID 名单中。 + +### 本地渲染 Markdown 图片(t2i)时中文乱码 + +可以自定义字体。详见 -> [#957](https://github.com/AstrBotDevs/AstrBot/issues/957#issuecomment-2749981802) + +推荐 [Maple Mono](https://github.com/subframe7536/maple-font) 字体。 + +### API 返回的 completion 无法解析 + +这是由于供应商的 API 返回了空文本,尝试以下步骤: + +1. 检查 API Key 是否仍然有效 +2. 检查是否达到 API 调用限制或配额 +3. 检查网络连接 +4. 尝试 `reset` +5. 降低最大对话次数设置 +6. 切换使用同一供应商的其他模型,或不同供应商的模型 + +## 插件相关 + +### 插件安装不上 + +1. 插件通过 GitHub 安装,在国内访问 GitHub 确实有时候连不上。可以挂代理,然后进入 `其他配置` -> `HTTP 代理` 设置代理,或者直接下载插件压缩包后上传。 + +### 安装插件后报错 `No module named 'xxx'` + +![image](https://files.astrbot.app/docs/source/images/faq/image.png) + +这个是因为插件依赖的库没有被正常安装。一般情况下,AstrBot 会在安装好插件后自动为插件安装依赖库,如果出现了以下情况可能造成安装失败: + +1. 网络问题导致依赖库无法下载 +2. 插件作者没有填写 `requirements.txt` 文件 +3. Python 版本不兼容 + +解决方法: + +结合报错信息,参考插件的 README 手动安装依赖库。你可以在 AstrBot WebUI 的 `平台日志` -> `安装 Pip 库` 中安装依赖库。 + +![image](https://files.astrbot.app/docs/source/images/faq/image-1.png) + +如果发现插件作者没有填写 `requirements.txt` 文件,请在插件仓库提交 Issue,提醒作者补充。 + + +## OneBot v11 实现端 NapCat 连接相关 + +### 我明明按照文档的步骤做了,为什么 NapCat 连不上 Astrbot? + +1. 如果你两个**全都**是使用 Docker 部署,请尝试在终端运行: + +```bash +sudo docker network create newnet # 创建新网络 +sudo docker network connect newnet astrbot +sudo docker network connect newnet napcat # 让两个容器连到一起 +sudo docker restart astrbot +sudo docker restart napcat # 重启容器 +``` + +运行无报错则回到 NapCat 的 WebUI,网络配置中,将你之前填写的 `ws://127.0.0.1:6199/ws` 修改为 `ws://astrbot:6199/ws`。 + +2. 如果只有 NapCat 是 Docker 部署,请将 NapCat 的 WebUI 网络配置中的 `ws://127.0.0.1:6199/ws` 修改为 `ws://宿主机IP:6199/ws`(宿主机 IP 请自行搜索如何查看)。 +3. 如果都不是 Docker 部署,则请将 NapCat 的 WebUI 网络配置中的 `ws://127.0.0.1:6199/ws` 修改为 `ws://localhost:6199/ws` 或 `ws://127.0.0.1:6199/ws`。 diff --git a/docs/zh/index.md b/docs/zh/index.md new file mode 100644 index 000000000..a62caef61 --- /dev/null +++ b/docs/zh/index.md @@ -0,0 +1,31 @@ +--- +# https://vitepress.dev/reference/default-theme-home-page +layout: home + +hero: + name: >- + Soulter%2FAstrBot | Trendshift + text: "Agentic AI 助手,服务个人与群聊" + tagline: 连接 IM / 1000+ 插件扩展 / 通用 Agent 能力编排 + actions: + - theme: brand + text: 快速开始 + link: /what-is-astrbot + - theme: alt + text: GitHub 仓库 + link: https://github.com/AstrBotDevs/AstrBot + +features: + - icon: ✨ + title: 多平台支持 + details: 可集成到 QQ、企业微信、飞书、Telegram、Discord 等多个聊天平台 + - icon: 😌 + title: 方便易用 + details: 支持多种方式部署,无需复杂的配置,配备高度可视化的管理面板 + - icon: 🧩 + title: 高扩展性 + details: 灵活易用的插件系统。 + - icon: 🌟 + title: AI + details: 支持 OpenAI、Anthropic、Gemini 等多种大模型接入,内置知识库和 Agent 智能体 +--- diff --git a/docs/zh/ospp/2025.md b/docs/zh/ospp/2025.md new file mode 100644 index 000000000..b4511363f --- /dev/null +++ b/docs/zh/ospp/2025.md @@ -0,0 +1,31 @@ +# 开源之夏 2025 + +**开源之夏**是由中国科学院软件研究所“开源软件供应链点亮计划”发起并长期支持的一项暑期开源活动,旨在鼓励在校学生积极参与开源软件的开发维护,培养和发掘更多优秀的开发者,促进优秀开源软件社区的蓬勃发展,助力开源软件供应链建设。具体活动信息请参考 [开源之夏官网](https://summer-ospp.ac.cn/)。 + +AstrBot 社区有幸作为开源社区参与了本次活动,下面列出了目前我们已经发布的项目,欢迎感兴趣的同学们参与。 + +## 插件数据存储逻辑优化 + +目前,AstrBot 插件系统在数据存储方面缺乏一致的架构。部分插件使用 SharedPreference 存储机制和 JSON 格式进行数据持久化。这种多样化的存储方式导致了存储逻辑的不统一,既影响了数据的安全性,也增加了插件间的兼容性问题。此外,缺乏标准化的接口使得插件的数据存储和访问方式各异,给系统的维护和扩展带来挑战。本项目旨在重构当前存储方案,引入更安全且高效的数据存储机制,并设计一个统一的插件数据接口模型,规范插件的数据存储与访问,提升系统的安全性、可扩展性和可维护性,为未来插件的开发与管理提供坚实基础。 + +**项目链接**:[插件数据存储逻辑优化](https://summer-ospp.ac.cn/org/prodetail/253550342?lang=zh&list=pro) + +**难度**:进阶 + +**导师**:[Soulter](https://github.com/Soulter) + +**期望完成时间**:210 小时 + +**项目产出要求**: + +1. 设计并实现统一且高效的插件数据存储接口模型,规范插件的数据存储; +2. 重构当前 SharedPreference 的存储逻辑,采用更安全的存储方式; +3. 补充相关技术文档。 + +**项目技术要求**: + +1. 熟悉 Python、Javascript 语言及 asyncio 异步编程技术; +2. 熟悉 SQLite 等关系型数据库相关开发; +3. 熟悉 AstrBot 框架及插件开发。 + +**成果仓库**:[https://github.com/AstrBotDevs/AstrBot](https://github.com/AstrBotDevs/AstrBot) diff --git a/docs/zh/others/github-proxy.md b/docs/zh/others/github-proxy.md new file mode 100644 index 000000000..88daea179 --- /dev/null +++ b/docs/zh/others/github-proxy.md @@ -0,0 +1,32 @@ +# 自建 GitHub 加速服务 + +如果发现升级 AstrBot、安装/更新插件时总是因为网络问题安装失败,您可以通过自建 GitHub 加速服务来实现高速访问。 + +![image](https://files.astrbot.app/docs/source/images/github-proxy/image.png) + +## 使用 `lxfight/astrbot2github` 自建加速服务 + +> 预计部署用时: `2` 分钟 + +0. 打开 [lxfight/astrbot2github](https://github.com/lxfight/astrbot2github) +1. **(可选但推荐)** 给本项目点个 [**Star ⭐**](https://github.com/lxfight/astrbot2github),你的支持是作者更新和维护的动力! +2. **Fork 本项目**: 点击页面右上角的 [**Fork**](https://github.com/lxfight/astrbot2github/fork) 按钮,将此项目复刻到你自己的 GitHub 账号下。 +3. **登录 Deno Deploy**: 访问 [Deno Deploy](https://dash.deno.com/) 并使用你的 GitHub 账号登录。 +4. **创建新项目**: + * 点击 **New Project** (或 **新建项目**)。 + * 选择 **Deploy from GitHub repository** (带有 GitHub 图标的那个选项)。 + * 授权 Deno Deploy 访问你的 GitHub 仓库。 +5. **选择仓库**: 在仓库列表中,选择刚刚 Fork 的 `astrbot2github` 项目。 +6. **配置部署**: + * **Production Branch**: 保持默认 (`main`) 即可。 + * **Entrypoint**: **这是关键步骤!** 点击下拉框,找到并选择 `deno_index.ts` 文件作为入口点。 + * **Project Name**: Deno 会自动生成一个项目名称,这将是你的服务地址的一部分。你可以保留自动生成的名称 (例如 `fluffy-donkey-12`),也可以自定义名称 (例如 `my-astrbot-proxy`)。 +7. **开始部署**: 确认设置无误后,点击 **Link** 或 **Deploy** 按钮。稍等片刻即可完成。 +8. **获取服务地址**: 部署成功后,页面会显示你的服务地址,格式为 `https://<第6步设置的项目名>.deno.dev`。复制这个地址。 +9. **配置 AstrBot**: + * 回到你的 AstrBot WebUI。 + * 进入 **设置 (Settings)** 页面。 + * 找到 **GitHub 加速地址 (GitHub Proxy)** + * 将**第 8 步**复制的 Deno 服务地址完整粘贴进去。 + +🎉 **完成!** 现在 AstrBot 在访问插件市场和下载插件时,将会通过你刚刚部署的 Deno 服务进行代理。 diff --git a/docs/zh/others/ipv6.md b/docs/zh/others/ipv6.md new file mode 100644 index 000000000..29aabb7dc --- /dev/null +++ b/docs/zh/others/ipv6.md @@ -0,0 +1,35 @@ +# IPv6支持 + +目前ipv6普及度很高,很多家庭宽带都支持ipv6,且具有公网ipv6地址,本教程将介绍如何在astrbot中充分利用ipv6。 + +# 准备 + +如果你是服务器环境,可以直接跳过以下内容,因为无需过多配置即可通过指定host,从而通过公网ipv6访问astrbot服务 + +如果你是家庭宽带环境,处于安全考虑,从外部无法直接访问,需按照以下步骤修改 +这里以中国电信天翼宽带为例 + +进入光猫后台面板 +你可以试试192.168.1.1 + +如图所示: +![image](https://files.astrbot.app/docs/source/images/ipv6/index.png) +这里超级管理员密码是随机生成的,需要用到一点社会工程学手段搞到这个超级密码 +当然你也可以用漏洞搞到 +如果你可以联系到当时给你家安装宽带的师傅,给他打个电话就可以要到 + +进入后菜单如下 +![image](https://files.astrbot.app/docs/source/images/ipv6/index.png) + +依此点击:安全-防火墙 +![image](https://files.astrbot.app/docs/source/images/ipv6/firewall.png) +将防火墙等级设置为低 +同时将启用IPV6 SESSEION关闭(此选项开启后将无法从外部访问) + +# 启动服务 +```bash +# 新版本默认0.0.0.0改成了::,默认启用了双栈支持,如果使用的旧版,需要手动修改配置文件,将host修改为[::] +astrbot run +# 不出意外,你可以在输出里面看到24开头,一长串的ipv6链接 +# http://[ipv6地址]:6185 +``` diff --git a/docs/zh/others/self-host-t2i.md b/docs/zh/others/self-host-t2i.md new file mode 100644 index 000000000..cb18acf1c --- /dev/null +++ b/docs/zh/others/self-host-t2i.md @@ -0,0 +1,27 @@ +# 自行部署文转图服务 + +AstrBot 使用 [AstrBotDevs/astrbot-t2i-service](https://github.com/AstrBotDevs/astrbot-t2i-service) 项目作为默认的文本转图像服务。默认使用的文转图服务接口是 + +```plain +https://t2i.soulter.top/text2img +https://t2i.rcfortress.site/text2img +``` + +此接口能够保障大部分时间正常响应。但是由于部署在国外的(纽约)服务器,因此响应速度可能会比较慢。 + +> [!TIP] +> 欢迎通过 [爱发电](https://afdian.com/a/astrbot_team) 支持我们,以帮助我们支付服务器费用。 + +您可以选择自行部署文转图服务,以提升响应速度。 + +```bash +docker run -itd -p 8999:8999 soulter/astrbot-t2i-service:latest +``` + +在部署完成后,前往 AstrBot 仪表盘 -> 配置文件 -> 系统,修改 `文本转图像服务 API 地址` 为你部署好的 url(如下图所示) + +>如果你是使用本文档的 Docker教程 部署的 AstrBot ,url应为 `http://文转图服务容器名:8999`。 + +>如果部署在与 AstrBot 相同的机器上,url 应为 `http://localhost:8999`。 + +image diff --git a/docs/zh/platform/aiocqhttp/lagrange.md b/docs/zh/platform/aiocqhttp/lagrange.md new file mode 100644 index 000000000..bf2ddc286 --- /dev/null +++ b/docs/zh/platform/aiocqhttp/lagrange.md @@ -0,0 +1,61 @@ +# 接入 Lagrange + +> [!TIP] +> +> - 请合理控制使用频率。过于频繁地发送消息可能会被判定为异常行为,增加触发风控机制的风险。 +> - 本项目严禁用于任何违反法律法规的用途。若您意图将 AstrBot 应用于非法产业或活动,我们**明确反对并拒绝**您使用本项目。 +> - 最新的部署方式请以 [Lagrange Doc](https://lagrangedev.github.io/Lagrange.Doc/Lagrange.OneBot/Config/#%E4%B8%8B%E8%BD%BD%E5%AE%89%E8%A3%85) 为准。 + +## 下载 + +从 [GitHub Release](https://github.com/LagrangeDev/Lagrange.Core/releases) 下载最新版的 `Lagrange.OneBot`。 + +对于 Windows 设备,请下载 `Lagrange.OneBot_win-x64_xxxx` 压缩包。 + +对于 X86 的 Linux 用户,下载 `Lagrange.OneBot_linux-x64_xxx` 压缩包。 + +对于 Arm 的 Linux 用户,下载 `Lagrange.OneBot_linux-arm64_xxx` 压缩包。 + +对于 M 芯片 Mac 用户,下载 `Lagrange.OneBot_osx-arm64_xxx` 压缩包。 + +对于 Intel 芯片 Mac 用户,下载 `Lagrange.OneBot_osx-x64_xxx` 压缩包。 + +## 部署 + +请参阅 [Lagrange Doc](https://lagrangedev.github.io/Lagrange.Doc/Lagrange.OneBot/Config/#%E8%BF%90%E8%A1%8C)。 + +运行完成后,请修改 [配置文件](https://lagrangedev.github.io/Lagrange.Doc/Lagrange.OneBot/Config/#%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6), + +在 `Implementations` 字段下添加: + +```json +{ + "Type": "ReverseWebSocket", + "Host": "127.0.0.1", + "Port": 6199, + "Suffix": "/ws", + "ReconnectInterval": 5000, + "HeartBeatInterval": 5000, + "AccessToken": "" +} +``` + +一定要保证 `Suffix` 为 `/ws`。 + +## 连接到 AstrBot + +### 配置 aiocqhttp + +1. 进入 AstrBot 的管理面板 +2. 点击左边栏 `机器人` +3. 然后在右边的界面中,点击 `+ 创建机器人` +4. 选择 `aiocqhttp(OneBotv11)` + +弹出的配置项填写: + +配置项填写: + +- ID(id):随意填写,用于区分不同的消息平台实例。 +- 启用(enable): 勾选。 +- 反向 WebSocket 主机地址:请填写你的机器的 IP 地址。一般情况下请直接填写 `0.0.0.0` +- 反向 WebSocket 端口:填写一个端口,例如 `6199`。 diff --git a/docs/zh/platform/aiocqhttp/napcat.md b/docs/zh/platform/aiocqhttp/napcat.md new file mode 100644 index 000000000..240579a71 --- /dev/null +++ b/docs/zh/platform/aiocqhttp/napcat.md @@ -0,0 +1,134 @@ +# 使用 NapCat + +> [!TIP] +> +> - 本项目严禁用于任何违反法律法规的用途。若您意图将 AstrBot 应用于非法产业或活动,我们**明确反对并拒绝**您使用本项目。 +> - AstrBot 通过 `aiocqhttp` 适配器接入 OneBot v11 协议。OneBot v11 协议是一个开放的通信协议,并不代表任何具体的软件或服务。 + +NapCat 的 GitHub 仓库:[NapCat](https://github.com/NapNeko/NapCatQQ) +NapCat 的文档:[NapCat 文档](https://napcat.napneko.icu/) + +NapCat 提供了大量的部署方式,包括 Docker、Windows 一键安装包等等。 + +## 通过一键脚本部署 + +推荐采用这种方式部署。 + +### Windows + +看这篇文章:[NapCat.Shell - Win手动启动教程](https://napneko.github.io/guide/boot/Shell#napcat-shell-win%E6%89%8B%E5%8A%A8%E5%90%AF%E5%8A%A8%E6%95%99%E7%A8%8B) + +### Linux + +看这篇文章:[NapCat.Installer - Linux一键使用脚本(支持Ubuntu 20+/Debian 10+/Centos9)](https://napneko.github.io/guide/boot/Shell#napcat-installer-linux%E4%B8%80%E9%94%AE%E4%BD%BF%E7%94%A8%E8%84%9A%E6%9C%AC-%E6%94%AF%E6%8C%81ubuntu-20-debian-10-centos9) + +> [!TIP] +> **Napcat WebUI 在哪打开**: +> 在 napcat 的日志里会显示 WebUI 链接。 +> +> 如果是 linux 命令行一键部署的napcat:`docker log <账号>`。 +> +> Docker部署的 NapCat:`docker logs napcat`。 + +## 通过 Docker Compose 部署 + +1. 下载或复制 [astrbot.yml](https://github.com/NapNeko/NapCat-Docker/blob/main/compose/astrbot.yml) 内容 +2. 将刚刚下载的文件重命名为 `astrbot.yml` +3. 编辑 `astrbot.yml`,将 `# - "6199:6199"` 修改为 `- "6199:6199"`,移除开头的 `#` +4. 在 `astrbot.yml` 文件所在目录执行: + +```bash +NAPCAT_UID=$(id -u) NAPCAT_GID=$(id -g) docker compose -f ./astrbot.yml up -d +``` + +## 通过 Docker 部署 + +此教程默认您安装了 Docker。 + +在终端执行以下命令即可一键部署。 + +```bash +docker run -d \ +-e NAPCAT_GID=$(id -g) \ +-e NAPCAT_UID=$(id -u) \ +-p 3000:3000 \ +-p 3001:3001 \ +-p 6099:6099 \ +--name napcat \ +--restart=always \ +mlikiowa/napcat-docker:latest +``` + +执行成功后,需要查看日志以得到登录二维码和管理面板的 URL。 + +```bash +docker logs napcat +``` + +请复制管理面板的 URL,然后在浏览器中打开备用。 + +然后使用你要登录的账号扫描出现的二维码,即可登录。 + +如果登录阶段没有出现问题,即成功部署。 + +## 连接到 AstrBot + +## 在 AstrBot 配置 aiocqhttp + +1. 进入 AstrBot 的管理面板 +2. 点击左边栏 `机器人` +3. 然后在右边的界面中,点击 `+ 创建机器人` +4. 选择 `OneBot v11` + +弹出的配置项填写: +- ID(id):随意填写,仅用于区分不同的消息平台实例。 +- 启用(enable): 勾选。 +- 反向 WebSocket 主机地址:请填写你的机器的 IP 地址,一般情况下请直接填写 `0.0.0.0` +- 反向 WebSocket 端口:填写一个端口,默认为 `6199`。 +- 反向 Websocket Token:只有当 NapCat 网络配置中配置了 token 才需填写。 + +图例:(最快只需要点击启用,然后保存即可) + +xinjianya + + +点击 `保存`。 + +### 配置管理员 + +填写完毕后,进入 `配置文件` 页,点击 `平台配置` 选项卡,找到 `管理员 ID`,填写你的账号号(不是机器人的账号)。 + +切记点击右下角 `保存`,AstrBot 重启并会应用配置。 + +### 在 NapCat 中添加 WebSocket 客户端 + +切换回 NapCat 的管理面板,点击 `网络配置->新建->WebSockets客户端`。 + +jiaochenXJY + + +在新弹出的窗口中: + +- 勾选 `启用`。 +- `URL` 填写 `ws://宿主机IP:端口/ws`。如 `ws://localhost:6199/ws` 或 `ws://127.0.0.1:6199/ws`。 + +> [!IMPORTANT] +> 1. 如果采用 Docker 部署并同时把 AstrBot 和 NapCat 两个容器接入了同一网络,`ws://astrbot:6199/ws`(参考本文档的 Docker 脚本)。 +> 2. 由于 Docker 网络隔离的原因,不在同一个网络时请使用内网 IP 地址或公网 IP 地址 ***(不安全)*** 进行连接,即 `ws://(内网/公网):6199/ws`。 + +- 消息格式:`Array` +- 心跳间隔: `5000` +- 重连间隔: `5000` + +> [!WARNING] +> +> 1. 切记后面加一个 `/ws`! +> 2. 这里的 IP 不能填为 `0.0.0.0` + +点击 `保存`。 + +前往 AstrBot WebUI `控制台`,如果出现 ` aiocqhttp(OneBot v11) 适配器已连接。` 蓝色的日志,说明连接成功。如果没有,若干秒后出现` aiocqhttp 适配器已被关闭` 则为连接超时(失败),请检查配置是否正确。 + +## 🎉 大功告成 + +此时,你的 AstrBot 和 NapCat 应该已经连接成功!使用 `私聊` 的方式对机器人发送 `/help` 以检查是否连接成功。 diff --git a/docs/zh/platform/aiocqhttp/others.md b/docs/zh/platform/aiocqhttp/others.md new file mode 100644 index 000000000..fcce08327 --- /dev/null +++ b/docs/zh/platform/aiocqhttp/others.md @@ -0,0 +1 @@ +支持接入所有适配了 OneBotv11 反向 Websockets(AstrBot 做服务器端) 的机器人协议端。 \ No newline at end of file diff --git a/docs/zh/platform/dingtalk.md b/docs/zh/platform/dingtalk.md new file mode 100644 index 000000000..dcc141065 --- /dev/null +++ b/docs/zh/platform/dingtalk.md @@ -0,0 +1,65 @@ +# 接入钉钉 DingTalk + +## 支持的基本消息类型 + +> 版本 v4.15.0。 + +| 消息类型 | 是否支持接收 | 是否支持发送 | 备注 | +| --- | --- | --- | --- | +| 文本 | 是 | 是 | | +| 图片 | 是 | 是 | | +| 语音 | 否 | 是 | | +| 视频 | 否 | 是 | | +| 文件 | 否 | 是 | | + +主动消息推送:支持。 + +## 创建和配置应用 + +前往 [钉钉开放平台](https://open-dev.dingtalk.com/fe/app),点击创建应用: + +![image](https://files.astrbot.app/docs/source/images/dingtalk/image-4.png) + +创建好之后,添加应用能力,选择机器人: + +![image](https://files.astrbot.app/docs/source/images/dingtalk/image-5.png) + +点击机器人配置,填写填写机器人相关信息: + +![image](https://files.astrbot.app/docs/source/images/dingtalk/image-7.png) + +确认无误后,点击下面的发布按钮。 + +点击凭证与基础信息,将 `ClientID` 和 `ClientSecret` 复制下来。 + +## 开始连接 + +打开 AstrBot 管理面板 -> `机器人` -> `+ 创建机器人`,创建一个钉钉适配器。 + +将刚刚复制的 `ClientID` 和 `ClientSecret` 填入,点击保存,AstrBot 将会自动向钉钉开放平台请求。 + +回到钉钉开放平台,点击事件订阅,选择 `Stream 模式推送`,点击保存,如果没有意外情况,将会看到 连接接入成功 字样。 + +![image](https://files.astrbot.app/docs/source/images/dingtalk/image-8.png) + +点击保存即可。 + +## 发布版本 + +点击边栏的 版本管理与发布,创建一个新版本。 + +填写应用版本号、版本描述、应用可见范围(选择全部员工或者按照您的需求),点击保存,确认发布。 + +![alt text](https://files.astrbot.app/docs/source/images/dingtalk/image-11.png) + +找到一个钉钉群聊,点击右上角的设置: + +![image](https://files.astrbot.app/docs/source/images/dingtalk/image-12.png) + +下拉找到添加机器人,然后找到刚刚创建的机器人,点击添加即可: + +![image](https://files.astrbot.app/docs/source/images/dingtalk/image-9.png) + +## 🎉 大功告成 + +在群聊中 @ 机器人后附带 `/help` 指令,如果机器人回复了,那么说明接入成功。 diff --git a/docs/zh/platform/discord.md b/docs/zh/platform/discord.md new file mode 100644 index 000000000..aa18e9231 --- /dev/null +++ b/docs/zh/platform/discord.md @@ -0,0 +1,72 @@ +# 接入 Discord + +## 创建 AstrBot Discord 平台适配器 + +进入消息平台,点击新增适配器,找到 Discord 并点击进入 Discord 配置页。 + +![点击创建机器人,选择discord类型](https://files.astrbot.app/docs/source/images/discord/image.png) + +![选项从上到下依次是 1.机器人名称 2. 启用 3. Bot token 4. Discord 代理地址 5. 是否自动将插件指令注册为 Discord 斜杠指令 6. discord_guild_id_for_debug 7.Discord 活动名称](https://files.astrbot.app/docs/source/images/discord/image-3.png) +> 本次教程只用管1,2,3,5项 + +- 机器人名称:自定义,方便区分不同适配器 +- 启用:勾选后启用该适配器 +- Bot Token:在 Discord 创建 App 后获取的 Token(见下文) +- Discord 代理地址:如果你需要使用代理访问 Discord,可以在这里填写代理地址(可选) +- 是否自动将插件指令注册为 Discord 斜杠指令:勾选后,AstrBot 会自动将已安装插件中的指令注册为 Discord 斜杠指令,方便用户使用。 + +## 在 Discord 创建 App + +1. 前往 [Discord](https://discord.com/developers/applications),点击右上角蓝色按钮,输入应用名字,创建应用。 + +![创建bot(输入名字)](https://files.astrbot.app/docs/source/images/discord/image-1.png) + +2. 点击左边栏的 Bot,点击 Reset Token 按钮,创建好 Token 后,点击 Copy 按钮,将 Token 填入配置中的 Discord Bot Token 处。 + +![token选项](https://files.astrbot.app/docs/source/images/discord/image-4.png) +4. 下滑找到这三个选项全开启 + +![Presence Intent,Server Members Intent,Message Content Intent截图](https://files.astrbot.app/docs/source/images/discord/image-2.png) + +- Presence Intent:允许机器人获取用户在线状态 +- Server Members Intent:允许机器人获取服务器成员信息 +- Message Content Intent:允许机器人读取消息内容 + +5. 点击左边栏的 OAuth2,在 OAuth2 URL Generator 中选中 `Bot` +也就是这样 +![OAuth2 URL Generator](https://files.astrbot.app/docs/source/images/discord/image-6.png) +然后在下方出现的 Bot Permissions 处选择允许的权限。一般来说,建议添加如下权限: + - Send Messages + - Create Public Threads + - Create Private Threads + - Send TTS Messages + - Manage Messages + - Manage Threads + - Embed Links + - Attach Files + - Read Message History + - Add Reactions +如果你觉得麻烦也可以直接使用administrator权限,但仍然建议在使用环境中使用上文的配置权限(或您自己需要的权限) +> 记住,权限越高,风险越大。 + +6. 复制下方出现的 Generated URL。打开这个 URL,将 Bot 添加到所需要的服务器。 +![Generated URL位置](https://files.astrbot.app/docs/source/images/discord/image-5.png) + +7. 进入 Discord 服务器,你的机器人应该已经提示在线了 +![机器人在线](https://files.astrbot.app/docs/source/images/discord/image-7.png) +@ 刚刚创建的机器人(也可以不 @),输入 `/help`,如果成功返回,则测试成功。 + +## 预回应表情 + +Discord 支持预回应表情功能。启用后,机器人在处理消息时会先添加一个表情反应,让用户知道机器人正在处理消息。 + +在管理面板的「配置」页面中,找到 `平台特定配置 -> Discord -> 预回应表情`: + +- **启用预回应表情**:开启后,机器人收到消息时会自动添加表情反应 +- **表情列表**:填写 Unicode 表情符号,例如:👍、🤔、⏳。可填写多个,机器人会随机选择一个使用 + +# 故障排除 + +- 如果卡在最后的步骤,机器人不在线请确定自己的服务器可以直接连接discord + +如果有疑问,请[提交 Issue](https://github.com/AstrBotDevs/AstrBot/issues)。 diff --git a/docs/zh/platform/kook.md b/docs/zh/platform/kook.md new file mode 100644 index 000000000..b585ab870 --- /dev/null +++ b/docs/zh/platform/kook.md @@ -0,0 +1,48 @@ +# 接入 Kook + +## 支持的基本消息类型 + +> 版本 v4.19.2 + +| 消息类型 | 是否支持接收 | 是否支持发送 | 备注 | +| ------------ | ------------ | ------------ | ---------------------------------------------- | +| 文本 | 是 | 是 | 支持官方[kmarkdown]语法 | +| 图片 | 是 | 是 | 支持外链,图片类型仅支持`jpeg`, `gif`, `png` | +| 语音 | 是 | 是 | 支持外链 | +| 视频 | 是 | 是 | 支持外链,视频仅支持`mp4`,`mov` | +| 文件 | 是 | 是 | 支持外链 | +| 卡片(JSON) | 是 | 是 | 参见[Kook文档-卡片消息] | + +主动消息推送:支持 + +消息接收模式:WebSocket + +## 在 Kook 创建机器人 + +1. 点击跳转 [Kook 开发者平台] ,完成以下步骤: +2. 登录账号并完成实名认证; +3. 点击「新建应用」,自定义 Bot 昵称; +4. 进入应用后台,选择「机器人」模块,开启 **WebSocket 连接模式**,注意保存生成的 **Token**,后续配置Astrbot需要使用; +5. 在左边栏「机器人」页面下点击「邀请链接」,设置角色权限(建议赋予全权限,确保功能完整)。 +6. 设置好角色权限后,点击上方邀请链接的复制按钮复制链接,在浏览器中打开复制出来的邀请链接,将机器人加入到所需的服务器。 + + ![image](https://files.astrbot.app/docs/source/images/kook/image-1.png) + +## 在 AstrBot 配置 + +1. 进入 AstrBot 的管理面板 +2. 点击左边栏 `机器人` +3. 然后在右边的界面中,点击 `+ 创建机器人` +4. 选择 `kook` 适配器 +5. 弹出的配置项填写: + + - ID(id):随意填写,用于区分不同的消息平台实例。 + - 启用(enable): 勾选。 + - 机器人 Token: 填写在 [Kook 开发者平台] 中创建机器人时生成的 Token。 + +6. 完成适配器配置填写后,点击 `保存`。 +7. 最后,在kook服务器频道(若没有属于自己的服务器频道,请先创建一个服务器频道)中,@ 刚刚创建的机器人,输入 `/sid`,如果机器人成功回复,则测试成功。 + +[Kook 开发者平台]: https://developer.kookapp.cn/app +[kmarkdown]: https://developer.kookapp.cn/doc/kmarkdown +[Kook文档-卡片消息]: https://developer.kookapp.cn/doc/cardmessage diff --git a/docs/zh/platform/lark.md b/docs/zh/platform/lark.md new file mode 100644 index 000000000..76385e74e --- /dev/null +++ b/docs/zh/platform/lark.md @@ -0,0 +1,115 @@ +# 接入飞书 + +## 支持的基本消息类型 + +> 版本 v4.15.0。 + +| 消息类型 | 是否支持接收 | 是否支持发送 | 备注 | +| --- | --- | --- | --- | +| 文本 | 是 | 是 | | +| 图片 | 是 | 是 | | +| 语音 | 否 | 是 | | +| 视频 | 否 | 是 | | +| 文件 | 否 | 是 | | + +主动消息推送:支持。 + +## 创建机器人 + +前往 [开发者后台](https://open.feishu.cn/app) ,创建企业自建应用。 + +![创建企业自建应用](https://files.astrbot.app/docs/source/images/lark/image.png) + +添加应用能力——机器人。 + +![添加应用能力](https://files.astrbot.app/docs/source/images/lark/image-1.png) + +点击凭证与基础信息,获取 app_id 和 app_secret。 + +![获取 app_id 和 app_secret](https://files.astrbot.app/docs/source/images/lark/image-4.png) + +## 配置 AstrBot + +1. 进入 AstrBot 的管理面板 +2. 点击左边栏 `机器人` +3. 然后在右边的界面中,点击 `+ 创建机器人` +4. 选择 `lark(飞书)` + +弹出的配置项填写: + +- ID(id):随意填写,用于区分不同的消息平台实例。 +- 启用(enable): 勾选。 +- app_id: 获取的 app_id +- app_secret: 获取的 app_secret +- 飞书机器人的名字 + +对于 domain,如果您使用国内版飞书,保持默认即可;如果您正在用国际版飞书,请设置为 `https://open.larksuite.com`;如果您使用企业自部署飞书,请填写您的飞书实例的域名。 + +对于订阅方式,`socket` 代表使用「长连接」订阅方式,`webhook` 代表「将事件发送至开发者服务器」的订阅方式,后者需要您拥有公网服务器。一般来说使用 `socket` 即可,如果您使用国际版飞书或者企业自部署飞书,请选择 `webhook`。相应地,接下来的配置也会有所不同。 + +如果您选择了 `webhook` 方式,选择了之后,前往飞书的开发者后台,点击事件与回调,点击加密策略,填写 Encrypt Key。这不是必须的,AstrBot 十分注重你的数据安全,所以请务必填写。填写后复制 `Encrypt Key` 和 `Verification Token` 到 AstrBot 配置的 `encrypt_key` 和 `verification_token` 处。 + +点击 `保存`。 + +## 设置回调和权限 + +对于上面选择的订阅方式,接下来的步骤有所不同,请你根据实际选择的方式,跳转到对应的章节。 + +### `socket` 长连接方式 + +接下来,点击事件与回调,使用长连接接收事件,点击保存。**如果上一步没有成功启动,那么这里将无法保存。** + +![设置事件与回调](https://files.astrbot.app/docs/source/images/lark/image-6.png) + +### `webhook` 将事件发送至开发者服务器方式 + +> [!TIP] +> 为了更好地使用这种方式,请先参考 [统一 Webhook 模式](/zh/use/unified-webhook.md#如何使用统一-webhook-模式) 做好相关配置。 + +在点击 `保存` 后,机器人卡片会显示「查看 Webhook 链接」,点击查看,复制回调 URL。 + +![](https://files.astrbot.app/docs/source/images/lark/webhook.png) + +接下来,回到飞书的事件与回调页,点击「事件配置」,选择「将事件发送至开发者服务器」,将“请求地址”填写为刚刚复制的回调 URL,点击保存。如果一切无误将不会报错。 + +### 设置事件 + +上一步事件配置完成后,点击添加事件,消息与群组,下拉找到 `接收消息`,添加。 + +![添加事件](https://files.astrbot.app/docs/source/images/lark/image-7.png) + +点击开通以下权限。 + +![开通权限](https://files.astrbot.app/docs/source/images/lark/image-8.png) + +再点击上面的`保存`按钮。 + +接下来,点击权限管理,点击开通权限,输入 `im:message:send,im:message,im:message:send_as_bot`。添加筛选到的权限。 + +再次输入 `im:resource:upload,im:resource` 开通上传图片相关的权限。 + +最终开通的权限如下图: + +![最终开通的权限](https://files.astrbot.app/docs/source/images/lark/image-11.png) + +## 创建版本 + +创建版本。 + +![创建版本](https://files.astrbot.app/docs/source/images/lark/image-2.png) + +填写版本号,更新说明,可见范围后点击保存,确认发布。 + +## 拉入机器人到群组 + +进入飞书 APP(网页版飞书无法添加机器人),点进群聊,点击右上角按钮->群机器人->添加机器人。 + +搜索刚刚创建的机器人的名字。比如教程创建了 `AstrBot` 机器人: + +![添加机器人](https://files.astrbot.app/docs/source/images/lark/image-9.png) + +## 🎉 大功告成 + +在群内发送一个 `/help` 指令,机器人将做出响应。 + +![成功](https://files.astrbot.app/docs/source/images/lark/image-13.png) \ No newline at end of file diff --git a/docs/zh/platform/line.md b/docs/zh/platform/line.md new file mode 100644 index 000000000..32b7dedcb --- /dev/null +++ b/docs/zh/platform/line.md @@ -0,0 +1,78 @@ +# 接入 LINE + +## 支持的基本消息类型 + +> 版本 v4.17.0。 + +| 消息类型 | 是否支持接收 | 是否支持发送 | 备注 | +| --- | --- | --- | --- | +| 文本 | 是 | 是 | | +| 图片 | 是 | 是 | | +| 语音 | 是 | 是 | | +| 视频 | 是 | 是 | | +| 文件 | 是 | 是 | | +| 贴纸 | 是 | 否 | | + +主动消息推送:支持。 + +## 创建 LINE Messaging API Channel + +1. 打开 [LINE Developers Console](https://developers.line.biz/console/) +2. 创建或选择一个 Provider +3. 创建一个 `Messaging API` Channel (不是 `LINE Login` Channel) +4. 在 `Messaging API` 页面中,完成机器人初始化 + +## 获取凭据 + +你需要以下配置项: + +- `channel_secret` +- `channel_access_token` + +获取方式: + +1. 进入对应 Channel 的设置页面 +2. 在 `Basic settings` 获取 `Channel secret` +3. 在 `Messaging API` 页面签发 `Channel access token` + +![](https://files.astrbot.app/docs/source/images/line/7ecee0a9102f191245330f8408eb0493.png) + +## 配置 AstrBot + +1. 进入 AstrBot 管理面板 +2. 点击左侧 `机器人` +3. 点击 `+ 创建机器人` +4. 选择 `line` + +填写配置: + +- `ID(id)`:自定义,区分多个平台实例 +- `启用(enable)`:勾选 +- `LINE Channel Access Token`:填入 `channel_access_token` +- `LINE Channel Secret`:填入 `channel_secret` + +点击保存。 + +## 配置回调地址(统一 Webhook) + +LINE 适配器仅支持 AstrBot 统一 Webhook 模式。 + +保存后,在机器人卡片里点击「查看 Webhook 链接」,复制 URL。 + +然后到 LINE Developers Console: + +1. 打开 `Messaging API` 页面 +2. 在 `Webhook settings` 中粘贴 `Webhook URL` +3. 点击 `Verify` +4. 打开 `Use webhook` + +> [!TIP] +> 如果你的 AstrBot 不在公网,请先配置好可公网访问的域名与反向代理,确保 LINE 可以访问该 Webhook URL。 + +## 测试 + +1. 用 LINE 添加该官方账号为好友(通过二维码即可添加) +2. 给机器人发送一条消息(例如 `hi`) +3. 若能收到回复,即接入成功 + +如果要在群内使用,请先将该官方账号拉入群组后再测试。 diff --git a/docs/zh/platform/matrix.md b/docs/zh/platform/matrix.md new file mode 100644 index 000000000..e4445e739 --- /dev/null +++ b/docs/zh/platform/matrix.md @@ -0,0 +1,20 @@ +# 接入 Matrix + +> [!TIP] +> 该平台适配器由社区([stevessr](https://github.com/stevessr)) 维护。如果您觉得有帮助,请支持开发者,给该仓库点一个 Star。❤️ + +## 安装 astrbot_plugin_matrix_adapter 插件 + +进入 AstrBot WebUI 的插件市场,搜索 `astrbot_plugin_matrix_adapter`,点击安装。 + +安装完成后,前往 消息平台 → 新增适配器 → 选择 Matrix(若选项缺失,尝试重启 AstrBot 或检查插件安装状态)。 + +在弹出的配置对话框中点击 `启用`。 + +## 配置 + +请参考该仓库的 [README.md](https://github.com/stevessr/astrbot_plugin_matrix_adapter?tab=readme-ov-file#astrbot-matrix-adapter-%E6%8F%92%E4%BB%B6) 进行配置。 + +## 问题提交 + +如有疑问,请提交 issue 至[插件仓库](https://github.com/stevessr/astrbot_plugin_matrix_adapter/issues)。 diff --git a/docs/zh/platform/misskey.md b/docs/zh/platform/misskey.md new file mode 100644 index 000000000..955c4342b --- /dev/null +++ b/docs/zh/platform/misskey.md @@ -0,0 +1,112 @@ +# 接入 Misskey 平台 + +> [!WARNING] +> 1. 我们建议您在非您参与管理的 Misskey 实例上部署 Bot 前请先查看实例规则或征求实例管理组或检察组的同意,并在部署后为机器人账号开启`Bot`标识。 +> 2. 本项目严禁用于任何违反法律法规的用途。若您意图将 AstrBot 应用于非法产业或活动,我们明确反对并拒绝您使用本项目。 + +## 创建 AstrBot Misskey 平台适配器 + +进入消息平台,点击新增适配器,找到 Misskey 并单击进入 Misskey 配置页。 + +![创建 Misskey 平台适配器](https://files.astrbot.app/docs/source/images/misskey/create.png) + +## 配置平台适配器设置 + +在 AstrBot Misskey 的平台适配器配置页,我们需要填写 Misskey 的接入信息和配置适配器的部分行为。 + +::: tip 注意 +别忘了退出保存前先点击`启用`以启用 Misskey 平台配置器! +::: + +获取 Misskey 接入信息的方式见下文介绍。 + +![Misskey 平台适配器配置](https://files.astrbot.app/docs/source/images/misskey/config.png) + +## Misskey 实例 URL + +就是你的 Bot 所处账号的 Misskey 实例前端地址,格式为标准域名。例如`https://misskey.example`。 + +## 获取 Bot 账号 Access Token + +1. 首先打开 Misskey Web 前端页面,在前端页面侧边栏找到并打开`设置 > 连接服务`页面。 + +![打开 Misskey 连接服务页面](https://files.astrbot.app/docs/source/images/misskey/pat-1.png) + +2. 单击“生成访问令牌”以生成账号接入访问令牌。 + +![生成 Misskey 账号令牌](https://files.astrbot.app/docs/source/images/misskey/pat-2.png) + +3. 在弹出的访问令牌配置页面,我们为令牌起一个名字,比如`AstrBot`。 + +4. 然后我们需要为令牌配置相关权限让 Bot 能够与 Misskey 实例交互。 + +::: tip 注意 +如果你使用的 AstrBot 第三方插件需要额外权限,请参考其文档增加相应权限。若你完全信任 Bot 的部署环境,也可以临时开启全部权限以简化调试,但仍建议您在生产环境使用时限制 Bot 的相关权限。 +::: + +![配置访问令牌权限](https://files.astrbot.app/docs/source/images/misskey/pat-3.png) + +**默认需要开启的权限** + +| 权限名称 | 说明 | 用途 | +|---|---:|---| +| 读取账户信息 | 查看账户的基本信息 | 获取 Bot 自身的用户信息和账号 ID | +| 撰写或删除帖子 | 创建、编辑和删除笔记内容 | 发送消息回复和发布内容 | +| 撰写或删除消息 | 创建、编辑和删除私信内容 | 处理私信对话 | +| 查看通知 | 接收系统通知和提醒 | 获取提及、回复等通知信息 | +| 查看消息 | 读取私信和聊天记录 | 接收和处理用户私信 | +| 查看回应 | 查看帖子的回复和反应 | 处理用户对 Bot 消息的回应 | + +5. 权限配置完成后,单击“完成”以查看账号访问令牌。把获取到的令牌复制并粘贴到 AstrBot 配置页面 Access Token 输入框内。 + +![查看账号令牌](https://files.astrbot.app/docs/source/images/misskey/pat-4.png) + +## 默认帖文可见性 + +修改机器人发帖时的默认可见性 + +| 名称 | 说明 | +|---|---| +| public | 任何人都可以看到 Bot 的帖文 | +| home | 公开 Bot 帖文于实例主页时间线 | +| followers | 只有关注了 Bot 账号的用户才能在主页时间线看到 Bot 帖文 | + +## 仅限本站(不参与联合) + +开启后,Bot 发送的所有帖文都不会参与 Fediverse 联合,非常适用于仅想在自己实例使用和传播 Bot 的帖子的需求。 + +## 启用聊天信息响应 + +::: tip 注意 +Misskey 的“聊天”组件特性并不受所有 Misskey Fork 版本支持!无法跨实例互联。 + +Misskey 在`v2025.4.0`及以后的版本中为加入“聊天”组件支持,且仅受其 Web 前端支持,并未受到第三方 App 良好的支持。 +::: + +默认开启,开启后 Bot 会响应 Misskey 聊天内用户发送的私聊内容并进行回复。 + +## 历史记录 + +聊天和贴文单个用户的对话历史在 AstrBot 的 WebUI 控制台“对话历史”会以`chat:UserID`的 id 记录,传统贴文则是以`note:UserID`的 id 记录。 + +::: tip Misskey 用户的 UserID 在哪里? +位于用户个人页面部分的`Raw`页面内可以查询,UserID 是单个实例中 Misskey 用户唯一的关键身份标识。 +::: + +![UserID](https://files.astrbot.app/docs/source/images/misskey/userid.png) + +## 测试成功性 + +配置完成并启用后,前往 Misskey 新建帖文并在发送中引用 Bot (@mention)测试效果。如果 Bot 账号能够成功触发回复,说明配置成功。 + +![效果示例](https://files.astrbot.app/docs/source/images/misskey/demo.png) + +## 杂谈 + +我们建议您为 Bot 账号开启 Misskey `Bot` 标识以尊重 Misskey 各实例的相关规定和速率限制等,也能有效帮助 Misskey 实例管理员管理和识别 Bot 的使用情况。 + +**开启方式** + +在 Bot 账号个人资料页面的高级设置中开启“这是一个机器人账号”即可。 + +![这是一个机器人账号](https://files.astrbot.app/docs/source/images/misskey/botset.png) diff --git a/docs/zh/platform/qqofficial.md b/docs/zh/platform/qqofficial.md new file mode 100644 index 000000000..c1ddbfad7 --- /dev/null +++ b/docs/zh/platform/qqofficial.md @@ -0,0 +1,8 @@ +# 接入 QQ 官方机器人平台 + +QQ 官方机器人平台是腾讯官方提供的一个机器人接入平台,允许开发者通过官方接口将机器人接入 QQ 群聊和个人聊天中。 + +目前主要通过 Webhook 方式接入。 + +- [Webhook 方式](/platform/qqofficial/webhook) +- [Websockets 方式](/platform/qqofficial/websockets) diff --git a/docs/zh/platform/qqofficial/webhook.md b/docs/zh/platform/qqofficial/webhook.md new file mode 100644 index 000000000..c7b241e0d --- /dev/null +++ b/docs/zh/platform/qqofficial/webhook.md @@ -0,0 +1,108 @@ + +# 通过 QQ官方机器人 接入 QQ (Webhook) + +> [!WARNING] +> +> 1. 截至目前,QQ 官方机器人需要设置 IP 白名单。 +> 2. 支持群聊、私聊、频道聊天、频道私聊。 +> +> **需要**一台带有公网 IP 的服务器和域名(如果没备案,需要服务器在海外或者中国港澳台地区) + +## 支持的基本消息类型 + +> 版本 v4.15.0。 + +| 消息类型 | 是否支持接收 | 是否支持发送 | 备注 | +| --- | --- | --- | --- | +| 文本 | 是 | 是 | | +| 图片 | 是 | 是 | | +| 语音 | 否 | 否 | | +| 视频 | 否 | 否 | | +| 文件 | 否 | 否 | | + +主动消息推送:不支持。 + +## 申请一个机器人 + +首先,打开 [QQ官方机器人](https://q.qq.com) 并登录。 + +然后,点击创建机器人,填写名称、简介、头像等信息。然后点击下一步、提交审核。等待安全校验通过后,创建成功。 + +点击创建好的机器人,然后你将会被导航到机器人的管理页面。如下图所示: + +![image](https://files.astrbot.app/docs/source/images/qqofficial/image.png) + +## 允许机器人加入频道/群/私聊 + +点击`沙箱配置`,这允许你立即设置一个沙箱频道/QQ群/QQ私聊,用于拉入机器人(需要小于等于20个人)。 + +然后你将会看到 QQ 群配置、消息列表配置和 QQ 频道配置。根据你的需求来选择QQ群、允许私聊的QQ号、QQ频道。 + +![image](https://files.astrbot.app/docs/source/images/qqofficial/image-1.png) + +## 获取 appid、secret + +添加机器人到你想用的地方后。 + +点击 `开发->开发设置`,找到 appid、secret。复制并保存它们。 + +## 添加 IP 白名单 + +点击 `开发->开发设置`,找到 IP 白名单。添加你的服务器 IP 地址。 + +![image](https://files.astrbot.app/docs/source/images/qqofficial/image-3.png) + +## 在 AstrBot 配置 + +1. 进入 AstrBot 的管理面板 +2. 点击左边栏 `机器人` +3. 然后在右边的界面中,点击 `+ 创建机器人` +4. 选择 `qq_official_webhook` + +弹出的配置项填写: + +- ID(id):随意填写,用于区分不同的消息平台实例。 +- 启用(enable): 勾选。 +- appid: QQ 官方机器人中获取的 appid。 +- secret: QQ 官方机器人中获取的 secret。 +- 统一 Webhook 模式 (unified_webhook_mode): 保持开启。 + +点击 `保存`。 + +## 反向代理 + +保存之后,请根据你的服务器环境,配置域名 DNS 解析和反向代理,将请求转发到 AstrBot 所在服务器的 `6185` 端口 (如果没有开启统一 Webhook 模式,将请求转发到上一步配置指定的端口)。 + +## 设置回调地址 + +在 `开发->回调配置` 处,配置回调地址。 + +上一步点击保存之后,AstrBot 将会自动为你生成唯一的 Webhook 回调链接,你可以在日志中或者 WebUI 的机器人页的卡片上找到。 + +![unified_webhook](https://files.astrbot.app/docs/source/images/use/unified-webhook.png) + +将请求地址填写为该地址。 + +> [!TIP] +> v4.8.0 之前没有 `统一 Webhook 模式`,则请求地址填写 `<你的域名>/astrbot-qo-webhook/callback`。 + +填写好之后,添加事件,四个事件类型都全选:单聊事件、群事件、频道事件等,如下图。 + +![image](https://files.astrbot.app/docs/source/images/webhook/image.png) + +输入完成后,将光标挪出输入框,将会发送一次验证请求。如果没问题,右边的确定配置按钮将可点击,点击即可。 + +接着重启 AstrBot。 + +## 🎉 大功告成 + +此时,你的 AstrBot 应该已经连接成功。如果发送消息没有反应,请等待一两分钟后重启 AstrBot 再进行确认(测试时发现回调地址不会立即生效)。 + +## 附录:如何配置反向代理 + +如果你还没有相关经验,这里推荐使用 Caddy 作为反向代理的工具,请参考: + +1. 安装 Caddy: +2. 设置反向代理: + +Caddy 将自动为您申请 TLS 证书,以达到接入 Webhook 的目的。 diff --git a/docs/zh/platform/qqofficial/websockets.md b/docs/zh/platform/qqofficial/websockets.md new file mode 100644 index 000000000..1deb6a363 --- /dev/null +++ b/docs/zh/platform/qqofficial/websockets.md @@ -0,0 +1,90 @@ + +# 通过 QQ官方机器人 接入 QQ (Websockets) + +## 支持的基本消息类型 + +> 版本 v4.15.0。 + +| 消息类型 | 是否支持接收 | 是否支持发送 | 备注 | +| --- | --- | --- | --- | +| 文本 | 是 | 是 | | +| 图片 | 是 | 是 | | +| 语音 | 否 | 否 | | +| 视频 | 否 | 否 | | +| 文件 | 否 | 否 | | + +主动消息推送:不支持。 + +## 快速部署通道 + +> 更新自: `2026/03/06`。该方法仅支持 `私聊`。 + +1. 打开 [QQ 开放平台](https://q.qq.com/qqbot/openclaw/)。如果没注册,需要先注册。 +2. 点击右侧 `创建机器人` 按钮。 +3. 获取 `AppID` 和 `AppSecret`。 +4. 进入 AstrBot 的 WebUI,点击左边栏 `机器人`,然后在右边的界面中,点击 `+ 创建机器人`,选择 `QQ 官方机器人(WebSocket)`,将之前得到的的 `AppID` 和 `AppSecret` 复制到这里的表单中,然后 `启用`,然后点击保存。 +5. 回到 QQ 开放平台页面,点击机器人右边的 `扫码聊天`。用手机 QQ 扫码即可聊天。 + +如果要在群聊中使用,参考下面文档的 `允许机器人加入频道/群/私聊` 一节。 + +--- + +## 申请一个机器人 + +> [!WARNING] +> +> 1. 截至目前,QQ 官方机器人需要设置 IP 白名单。 +> 2. 支持群聊、私聊、频道聊天、频道私聊。 + +首先,打开 [QQ官方机器人](https://q.qq.com) 并登录。 + +然后,点击创建机器人,填写名称、简介、头像等信息。然后点击下一步、提交审核。等待安全校验通过后,创建成功。 + +点击创建好的机器人,然后你将会被导航到机器人的管理页面。如下图所示: + +![image](https://files.astrbot.app/docs/source/images/qqofficial/image.png) + +## 允许机器人加入频道/群/私聊 + +点击`沙箱配置`,这允许你立即设置一个沙箱频道/QQ群/QQ私聊,用于拉入机器人(需要小于等于20个人)。 + +然后你将会看到 QQ 群配置、消息列表配置和 QQ 频道配置。根据你的需求来选择QQ群、允许私聊的QQ号、QQ频道。 + +![image](https://files.astrbot.app/docs/source/images/qqofficial/image-1.png) + +## 获取 appid、secret + +添加机器人到你想用的地方后。 + +点击 `开发->开发设置`,找到 appid、secret。复制并保存它们。 + +## 添加 IP 白名单(可选) + +点击 `开发->开发设置`,找到 IP 白名单。添加你的服务器 IP 地址。 + +![image](https://files.astrbot.app/docs/source/images/qqofficial/image-3.png) + +> [!TIP] +> 如果你不知道你的服务器 IP 地址,可以在终端中输入 `curl ifconfig.me` 来获取。或者登录 [ip138.com](https://ip138.com/) 查看。 +> +> 如果你在没有公网 IP 的环境下,你看到的 IP 是运营商 NAT 的 IP,这个 IP 根据你的运营商的情况可能会随时变化。如有必要,可以配置代理。 + +## 在 AstrBot 配置 + +1. 进入 AstrBot 的管理面板 +2. 点击左边栏 `机器人` +3. 然后在右边的界面中,点击 `+ 创建机器人` +4. 选择 `QQ 官方机器人(WebSocket)` + +弹出的配置项填写: + +- ID(id):随意填写,用于区分不同的消息平台实例。 +- 启用(enable): 勾选。 +- appid: QQ 官方机器人中获取的 appid。 +- secret: QQ 官方机器人中获取的 secret。 + +点击 `保存`。 + +## 🎉 大功告成 + +此时,你的 AstrBot 和 NapCatQQ 应该已经连接成功。使用 `私聊` 的方式在 QQ 对机器人发送 `/help` 以检查是否连接成功。 diff --git a/docs/zh/platform/satori/llonebot.md b/docs/zh/platform/satori/llonebot.md new file mode 100644 index 000000000..994c5de97 --- /dev/null +++ b/docs/zh/platform/satori/llonebot.md @@ -0,0 +1,78 @@ +# 接入 LLTwoBot (Satori) + +> [!TIP] +> LLTwoBot 是一个基于 QQNT 的 Onebot v11、Satori 多协议实现端,可以让你在 QQ 平台使用 Satori 协议与 AstrBot 进行通信。 + +> [!TIP] +> +> - 请合理控制使用频率。过于频繁地发送消息可能会被判定为异常行为,增加触发风控机制的风险。 +> - 本项目严禁用于任何违反法律法规的用途。若您意图将 AstrBot 应用于非法产业或活动,我们**明确反对并拒绝**您使用本项目。 + +## 准备工作 + +请先参考 LLTwoBot 官方文档完成基础配置: +[LLTwoBot 文档](https://llonebot.com/guide/getting-started) + +完成文档中的步骤,确保你已经: + +1. 下载并安装了 LLTwoBot +2. 成功登录了 QQ 账号 + +## 配置 LLTwoBot 的 Satori 服务 + +在成功登录 QQ 后,先打开 LLTwoBot 的 WebUI 配置界面。 +> WebUI 默认地址为: + +--- + +在WebUI的配置界面侧边,选择【Satori】选项卡,进行如下配置: + +1. 确认【启用 Satori 协议】配置项已开启 +2. 端口默认为 5600(如需修改请记住新端口) +3. 如有必要,可填写【Satori Token】 +4. 点击右下角的【保存配置】 + +![image](https://files.astrbot.app/docs/source/images/satori/2025-10-10_15-52-32.png) + +## 在 AstrBot 中配置 Satori 适配器 + +1. 进入 AstrBot 的管理面板 +2. 点击左边栏 `机器人` +3. 然后在右边的界面中,点击 `+ 创建机器人` +4. 选择 `satori` + +弹出的配置项填写: + +- 机器人名称 (id): `LLTwoBot` +- 启用 (enable): 勾选 +- Satori API 终结点 (satori_api_base_url):`http://localhost:5600/v1` +- Satori WebSocket 终结点 (satori_endpoint):`ws://localhost:5600/v1/events` +- Satori 令牌 (satori_token):根据 LLTwoBot 配置填写(如有设置) + +> [!NOTE] +> +> - LLTwoBot 的 satori协议 默认在 `5600` 端口提供服务 +> - 因此完整的 URL 路径为 `http://localhost:5600/v1` +> +> 如果你的 satori协议运行在其他端口,请根据实际情况修改对应的配置! + +![image](https://files.astrbot.app/docs/source/images/satori/2025-10-10_16-10-54.png) + +点击右下角 `保存` 完成配置。 + +## 🎉 大功告成 + +此时,你的 AstrBot 应该已经通过 Satori 协议成功连接到 LLTwoBot。 + +在 QQ 中发送 `/help` 以检查是否连接成功。 + +如果成功回复,则配置成功。 + +## 常见问题 + +如果遇到连接问题,请检查: + +1. LLTwoBot 是否正常运行 +2. Satori 服务是否已启用 +3. 端口配置是否正确 +4. Token 是否匹配(如设置了 Token) diff --git a/docs/zh/platform/satori/server-satori.md b/docs/zh/platform/satori/server-satori.md new file mode 100644 index 000000000..b957bd0eb --- /dev/null +++ b/docs/zh/platform/satori/server-satori.md @@ -0,0 +1,68 @@ +# 接入 server-satori (基于 Koishi) + +> [!TIP] +> server-satori 是 Koishi 平台的一个插件,可以将 Koishi 作为 Satori 协议的服务端,让 AstrBot 通过 Satori 协议接入 koishi 响应消息。 + +## 准备工作 + +确保你已经有一个运行中的 Koishi 实例。 + +如果没有,请先参考 [Koishi 官方文档](https://koishi.chat/zh-CN/manual/starter/windows.html) 完成安装和基础配置。 + +> 安装过程中遇到任何问题,欢迎前往 [Koishi 社区](https://koishi.chat/zh-CN/about/contact.html) 社区讨论。 + +## 在 Koishi 中启用 server-satori 插件 + +1. 打开 Koishi 管理界面 +2. 进入`插件配置` 页面 +3. 启用该插件(通常不需要额外配置,使用默认设置即可) + +安装并启用插件后,server-satori 会自动在 Koishi 的 `/satori` 路径下提供 Satori 协议服务。 + +![image](https://files.astrbot.app/docs/source/images/satori/2025-09-07_17-14-55.png) + +## 在 AstrBot 中配置 Satori 适配器 + +1. 进入 AstrBot 的管理面板 +2. 点击左边栏 `机器人` +3. 然后在右边的界面中,点击 `+ 创建机器人` +4. 选择 `satori` + +弹出的配置项填写: + +- 机器人名称 (id): `server-satori` +- 启用 (enable): 勾选 +- Satori API 终结点 (satori_api_base_url):`http://localhost:5140/satori/v1` +- Satori WebSocket 终结点 (satori_endpoint):`ws://localhost:5140/satori/v1/events` +- Satori Token (satori_token):通常留空(除非在 Koishi 中特别配置了 Token) + +> [!NOTE] +> +> - Koishi 默认运行在 5140 端口 +> - server-satori 插件默认在 `/satori` 路径下提供服务 +> - 因此完整的 URL 路径为 `http://localhost:5140/satori/v1` +> +> 如果你的 koishi 运行在其他端口或路由下,**请根据实际情况修改对应的配置!** + +![image](https://files.astrbot.app/docs/source/images/satori/2025-10-10_16-16-25.png) + +点击右下角 `保存` 完成配置。 + +## 🎉 大功告成 + +此时,你的 AstrBot 应该已经通过 Satori 协议成功连接到了 Koishi 的 server-satori 插件。 + +在 Koishi 的沙盒里 向机器人发送 AstrBot的指令(例如:`/help`)进行测试, + +如果成功回复,则配置成功。 + +![image](https://files.astrbot.app/docs/source/images/satori/2025-09-07_17-19-04.png) + +## 常见问题 + +如果遇到连接问题,请检查: + +1. Koishi 是否正常运行 +2. server-satori 插件是否已正确安装并启用 +3. 端口和路径配置是否正确 +4. 防火墙是否阻止了相关端口的访问 diff --git a/docs/zh/platform/slack.md b/docs/zh/platform/slack.md new file mode 100644 index 000000000..de5b31c3f --- /dev/null +++ b/docs/zh/platform/slack.md @@ -0,0 +1,93 @@ +# 接入 Slack + +## 创建 AstrBot Slack 平台适配器 + +进入 `机器人` 页,点击 `+ 创建机器人`,找到 Slack 并点击进入 Slack 配置页。 + +![image](https://files.astrbot.app/docs/source/images/slack/image-1.png) + +在弹出的配置对话框中点击 `启用`。 + +## 在 Slack 创建 App + +Slack 支持两种接入方式:`Webhook` 与 `Socket`。如果您没有公网服务器并且消息业务量的规模较小,我们建议您使用 `socket` 方式。如果您有公网服务器(或者有一定的技术背景,了解如何设置 Tunnel,如 Cloudflare Tunnel),可以选择 `webhook` 方式。`socket` 方式部署相对简单。 + +1. 创建 [Slack](https://slack.com/signin) 账号和一个工作区(Workspace)。 +2. 前往 [应用后台](https://api.slack.com/apps),点击「Create New App」->「From Scratch」,输入 `应用名称` 和要添加到的工作区,然后点击「Create App」。 +3. (仅 Webhook 需要)获取 `Signing Secret`,在左边栏 Basic Information 页下,找到 App Credentials 的 `Signing Secret`,点击 Show 并且复制到平台适配器配置的 signing_secret 处。 + +![image](https://files.astrbot.app/docs/source/images/slack/image.png) + +4. 在左边栏 Basic Information 页下,找到 App-Level Tokens,点击 「Generate Token and Scopes」。Token Name 任意输入,点击 Add Scope,选择 `connections:write`,然后点击 「Generate」,点击 Copy 将结果复制到 AstrBot 配置页的 app_token 处。 + +![image](https://files.astrbot.app/docs/source/images/slack/image-2.png) + +5. 在左边栏 OAuth & Permissions 页下,在 Bot Token Scopes 下方添加如下权限: + - channels:history + - channels:read + - channels:write.invites + - chat:write + - chat:write.customize + - chat:write.public + - files:read + - files:write + - groups:history + - groups:read + - groups:write + - im:history + - im:read + - im:write + - reactions:read + - reactions:write + - users:read + +6. 在左边栏 OAuth & Permissions 页下,在 Oauth Token 处点击 `Install to xxx`(xxx 是您工作区的名字)。然后复制生成的 Bot User OAuth Token 到平台适配器配置的 bot_token 处。 + +7. (仅 Socket 需要)在左边栏 Socket Mode 页下,开启 Enable Socket Mode。 + +![image](https://files.astrbot.app/docs/source/images/slack/image-3.png) + +## 启动平台适配器 + +现在,配置已经完成。如果您使用的是 Socket 模式,那么直接点击配置的右下角的保存按钮即可。 + +如果您使用的是 Webhook 模式,请保持 `统一 Webhook 模式 (unified_webhook_mode)` 为开启状态。 + +> [!TIP] +> v4.8.0 之前没有 `统一 Webhook 模式`,请填写以下配置项: +> Slack Webhook Host、Slack Webhook Port 和 Slack Webhook Path + +## 开启事件接收 + +新建平台适配器成功后,返回到 Slack 设置,在左边栏 Event Subscriptions 页下,点击 Enable Events 启用事件接收。 + +如果您使用的是 Webhook 模式: + +- 如果开启了 `统一 Webhook 模式`,点击保存之后,AstrBot 将会自动为你生成唯一的 Webhook 回调链接,你可以在日志中或者 WebUI 的机器人页的卡片上找到,将该链接填入 `Request URL` 输入框中。 + +![unified_webhook](https://files.astrbot.app/docs/source/images/use/unified-webhook.png) + +- 如果没有开启 `统一 Webhook 模式`,请在 `Request URL` 输入框中输入 `https://您的域名/astrbot-slack-webhook/callback`。 + +> [!TIP] +> Webhook 模式下,您需要先在 DNS 服务商处设置好域名,然后使用反向代理软件将请求转发到 AstrBot 所在服务器的 `6185` 端口(如果开启了统一 Webhook 模式)或配置指定的端口(如果没有开启统一 Webhook 模式)。或者您可以使用 Cloudflare Tunnel。具体教程请参考网络资源,本教程不赘述。 + +启用后,在下方的 Subscribe to bot events 处,点击 Add Bot User Event,添加如下事件: + +1. channel_created +2. channel_deleted +3. channel_left +4. member_joined_channel +5. member_left_channel +6. message.channels +7. message.groups +8. message.im +9. reaction_added +10. reaction_removed +11. team_join + +## 测试成功性 + +进入您刚刚添加的 Slack 工作区,进入需要用到 Bot 的频道,然后 @ 您刚刚创建的应用。然后点击 Slackbot 随后发送的消息中的 添加 按钮来添加到工作区中。然后,@ 应用,输入 `/help`,如果能够成功回复,说明测试成功。 + +如果有疑问,请[提交 Issue](https://github.com/AstrBotDevs/AstrBot/issues)。 diff --git a/docs/zh/platform/start.md b/docs/zh/platform/start.md new file mode 100644 index 000000000..6e48d5ddd --- /dev/null +++ b/docs/zh/platform/start.md @@ -0,0 +1,8 @@ +# 接入消息平台 + +AstrBot 支持接入众多主流即时通讯软件平台,帮助您在自己喜欢的 IM 平台上使用 AstrBot 的强大功能。 + +在 WebUI 中,点击侧边栏的**机器人**,即可进入消息平台接入界面。点击右上角的**创建机器人**,选择您想要接入的平台,按照本文档左侧提供的接入指南进行操作,即可完成接入。 + +> [!TIP] +> 建议在部署前预先安装 `ffmpeg`(并确保支持 `amr`),否则媒体类文件可能无法正常收发。对于微信类平台接入,强烈建议安装。 \ No newline at end of file diff --git a/docs/zh/platform/telegram.md b/docs/zh/platform/telegram.md new file mode 100644 index 000000000..0156b668d --- /dev/null +++ b/docs/zh/platform/telegram.md @@ -0,0 +1,56 @@ + +# 接入 Telegram + +## 支持的基本消息类型 + +> 版本 v4.15.0。 + +| 消息类型 | 是否支持接收 | 是否支持发送 | 备注 | +| --- | --- | --- | --- | +| 文本 | 是 | 是 | | +| 图片 | 是 | 是 | | +| 语音 | 是 | 是 | | +| 视频 | 是 | 是 | | +| 文件 | 是 | 是 | | + + +主动消息推送:支持。 + +## 1. 创建 Telegram Bot + +首先,打开 Telegram,搜索 `BotFather`,点击 `Start`,然后发送 `/newbot`,按照提示输入你的机器人名字和用户名。 + +创建成功后,`BotFather` 会给你一个 `token`,请妥善保存。 + +如果需要在群聊中使用,需要关闭Bot的 [Privacy mode](https://core.telegram.org/bots/features#privacy-mode),对 `BotFather` 发送 `/setprivacy` 命令,然后选择bot, 再选择 `Disable`。 + +## 2. 配置 AstrBot + +1. 进入 AstrBot 的管理面板 +2. 点击左边栏 `机器人` +3. 然后在右边的界面中,点击 `+ 创建机器人` +4. 选择 `telegram` + +弹出的配置项填写: + +- ID(id):随意填写,用于区分不同的消息平台实例。 +- 启用(enable): 勾选。 +- Bot Token: 你的 Telegram 机器人的 `token`。 + +请确保你的网络环境可以访问 Telegram。你可能需要使用 `配置页->其他配置->HTTP 代理` 来设置代理。 + +## 流式输出 + +Telegram 平台支持流式输出。需要在「AI 配置」->「其他配置」中开启「流式输出」开关。 + +### 私聊流式输出 + +在私聊中,AstrBot 使用 Telegram Bot API v9.3 新增的 `sendMessageDraft` API 实现流式输出。这种方式会在私聊界面展示一个「正在输入」的草稿预览动画,体验更接近「打字机」效果,且避免了传统方案的消息闪烁、推送通知干扰和 API 编辑频率限制等问题。 + +### 群聊流式输出 + +在群聊中,由于 `sendMessageDraft` API 仅支持私聊,AstrBot 会自动回退到传统的 `send_message` + `edit_message_text` 方案。 + +:::warning +`sendMessageDraft` 功能需要 `python-telegram-bot>=22.6`。 +::: diff --git a/docs/zh/platform/vocechat.md b/docs/zh/platform/vocechat.md new file mode 100644 index 000000000..67bd7e222 --- /dev/null +++ b/docs/zh/platform/vocechat.md @@ -0,0 +1,43 @@ +# 接入 VoceChat + +> [!TIP] +> AstrBot 未自带这个适配器,需要安装 [astrbot_plugin_vocechat](https://github.com/HikariFroya/astrbot_plugin_vocechat) 插件。该插件由 [HikariFroya](https://github.com/HikariFroya) 开发 ❤️。 +> **如果您觉得有帮助,请支持开发者,给该仓库点一个 Star。** + +> [!WARNING] +> 这个适配器目前不由 AstrBot 官方维护,因此稳定性未知。 + +## 部署 VoceChat + +VoceChat 是一个开源的支持多平台、搭建简单的即时通讯平台。 + +请在 [VoceChat 官方网站](https://voce.chat/zh-CN)查看部署方式。 + +## 安装 astrbot_plugin_vocechat 插件 + +进入 AstrBot 仪表盘的插件市场,搜索 `astrbot_plugin_vocechat`,点击安装。 + +![image](https://files.astrbot.app/docs/source/images/vocechat/image.png) + +安装完成后,前往 `机器人` → `+ 创建机器人` → 选择 VoceChat(若选项缺失,尝试重启 AstrBot 或检查插件安装状态)。 + +在弹出的配置对话框中点击 `启用`。 + +## 配置 + +- **`vocechat_server_url` (必填)**: 您的 VoceChat 服务器的完整 URL 地址。例如: `http://localhost:3009` 或 `https://your.vocechat.domain`。请确保末尾没有 `/`。 +- **`api_key` (必填)**: 您在 VoceChat 后台为该机器人账号生成的 API Key。 +- **`webhook_path` (建议保留默认或自定义)**: AstrBot 用于接收 VoceChat 推送消息的 Webhook 路径。例如: `/vocechat_webhook`。您需要在 VoceChat 机器人设置中填写的 Webhook URL 将是 `http://<你的AstrBot可访问地址>:`。 +- **`webhook_listen_host` (通常为 `0.0.0.0`)**: AstrBot Webhook 服务器监听的IP地址。`0.0.0.0` 表示监听所有可用的网络接口。 +- **`webhook_port` (必填)**: AstrBot Webhook 服务器监听的端口号。例如: `8080`。请确保此端口未被其他应用占用,并且如果您的 AstrBot 服务器在防火墙后,此端口需要被允许访问。 +- **`get_user_nickname_from_api` (布尔值, 默认: `true`)**: 是否尝试通过 VoceChat API 获取用户昵称。如果为 `false`,将使用 `VoceChatUser_UID` 作为默认昵称。 +- **`send_plain_as_markdown` (布尔值, 默认: `false`)**: 如果为 `true`,发送纯文本消息时会使用 Markdown 格式(可能会影响部分纯文本的显示,但能更好地支持一些特殊字符)。通常建议保持 `false`,除非有特定需求。 +- **`default_bot_self_uid` (必填)**: 您要连接的这个 VoceChat 机器人账号在 VoceChat 中的用户 ID (UID)。 + +全部配置好后,点击右下角的保存按钮。然后前往 VoceChat 中测试。 + +## 问题提交 + +如有疑问,请提交 issue 至[插件仓库](https://github.com/HikariFroya/astrbot_plugin_vocechat/issues) 以及 [AstrBot 仓库](https://github.com/AstrBotDevs/AstrBot/issues/new?template=bug-report.yml)。 + +**如果您觉得有帮助,请支持开发者,给 [astrbot_plugin_vocechat](https://github.com/HikariFroya/astrbot_plugin_vocechat) 仓库点一个 Star。** diff --git a/docs/zh/platform/wecom.md b/docs/zh/platform/wecom.md new file mode 100644 index 000000000..0774a05fe --- /dev/null +++ b/docs/zh/platform/wecom.md @@ -0,0 +1,148 @@ +# AstrBot 接入企业微信 + +AstrBot 支持接入企业微信应用和微信客服。 + +## 支持的基本消息类型 + +> 版本 v4.15.0。 + +| 消息类型 | 是否支持接收 | 是否支持发送 | 备注 | +| --- | --- | --- | --- | +| 文本 | 是 | 是 | | +| 图片 | 是 | 是 | | +| 语音 | 是 | 是 | | +| 视频 | 否 | 是 | | +| 文件 | 否 | 是 | | + +主动消息推送:企业微信应用支持,未测试企业微信客服。 + +## 准备接入 + +步骤: + +1. 进入 AstrBot 的管理面板 +2. 点击左边栏 `机器人` +3. 然后在右边的界面中,点击 `+ 创建机器人` +4. 选择 `wecom` + +这将弹出一个对话框。接下来,不要关闭页面,转移到下一步。 + +## 接入方式一:微信客服 + +> [!NOTE] +> +> 1. 需要 >= v3.5.7 +> 2. 以这种方式接入,支持在微信内使用。 + +1. 进入 [微信客服后台](https://kf.weixin.qq.com/),使用企业微信扫码登录。 + +2. **得到客服账号名。** 在 `客服账号` 中创建一个客服账号,记录下名称,填入 AstrBot 配置的 `微信客服账号名` 中(不是账号 ID)。 + +3. **得到企业 ID。** 在 [企业微信 - 企业信息](https://work.weixin.qq.com/wework_admin/frame#profile) 得到企业 ID(`Corpid`),复制到 AstrBot 配置的 `corpid` 处。 + +4. **回调服务器验证。** 如果您之前没有使用过微信客服机器人,那么请在 `开发配置` 中点击企业内部接入右侧的 `开始使用` 按钮,您应该会看到回调配置的页面。 + +![image](https://files.astrbot.app/docs/source/images/wecom/8287fd9fec5823847e6b590dc3f0f545.png) + +如果您之前使用过微信客服机器人,那么在 `开发配置` 中直接找到 `回调配置`,点击修改。 + +点击下方的两个随机获取,得到 `Token` 和 `EncodingAESKey`,复制到 AstrBot 配置的 `token` 和 `encoding_aes_key` 处。请保持 `统一 Webhook 模式 (unified_webhook_mode)` 为开启状态。然后点击保存配置,等待适配器加载完成。 + +回调 URL 填写: + +- 如果开启了 `统一 Webhook 模式`,点击保存之后,AstrBot 将会自动为你生成唯一的 Webhook 回调链接,你可以在日志中或者 WebUI 的机器人页的卡片上找到,将该链接填入回调 URL 处。 + +![unified_webhook](https://files.astrbot.app/docs/source/images/use/unified-webhook.png) + +- 如果没有开启 `统一 Webhook 模式`,填写 `http://你的带公网地址的服务器ip:6195/callback/command`。 + +> 请注意放行端口。如果开启了统一 Webhook 模式,需要将请求转发到 AstrBot 所在服务器的 `6185` 端口;如果没有开启,则转发到配置指定的端口(默认 `6195`)。 + +回到微信客服 `回调配置`,点击 `完成`。如果一切无误,将会显示 `已完成`(否则会显示类似 `openapi 回调不通过` 类似的文本)。 + +1. **获取 Secret。** 之后,在 `开发配置` 中得到 Secret,找到复制到刚刚创建的企业微信适配器,点击编辑,然后修改配置中的 `secret`。然后再次保存配置,等待适配器加载完成。 + +> [!TIP] +> 根据 [#571](https://github.com/Soulter/AstrBot/issues/571) 的反馈,对于新注册的企业,`corp_id` 可能要注册一段时间后才生效(前后大概过了半个小时)。 + +然后,打开 `控制台` 页,你应该会看到如下日志: + +```txt +请打开以下链接,在微信扫码以获取客服微信 ... +``` + +![image](https://files.astrbot.app/docs/source/images/wecom/image-13.png) + +打开链接,用微信扫码,然后即可打开微信客服聊天页,输入 `help` 测试是否正常连通。 + +## 接入方式二:企业微信应用 + +进入 https://work.weixin.qq.com/wework_admin/frame#apps + +点击 `我的企业`,查看并得到企业 ID(`Corpid`),复制到 AstrBot 配置的 `corpid` 处。 + +> [!TIP] +> 根据 [#571](https://github.com/Soulter/AstrBot/issues/571) 的反馈,对于新注册的企业,`corp_id` 可能要注册一段时间后才生效(前后大概过了半个小时)。 + +![image](https://files.astrbot.app/docs/source/images/wecom/image-5.png) + +点击下面的 `自建应用`,然后点击 `创建应用`,填写好应用名称、头像、应用可见范围等信息。 + +进入应用,查看并得到机器人的 `Secret`,复制到 AstrBot 配置的 `secret` 处。 + +![image](https://files.astrbot.app/docs/source/images/wecom/image-4.png) + +在下方,找到 `接收消息`,点击 `设置 API 接收`,进入 API 接收页面。 + +![image](https://files.astrbot.app/docs/source/images/wecom/image-6.png) + +![image](https://files.astrbot.app/docs/source/images/wecom/image-9.png) + +并且点击下方的两个随机获取,得到 `Token` 和 `EncodingAESKey`,复制到 AstrBot 配置的 `token` 和 `encoding_aes_key` 处。建议保持 `统一 Webhook 模式 (unified_webhook_mode)` 为开启状态。 + +现在应该已经填完 AstrBot 连接到企业微信的所有配置项。点击 AstrBot 配置页右下角保存,等待 AstrBot 重启。 + +在 URL 处填入回调地址: + +- 如果开启了 `统一 Webhook 模式`,点击保存之后,AstrBot 将会自动为你生成唯一的 Webhook 回调链接,你可以在日志中或者 WebUI 的机器人页的卡片上找到,将该链接填入 URL 处。 + +![unified_webhook](https://files.astrbot.app/docs/source/images/use/unified-webhook.png) + +- 如果没有开启 `统一 Webhook 模式`,填入 `http://你的带公网地址的服务器ip:6195/callback/command`。 + +> 请注意放行端口。如果开启了统一 Webhook 模式,需要将请求转发到 AstrBot 所在服务器的 `6185` 端口;如果没有开启,则转发到配置指定的端口(默认 `6195`)。 + +接下来配置企业可信 IP。 + +![image](https://files.astrbot.app/docs/source/images/wecom/image-10.png) + +将你的 公网 IP 地址填写到此处,点击确定。 + + +重启成功后,回到API 接收页面,点击下面的保存,看是否能\够保存成功。如果出现 `openapi 请求回调地址不通过` 说明配置有问题,请检查四个配置项是否填写正确。 + +如果能够保存成功,AstrBot 就已经能够接收信息。 + +## 测试 + +在企业微信-工作台中,找到刚刚创建的应用,发送 `/help`,看看 AstrBot 是否能够回复。 + +![image](https://files.astrbot.app/docs/source/images/wecom/3dc9fa61145ab0dd8f56a10295affec8_720.png) + +## 反向代理(自定义 API BASE) + +AstrBot 支持自定义企业微信的终结点以适应家庭 ip 没有固定的公网 IP 问题。 + +只需要将您的自定义地址填入 `api_base_url` 即可。 + +> 如果您没有公网 ip 当然也可以购买一台服务器,推荐 阿里云 的 99 元/年的服务器。 + +## 语音输入 + +为了语音输入,需要你的电脑上安装有 `ffmpeg`。 + +linux 用户可以使用 `apt install ffmpeg` 安装。 + +windows 用户可以在 [ffmpeg 官网](https://ffmpeg.org/download.html) 下载安装。 + +mac 用户可以使用 `brew install ffmpeg` 安装。 diff --git a/docs/zh/platform/wecom_ai_bot.md b/docs/zh/platform/wecom_ai_bot.md new file mode 100644 index 000000000..904c0790c --- /dev/null +++ b/docs/zh/platform/wecom_ai_bot.md @@ -0,0 +1,77 @@ +# 接入企业微信智能机器人平台 + +企业微信智能机器人是企业微信官方推出的 AI 友好的机器人平台,可在单聊或群聊(企业微信内部群)中直接使用,并且支持流式传输。 + +## 支持的基本消息类型 + +> 版本 v4.15.0。 + +| 消息类型 | 是否支持接收 | 是否支持发送 | 备注 | +| --- | --- | --- | --- | +| 文本 | 是 | 是 | | +| 图片 | 是 | 是 | 仅限配置了消息推送 Webhook URL。| +| 语音 | 否 | 是 | 仅限配置了消息推送 Webhook URL。| +| 视频 | 否 | 是 | 仅限配置了消息推送 Webhook URL。| +| 文件 | 否 | 是 | 仅限配置了消息推送 Webhook URL。| + +主动消息推送:支持,但需要配置消息推送 Webhook URL。 + +## 配置智能机器人 + +1. 登录到[企业微信后台](https://work.weixin.qq.com/wework_admin)。 + +2. 在左侧导航栏中,点击 `管理工具`,找到 `智能机器人`,点击进入,然后点击创建机器人。 + +![管理工具-智能机器人](https://files.astrbot.app/docs/source/images/wecom_ai_bot/image-1.png) + +3. 在创建智能机器人页面下方找到并点击 `API模式创建`。填写机器人名称、头像等基本信息。Token、EncodingAESKey 请点击 `随机获取` 按钮生成。生成之后,先不要点击创建,接下来将配置 AstrBot。 + +![创建智能机器人账号](https://files.astrbot.app/docs/source/images/wecom_ai_bot/image.png) + +## 配置 AstrBot + +1. 进入 AstrBot 的管理面板,点击左侧栏 `消息平台`,然后在右侧的界面中,点击 `+ 新增适配器`,选择 `企业微信智能机器人`,进入配置页面。 + +![新增适配器](https://files.astrbot.app/docs/source/images/wecom_ai_bot/image-2.png) + +2. 在弹出的配置项中将 `企业微信智能机器人的名字`、`token`、`encoding_aes_key` 从上一步创建智能机器人时填写的值复制粘贴到对应的输入框中。ID 可以随意填写,用于区分不同的消息平台实例。`port` 默认为 `6198`,可以根据需要修改,但请确保该端口未被占用。请保持 `统一 Webhook 模式 (unified_webhook_mode)` 为开启状态。点击 `保存`。 + +3. 回到企业微信智能机器人创建页面,填写 `URL`: + + - 如果开启了 `统一 Webhook 模式`,点击保存之后,AstrBot 将会自动为你生成唯一的 Webhook 回调链接,你可以在日志中或者 WebUI 的机器人页的卡片上找到,将该链接填入 `URL` 处。 + + ![unified_webhook](https://files.astrbot.app/docs/source/images/use/unified-webhook.png) + + - 如果没有开启 `统一 Webhook 模式`,填写 `http://IP:port/webhook/wecom-ai-bot`,其中 `IP` 替换为你的 AstrBot 服务器的公网 IP 地址,`port` 替换为上一步填写的端口号。 + +> 建议有能力的用户自行配置域名和反向代理,将请求转发到 AstrBot 所在服务器的 `6185` 端口(如果开启了统一 Webhook 模式)或配置指定的端口(如果没有开启统一 Webhook 模式),并使用 HTTPS 协议。如果没有域名,也可以使用 [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-guide/)。 + +4. 点击 `创建` 按钮,如果一切无误,将进入智能机器人详情页面。如果报错 `服务没有正确响应,请确认后重试`,请检查 AstrBot 的配置、服务器防火墙端口放行规则等。 + +![创建智能机器人详情页面](https://files.astrbot.app/docs/source/images/wecom_ai_bot/image-3.png) + +5. [可选,推荐] 配置企业微信消息推送 Webhook URL。默认情况下,企业微信智能机器人只能在用户主动发送消息时被动回复消息。如果希望实现机器人主动消息推送功能,可以配置企业微信的消息推送 Webhook URL。只需要在企业微信内部群中,点击群设置 -消息推送,创建一个推送机器人,然后将下方生成的 Webhook URL 填入配置中即可。要求 AstrBot 版本不低于 v4.15.0。企业微信智能机器人之支持图片和文本消息类型,如果配置了该选项,在发送其他类型消息(如视频、音频、文件)时,AstrBot 将会调用消息推送的接口去发送消息。**强烈建议配置该选项以获得更完整的消息类型支持。** + +6. [可选,推荐] 企业微信智能机器人只支持对用户的一个消息回复最多一个消息气泡。如果您希望机器人发送更复杂的消息(例如连续发送多条消息、包含图片或文件的消息等),您可打开 「仅使用 Webhook 发送消息」。这将仅使用 Webhook 方式发送消息,绕过企业微信智能机器人的回复限制。**如果您不需要类似企业微信智能机器人那样的打字机效果,强烈建议您打开此选项。**此选项需要您配置第 5 步中的消息推送 Webhook URL。 + +## 使用智能机器人 + +### 将机器人添加到群聊 + +在企业微信客户端的企业内部群中,点击添加成员,点击智能机器人,找到刚刚创建的智能机器人,点击添加即可。 + +![点击添加成员](https://files.astrbot.app/docs/source/images/wecom_ai_bot/image-4.png) + +![添加成功](https://files.astrbot.app/docs/source/images/wecom_ai_bot/image-5.png) + +### 使用机器人 + +在单聊或群聊中,直接发送消息即可与机器人进行对话。 + +如果您需要类似实时打字机的效果,请确保在 AstrBot 中开启了 `流式回复` 功能。 + +![流式回复](https://files.astrbot.app/docs/source/images/wecom_ai_bot/image-6.png) + +## 帮助与支持 + +如您在配置或使用过程中遇到问题,或需要其他企业支持服务,可发送邮件至 [community@astrbot.app](mailto://community@astrbot.app)。 diff --git a/docs/zh/platform/weixin-official-account.md b/docs/zh/platform/weixin-official-account.md new file mode 100644 index 000000000..90c4fc1d4 --- /dev/null +++ b/docs/zh/platform/weixin-official-account.md @@ -0,0 +1,83 @@ +# AstrBot 接入微信公众平台 + +AstrBot 支持接入微信公众平台(版本 >= v3.5.8),并以微信公众号的形式接入,届时,您可以直接在微信公众号聊天界面中与 AstrBot 进行交互。 + +## 准备接入 + +步骤: + +1. 进入 AstrBot 的管理面板 +2. 点击左边栏 `机器人` +3. 然后在右边的界面中,点击 `+ 创建机器人` +4. 选择 `weixin_official_account(微信公众平台)` + +这将弹出一个对话框。接下来,不要关闭页面,转移到下一步。 + +## 创建/登入微信公众平台 + +进入[微信公众平台](https://mp.weixin.qq.com/),如果您需要接入现有的公众号请直接登录即可,如果没有,请点击立即注册然后选择 `公众号` 并填写相关信息注册。 + +> [!NOTE] +> 新注册的公众号需要花费 1-2 天审核,期间不能使用。 + +## 设置回调服务 + +点击 `设置与开发` -> `开发接口管理`。界面如下: + +![开发接口管理](https://files.astrbot.app/docs/source/images/weixin-official-account/image.png) + +记录开发者 ID(AppID) 和开发者密码(AppSecret),分别填入 AstrBot 配置的 `appid` 和 `secret` 处。 + +找到 IP白名单,点击查看,然后添加你的公网 IP 地址。如果有多个公网 IP 地址,换行分隔。 + +找到下方的服务器配置,然后点击修改配置。 + + +`Token` 由自己填写,请随意填写一个字符串,长度 3-32 位。并同样填入 AstrBot 配置的 `token` 处(一定要相同)。 + +`EncodingAESKey` 请点击随机生成,然后复制到 AstrBot 配置的 `encoding_aes_key` 处。 + +建议保持 `统一 Webhook 模式 (unified_webhook_mode)` 为开启状态。 + +现在应该已经填完 AstrBot 连接到微信公众平台的所有配置项。点击 AstrBot 配置页右下角保存,等待 AstrBot 重启。 + +`URL` 填写: + +- 如果开启了 `统一 Webhook 模式`,点击保存之后,AstrBot 将会自动为你生成唯一的 Webhook 回调链接,你可以在日志中或者 WebUI 的机器人页的卡片上找到,将该链接填入 URL 处。 + +![unified_webhook](https://files.astrbot.app/docs/source/images/use/unified-webhook.png) + +- 如果没有开启 `统一 Webhook 模式`,请填入 `http://你的域名/callback/command`。 + +> 注意⚠️:仅支持 80 或者 443 端口。您可能需要购买域名,然后反向代理流量到 AstrBot 所在服务器的 `6185` 端口(如果开启了统一 Webhook 模式)或 `6194` 端口(如果没有开启统一 Webhook 模式),或者将端口改成 80 端口(注意服务器需要没有软件在占用 80 端口)。 + +消息加解密方式请选中 `安全模式`。 + +等待片刻,点击 `提交`。如果一切无误,会显示 `提交成功`。 + +## 测试 + +点击左下角你的账号头像,点击账号详情,找到 `二维码`,扫码进入到公众号聊天界面,发送 `help`,看看 AstrBot 是否能够回复。 + +如果可以回复,说明接入成功。 + +> [!NOTE] +> 如果没有回复,并且控制台报错 `ip xxxxx not in whitelist`,说明你没有添加公网 IP 地址到微信公众平台的 IP 白名单中。如果确认添加了,那请等待若干分钟以让微信服务器更新。 + +## 反向代理(自定义 API BASE) + +AstrBot 支持自定义企业微信的终结点以适应家庭 ip 没有固定的公网 IP 问题。 + +只需要将您的自定义地址填入 `api_base_url` 即可。 + +> 如果您没有公网 ip 当然也可以购买一台服务器,推荐 阿里云 的 99 元/年的服务器。 + +## 语音输入 + +为了语音输入,需要你的电脑上安装有 `ffmpeg`。 + +linux 用户可以使用 `apt install ffmpeg` 安装。 + +windows 用户可以在 [ffmpeg 官网](https://ffmpeg.org/download.html) 下载安装。 + +mac 用户可以使用 `brew install ffmpeg` 安装。 \ No newline at end of file diff --git a/docs/zh/providers/302ai.md b/docs/zh/providers/302ai.md new file mode 100644 index 000000000..50a3abe77 --- /dev/null +++ b/docs/zh/providers/302ai.md @@ -0,0 +1,21 @@ +# 接入 302.AI + +302.AI 是企业级 AI 应用平台,支持快捷接入全球各类 AI 模型。 + +## 使用 + +点击[此链接](https://share.302.ai/rr1M3l) 注册账户。 + +注册完毕之后,点击[此链接](https://302.ai/apis/)选择需要接入的模型。 + +根据需求,进入[此链接](https://dash.302.ai/charge) 充值对应的金额。 + +## 接入 + +打开 AstrBot 控制台 -> 服务提供商页面,点击新增提供商,找到并点击 `302.AI`(需要版本 >= 3.5.18) + +修改 ID,并将 API Key 和模型名称填入对话框表单,点击保存,即可完成创建。 + +## 使用 + +对机器人输入 `/provider` 指令,将提供商切换到刚刚添加的 302.AI 提供商,即可使用。 diff --git a/docs/zh/providers/agent-runners.md b/docs/zh/providers/agent-runners.md new file mode 100644 index 000000000..34b9510b1 --- /dev/null +++ b/docs/zh/providers/agent-runners.md @@ -0,0 +1,19 @@ +# Agent 执行器 + +## 什么是 Agent 执行器? + +Agent 执行器是 AstrBot 中用于执行 Agent 的组件,Agent 执行器负责了所有关于 AI 的功能。 + +AstrBot 内置了**强大的** Agent 执行器,用户也可以选择接入第三方的 Agent 执行器服务,例如 Dify、Coze、阿里云百炼应用、DeerFlow 等,或者自己开发 Agent 执行器。 + +想象一下,你现在已经有 AI 模型提供商,它接收你的单次请求,然后返回响应。你需要一个东西来调用这个 AI 模型提供商来实现多轮对话、工具调用等功能,这就是 Agent 执行器 的作用。 + +要了解更多,请参阅 [使用 · Agent 执行器](/use/agent-runner)。 + +## 快捷链接 + +- [内置 Agent 执行器](/providers/agent-runners/astrbot-agent-runner) +- [Dify](/providers/agent-runners/dify) +- [扣子 Coze](/providers/agent-runners/coze) +- [阿里云百炼应用](/providers/agent-runners/dashscope) +- [DeerFlow](/providers/agent-runners/deerflow) diff --git a/docs/zh/providers/agent-runners/astrbot-agent-runner.md b/docs/zh/providers/agent-runners/astrbot-agent-runner.md new file mode 100644 index 000000000..8699236cd --- /dev/null +++ b/docs/zh/providers/agent-runners/astrbot-agent-runner.md @@ -0,0 +1,8 @@ +# 内置 Agent 执行器 + +默认情况下,AstrBot 内置 Agent 执行器为默认执行器,您不用进行任何配置,即可使用 AstrBot 的强大的内置 Agent 执行器。`✨ 接入模型服务` 中就是在配置 `内置 Agent 执行器` 的 AI 提供商。 + +![image](https://files.astrbot.app/docs/source/images/astrbot-agent-runner/image.png) + +在内置 Agent 执行器下,您可以使用 AstrBot 的 [MCP 服务器](/use/mcp)、[知识库](/use/knowledge-base)、[网页搜索](/use/websearch)、人格功能。 + diff --git a/docs/zh/providers/agent-runners/coze.md b/docs/zh/providers/agent-runners/coze.md new file mode 100644 index 000000000..80c673b61 --- /dev/null +++ b/docs/zh/providers/agent-runners/coze.md @@ -0,0 +1,64 @@ +# 接入 Coze + +AstrBot v4.2.1 之后的版本, 支持接入 [Coze](https://www.coze.cn/) 的 Agent 服务。 + +## 预备工作:准备 API Key + +首先我们注册并登录 [Coze](https://www.coze.cn/) 账号,然后进入 [API Key 管理页面](https://www.coze.cn/open/oauth/pats) 创建一个新的 API Key。 + +你可以按图片的步骤到达 API Key 管理页面, 也可以点击上面的链接直接进入。 + +![创建 API Key](https://files.astrbot.app/docs/source/images/coze/image_1.png) + +随后, 点击 "创建", 在下面的页面填写你的 API Key 名称, 选择一个过期时间(不建议使用永久令牌), 在 "权限" 处选择点击 "全选", 选择一个工作空间, 然后点击 "确定"。 + +![创建令牌](https://files.astrbot.app/docs/source/images/coze/image_2.png) + +随后我们可以得到一个新的 API Key, 请复制保存好, 后面会用到。 + +![新的 API Key](https://files.astrbot.app/docs/source/images/coze/image_3.png) + +## 预备工作: 配置智能体 + +进入 [项目开发](https://www.coze.cn/space/develop) 页面, 点击右上角 "+项目" 创建一个新的项目, 选择创建智能体。 + +![新建项目](https://files.astrbot.app/docs/source/images/coze/image_4.png) + +![新建项目](https://files.astrbot.app/docs/source/images/coze/image_5.png) + +**注意**: 完成智能体创建后, 必须先点击右上角的发布 **发布** 智能体, 并在发布中的 "选择发布平台" 处将 API 分栏全部勾选上, 然后点击 "发布"。 + +> 如果没有发布或发布时没有勾选 API 分栏, 则无法通过 API 调用智能体。 + +![发布智能体](https://files.astrbot.app/docs/source/images/coze/image_6.png) + +点击发布后, 智能体就创建完成了, 你可以在智能体开发页面的发布按钮左侧看到发布历史记录, 以确认智能体已经发布成功。 + +接下来注意在智能体开发页面的链接: + +![智能体开发](https://files.astrbot.app/docs/source/images/coze/image_7.png) + +例如示例中链接为: "https://www.coze.cn/space/7553214941005004863/bot/7553248674860826660" + +那么 `bot_id` 就是链接中 `bot/` 后面的那一串数字: `7553248674860826660` + +我们需要将 `bot_id` 记录下来, 后面会用到。 + +## 在 AstrBot 中配置 Coze + +完成了所有预备工作, 现在我们就可以在 AstrBot 中配置 Coze 了。 + +进入 AstrBot 管理面板 -> 服务提供商 -> 新增服务提供商 -> Coze, 进入配置页面。 + +![Coze 供应商](https://files.astrbot.app/docs/source/images/coze/image_8.png) + +填入刚刚创建的 API Key 和 bot_id, 然后点击保存。 + +> 其他配置说明: +> +> - API Base URL: 一般不需要修改, 如果你使用的是 Coze 国际版, 这里修改为: "https://api.coze.com" +> - 由 Coze 管理对话记录: 如描述所示。 + +## 选择 Agent 执行器 + +进入左边栏配置页面,点击「Agent 执行方式」,选择「Coze」,然后在下方出现的新的配置项中选择你刚刚创建的 Coze Agent 执行器的 ID,点击右下角「保存」,即可完成配置。 diff --git a/docs/zh/providers/agent-runners/dashscope.md b/docs/zh/providers/agent-runners/dashscope.md new file mode 100644 index 000000000..46811c32e --- /dev/null +++ b/docs/zh/providers/agent-runners/dashscope.md @@ -0,0 +1,51 @@ +# 接入阿里云百炼应用 + +在 v3.4.30 及之后,AstrBot 支持接入阿里云百炼应用。 + +## 在 AstrBot 中配置阿里云百炼应用 + +在 [阿里云百炼应用](https://bailian.console.aliyun.com/app-center#/app-center) 官网点击新增应用,根据自己的需要创建智能体应用或者工作流应用或者智能体编排应用,并且按照自己的需求构建好智能体或者工作流。 + +记录应用ID: + +![image](https://files.astrbot.app/docs/source/images/dashscope/image-1.png) + +点击进入应用,点击发布渠道->API 调用->API KEY,创建并且复制 API KEY: + +![alt text](https://files.astrbot.app/docs/source/images/dashscope/image-2.png) + +在 WebUI 中,点击「模型提供商」->「新增提供商」,选择「Agent 执行器」,选择「阿里云百炼应用」,进入阿里云百炼应用的配置页面。 + +根据阿里云百炼应用,一共有四种应用类型,分别是 + +- 智能体应用(agent) +- 任务型工作流应用(task-workflow) +- 对话型工作流应用(dialog-workflow) +- 智能体编排应用(agent-arrange) + +> [!TIP] +> 多轮对话仅支持智能体应用和对话型工作流应用。AstrBot 会自动为这两种应用附上对话历史记录以支持多轮对话。 + +请保证 AstrBot 里配置的 `应用类型` 和阿里云百炼应用里创建的应用类型一致。 + +然后将应用 ID 填写到 `dashscope_app_id`,API KEY 填写到 `dashscope_api_key`。 + +填写完这三项之后点击保存。 + +## 选择 Agent 执行器 + +进入左边栏配置页面,点击「Agent 执行方式」,选择「阿里云百炼应用」,然后在下方出现的新的配置项中选择你刚刚创建的阿里云百炼应用 Agent 执行器的 ID,点击右下角「保存」,即可完成配置。 + +## 附录:在聊天时动态设置 Workflow 输入变量(可选) + +对于两种工作流应用,可在聊天区动态设置输入的变量。 + +使用 `/set` 指令可以动态设置输入变量,如下图所示: + +![alt text](https://files.astrbot.app/docs/source/images/dify/image-5.png) + +当设置变量后,AstrBot 会在下次向阿里云百炼应用请求时附上您设置的变量,以灵活适配您的 Workflow。 + +当然,可以使用 `/unset` 指令来取消您所设置的变量。如 `/unset name` + +变量在当前会话永久有效。 diff --git a/docs/zh/providers/agent-runners/deerflow.md b/docs/zh/providers/agent-runners/deerflow.md new file mode 100644 index 000000000..a2c395b2b --- /dev/null +++ b/docs/zh/providers/agent-runners/deerflow.md @@ -0,0 +1,53 @@ +# 接入 DeerFlow + +在 v4.19.2 及之后,AstrBot 支持接入 [DeerFlow](https://github.com/bytedance/deer-flow) Agent Runner。 + +## 预备工作:部署 DeerFlow + +如果你还没有部署 DeerFlow,请先参考 DeerFlow 官方文档完成安装和启动: + +- [DeerFlow GitHub 仓库](https://github.com/bytedance/deer-flow) +- [DeerFlow 官方网站](https://deerflow.tech/) +- [DeerFlow 配置文档](https://github.com/bytedance/deer-flow/blob/main/backend/docs/CONFIGURATION.md) + +请确认 DeerFlow 已正常启动,并且 AstrBot 可以访问 DeerFlow 的网关地址。默认情况下,DeerFlow 网关地址为 `http://127.0.0.1:2026`。 + +> [!TIP] +> - `API Base URL` 必须以 `http://` 或 `https://` 开头。 +> - 如果 AstrBot 与 DeerFlow 运行在不同容器或主机上,请将 `127.0.0.1` 替换为 DeerFlow 实际可访问的内网地址、主机名或域名。 + +## 在 AstrBot 中配置 DeerFlow + +在 WebUI 中,点击「模型提供商」->「新增提供商」,选择「Agent 执行器」,选择「DeerFlow」,进入 DeerFlow 的配置页面。 + +填写以下配置项: + +- `API Base URL`:DeerFlow API 网关地址,默认为 `http://127.0.0.1:2026` +- `DeerFlow API Key`:可选。若你的 DeerFlow 网关使用 Bearer 鉴权,可在此填写 +- `Authorization Header`:可选。自定义 Authorization 请求头,优先级高于 `DeerFlow API Key` +- `Assistant ID`:对应 LangGraph 的 `assistant_id`,默认为 `lead_agent` +- `模型名称覆盖`:可选。覆盖 DeerFlow 默认模型 +- `启用思考模式`:是否启用 DeerFlow 的思考模式 +- `启用计划模式`:对应 DeerFlow 的 `is_plan_mode` +- `启用子智能体`:对应 DeerFlow 的 `subagent_enabled` +- `子智能体最大并发数`:对应 `max_concurrent_subagents`,仅在启用子智能体时生效,默认 `3` +- `递归深度上限`:对应 LangGraph 的 `recursion_limit`,默认 `1000` + +填写完成后点击「保存」。 + +> [!TIP] +> - 如果 DeerFlow 侧已经配置了默认模型,可以将 `模型名称覆盖` 留空。 +> - 只有在 DeerFlow 侧已经启用了相应能力时,才建议开启 `计划模式` 或 `子智能体` 相关选项。 + +## 选择 Agent 执行器 + +进入左边栏配置页面,点击「Agent 执行方式」,选择「DeerFlow」,然后在下方出现的新的配置项中选择你刚刚创建的 DeerFlow Agent 执行器提供商 ID,点击右下角「保存」,即可完成配置。 + +## 常见检查项 + +如果请求没有正常通过 DeerFlow 执行,请优先检查以下内容: + +- DeerFlow 服务是否已经正常启动 +- `API Base URL` 是否能从 AstrBot 所在环境访问 +- 鉴权配置是否填写正确 +- `Assistant ID` 是否与 DeerFlow 中实际可用的 assistant 一致 diff --git a/docs/zh/providers/agent-runners/dify.md b/docs/zh/providers/agent-runners/dify.md new file mode 100644 index 000000000..ef27613cc --- /dev/null +++ b/docs/zh/providers/agent-runners/dify.md @@ -0,0 +1,81 @@ +# 接入 Dify + +## 安装 Dify + +如果您还没有安装 Dify,请参考 [Dify 安装文档](https://docs.dify.ai/zh-hans/getting-started/install-self-hosted) 安装。 + +## 在 AstrBot 中配置 Dify + +在 WebUI 中,点击「模型提供商」->「新增提供商」,选择「Agent 执行器」,选择「Dify」,进入 Dify 的配置页面。 + +![image](https://files.astrbot.app/docs/source/images/dify/image.png) + +在 Dify 中,一个 `API Key` 唯一对应一个 Dify 应用。因此,您可以创建多个 Provider 以适配多个 Dify 应用。 + +根据目前的 Dify 项目,一共有三种类型,分别是: + +- chat +- agent +- workflow + +>[!TIP] +>请确保你在 AstrBot 里设置的 APP 类型和 Dify 里面创建的应用的类型一致。 +>![image](https://files.astrbot.app/docs/source/images/dify/image-3.png) + + +### Chat 和 Agent 应用 + +按下图所示创建你的 Dify Chat 和 Agent 应用的密钥: + +![image](https://files.astrbot.app/docs/source/images/dify/chat-agent-api-key.png) + +![image](https://files.astrbot.app/docs/source/images/dify/chat-agent-api-key-2.png) + +复制密钥并粘贴到配置中的 `API Key` 字段中,点击「保存」。 + +### Workflow 应用 + +#### 配置输入输出变量名 + +Workflow 应用接收输入变量,然后执行工作流,最后输出结果。 + +![image](https://files.astrbot.app/docs/source/images/dify/workflow-io-key.png) + +对于 Workflow 应用,AstrBot 在每次请求时会附上两个变量: + +- `astrbot_text_query`: 输入变量名。即用户输入的文本内容。 +- `astrbot_session_id`: 会话 ID + +你可以在配置中自定义输入变量名,即上图配置中的 “Prompt 输入变量名”。 + +您需要修改您的 Workflow 的输入的变量名以适配 AstrBot 的输入。 + +最终,Workflow 会输出一个结果,您可以自定义这个结果的变量名,即上图配置中的 “Dify Workflow 输出变量名”,默认为 `astrbot_wf_output`。你需要在 Dify 的 Workflow 的输出节点中配置这个变量名,否则 AstrBot 无法正确解析。 + +#### 创建 API Key + +按下图所示创建你的 Dify Workflow 应用的 API Key: + +点击右上角发布-访问 API-点击右上角 API 密钥-创建密钥,然后复制 API Key。 + +![image](https://files.astrbot.app/docs/source/images/dify/workflow-api-key.png) + +复制密钥并粘贴到配置中的 `API Key` 字段中,点击「保存」。 + +### 选择 Agent 执行器 + +进入左边栏配置页面,点击「Agent 执行方式」,选择「Dify」,然后在下方出现的新的配置项中选择你刚刚创建的 Dify Agent 执行器的 ID,点击右下角「保存」,即可完成配置。 + +## 附录:在聊天时动态设置输入 Workflow 变量(可选) + +可以使用 `/set` 指令动态设置输入变量,如下图所示: + +![alt text](https://files.astrbot.app/docs/source/images/dify/image-5.png) + +当设置变量后,AstrBot 会在下次向 Dify 请求时附上您设置的变量,以灵活适配您的 Workflow。 + +![alt text](https://files.astrbot.app/docs/source/images/dify/image-4.png) + +当然,可以使用 `/unset` 指令来取消设置的变量。 + +变量在当前会话永久有效。 \ No newline at end of file diff --git a/docs/zh/providers/aihubmix.md b/docs/zh/providers/aihubmix.md new file mode 100644 index 000000000..676e7bf0c --- /dev/null +++ b/docs/zh/providers/aihubmix.md @@ -0,0 +1,68 @@ +# 接入 AIHubMix + +[AIHubMix](https://aihubmix.com/?aff=4bfH) 是一个多模型 AI API 聚合平台,通过统一接口可调用 OpenAI、Claude、Gemini、DeepSeek、Kimi 等主流模型,同时支持语音、嵌入、重排序等多种能力。 + +API 格式完全兼容 OpenAI,只需修改 API Base 和 Key 即可接入。**部分模型免费,可直接用于开发测试。** + +## 获取 API Key + +1. 前往 [AIHubMix](https://aihubmix.com/?aff=4bfH) 注册账号 +2. 登录后在控制台 → API Keys 页面创建一个新的 Key +![获取 API Key](https://github.com/user-attachments/assets/d717f21b-2805-4aff-ac90-f5c98f17cb79) + +## 在 AstrBot 中配置 + +进入 AstrBot 管理面板,点击左栏 **服务提供商 → 新增提供商 → OpenAI**。 + +填写以下信息: + +| 配置项 | 值 | +|--------|-----| +| API Base URL | `https://aihubmix.com/v1` | +| API Key | 你在 AIHubMix 获取的 Key | + +保存后,点击该 provider 卡片,添加你需要的模型。 +![在 AstrBot 中配置](https://github.com/user-attachments/assets/ee2fb8ba-652c-4e97-a781-42a9082ad7eb) + +## 推荐模型 + +### 免费模型 🆓 + +以下模型完全免费,适合开发测试和轻量场景: + +| 模型 ID | 说明 | +|---------|------| +| `gpt-4.1-free` | GPT-4.1 免费版 | +| `gemini-3-flash-preview-free` | Gemini 3 Flash 免费版 | +| `coding-glm-5-free` | GLM-5 代码模型免费版 | +| `coding-minimax-m2.5-free` | MiniMax M2.5 代码模型免费版 | + +### 付费模型(常用推荐) + +| 模型 ID | 提供商 | 说明 | +|---------|--------|------| +| `gpt-5.4` | OpenAI | 最新旗舰模型 | +| `claude-sonnet-4-6` | Anthropic | 擅长推理和代码 | +| `gpt-5.3-chat-latest` | OpenAI | 高性能对话模型 | +| `deepseek-v3.2` | DeepSeek | 高性价比 | +| `kimi-k2.5` | Moonshot | 长上下文 | +| `gemini-3.1-pro-preview` | Google | 多模态 | + +> 完整模型列表请查看 [AIHubMix 文档](https://doc.aihubmix.com)。 + +## 不只是聊天模型 + +AIHubMix 同时支持以下能力,均可在 AstrBot 中配置: + +| 能力 | AstrBot 配置位置 | +|------|-----------------| +| 语音转文字 (STT) | 服务提供商 → 语音转文字 | +| 文字转语音 (TTS) | 服务提供商 → 文字转语音 | +| 嵌入 (Embedding) | 服务提供商 → 嵌入 | +| 重排序 (Rerank) | 服务提供商 → 重排序 | + +所有能力使用同一个 API Key 和 API Base,无需额外配置。 + +## 设为默认 + +前往 **配置 → 提供商设置**,将「默认聊天模型提供商」改为刚创建的 AIHubMix 提供商,保存即可。 diff --git a/docs/zh/providers/coze.md b/docs/zh/providers/coze.md new file mode 100644 index 000000000..ed527969d --- /dev/null +++ b/docs/zh/providers/coze.md @@ -0,0 +1 @@ +本页面已弃用,请参考 [Coze Agent 执行器](../agent-runners/coze.md)。 \ No newline at end of file diff --git a/docs/zh/providers/dashscope.md b/docs/zh/providers/dashscope.md new file mode 100644 index 000000000..1d2d57773 --- /dev/null +++ b/docs/zh/providers/dashscope.md @@ -0,0 +1 @@ +本页面已弃用,请参考 [阿里云百炼应用 Agent 执行器](../agent-runners/dashscope.md)。 \ No newline at end of file diff --git a/docs/zh/providers/dify.md b/docs/zh/providers/dify.md new file mode 100644 index 000000000..76897c8f5 --- /dev/null +++ b/docs/zh/providers/dify.md @@ -0,0 +1 @@ +本页面已弃用,请参考 [Dify Agent 执行器](../agent-runners/dify.md)。 \ No newline at end of file diff --git a/docs/zh/providers/llm.md b/docs/zh/providers/llm.md new file mode 100644 index 000000000..80f1a1fc8 --- /dev/null +++ b/docs/zh/providers/llm.md @@ -0,0 +1,13 @@ +# 大语言模型提供商 + +你可在管理面板->服务提供商->+新增服务提供商 处配置各种大语言模型服务。 + +> [!TIP] +> 如果没有你希望接入的模型服务,你可以试着查看您希望接入的服务提供商处是否支持 兼容 OpenAI API,如果支持,那么你可以选择上面截图中的第一项 `OpenAI` 然后通过修改 API Base URL 的方式接入。 + +![image](https://files.astrbot.app/docs/source/images/llm/image.png) + +![image](https://files.astrbot.app/docs/source/images/llm/image-1.png) + + +> 相应的配置保存在 `data/cmd_config.json` 的 `provider` 字段中。 \ No newline at end of file diff --git a/docs/zh/providers/newapi.md b/docs/zh/providers/newapi.md new file mode 100644 index 000000000..3dea474d7 --- /dev/null +++ b/docs/zh/providers/newapi.md @@ -0,0 +1,38 @@ +# 接入 NewAPI + +[New API](http://newapi.ai/) 是一个新一代大模型网关与 AI 资产管理系统,基于 One API 进行二次开发。该项目旨在提供一个统一的接口来管理和使用各种 AI 模型服务,包括但不限于 OpenAI、Anthropic、Gemini 和 Midjourney 等。 + +AstrBot 支持接入 NewAPI 作为模型提供商,用户可以通过 NewAPI 来访问和使用各种 AI 模型服务。 + +## 配置步骤 + +### 获取 NewAPI API Key 密钥 + +在 NewAPI 注册并登录后,点击上方导航栏的「控制台」,点击「令牌管理」,然后点击「添加令牌」按钮,创建一个新的 API Key 密钥,选择适当的权限,然后点击「创建」。 + +![create-api-key](https://files.astrbot.app/docs/source/images/newapi/image.png) + +创建成功后,点击复制密钥按钮,复制生成的 API Key 密钥。 + +![copy-api-key](https://files.astrbot.app/docs/source/images/newapi/image-1.png) +### 在 AstrBot 中配置 NewAPI 服务提供商 + +打开 AstrBot 管理面板,进入「模型提供商」页面,然后,点击「新增模型提供商」按钮。 + +NewAPI 完美地支持了 OpenAI Chat Completion 和 Responses 接口,我们点击 「OpenAI」,进入 OpenAI 提供商的配置页面。 + +在弹出的对话框中,将 API Base URL 设置为 NewAPI 的接口地址。如果您本地部署了 NewAPI,则填写本地地址,例如 `http://localhost:3000/v1`,如果您使用第三方服务商提供的 NewAPI 服务,则填写相应的 URL 地址,例如 `https://api.example.com/v1`。 + +然后,将 API Key 填入「API Key」字段中,点击「保存」按钮。 + +![astrbot-provider-config](https://files.astrbot.app/docs/source/images/newapi/image-2.png) + +然后点击保存,完成 NewAPI 提供商的配置。 + +### 应用服务提供商 + +进入「配置文件」页面,找到模型一节,将「默认聊天模型」修改为刚刚创建的 NewAPI 提供商,点击「保存」按钮。 + +![apply](https://files.astrbot.app/docs/source/images/newapi/image-3.png) + +至此,您已经成功配置了 NewAPI 作为 AstrBot 的模型提供商。现在,您可以通过 AstrBot 来访问和使用 NewAPI 提供的各种 AI 模型服务了。 diff --git a/docs/zh/providers/ppio.md b/docs/zh/providers/ppio.md new file mode 100644 index 000000000..fceb61dd1 --- /dev/null +++ b/docs/zh/providers/ppio.md @@ -0,0 +1,43 @@ +# 接入 PPIO 派欧云 + +PPIO 派欧云是中国领先的独立分布式云计算服务商,您可以在派欧云上使用稳定、低价甚至免费的模型服务。 + +## 准备 + +打开 [PPIO 派欧云官网](https://ppio.cn/user/register?invited_by=AIOONE),并注册账户(通过此链接注册的账户将会获得 15 元人民币的代金券)。 + +进入 [模型 API 服务](https://ppio.cn/model-api/console),找到你想接入的模型。你可以通过筛选器选择不同厂商或者免费的模型。 + +![image](https://files.astrbot.app/docs/source/images/ppio/image-1.png) + +找到你想要接入的模型后,点击模型卡片,侧边会展开一个模型详情卡片,找到下方的 API 接入指南,如果您还没创建过 Key 可以点击创建。 + +![image](https://files.astrbot.app/docs/source/images/ppio/image-3.png) + +打开 AstrBot 控制台 -> 服务提供商页面,点击新增提供商,找到并点击 `PPIO派欧云`(需要版本 >= 3.5.10,旧版本也可使用,见下文)。 + +![image](https://files.astrbot.app/docs/source/images/ppio/image.png) + +将 API Key 和模型名称填入对话框表单,点击保存,即可完成创建。 + +> [!TIP] +> 如果您是 AstrBot 旧版本(< 3.5.10)的用户,请打开 AstrBot 控制台 -> 服务提供商页面,点击新增提供商,找到 `OpenAI`,点击进入。 +> 1. 将 ID 命名为 `ppio`(随意) +> 2. 然后将 `API Base URL` 设置为 `https://api.ppinfra.com/v3/openai` +> 3. 然后将 API Key 和模型名称填入对话框表单,点击保存,即可完成创建。 + + +## 使用 + +对机器人输入 `/provider` 指令,将提供商切换到刚刚添加的 PPIO 派欧云提供商,即可使用。 + +## 常见问题 + +#### 显示 `400` 错误 + +```log +Error code: 400 - {'code': 400, 'message': '"auto" tool choice requires --enable-auto-tool-choice and --tool-call-parser to be set', 'type': 'BadRequestError'} +``` + + +请暂时使用 `/tool off_all` 禁用所有的函数调用工具即可使用,或者换用其他模型。 \ No newline at end of file diff --git a/docs/zh/providers/provider-lmstudio.md b/docs/zh/providers/provider-lmstudio.md new file mode 100644 index 000000000..969f7d7c8 --- /dev/null +++ b/docs/zh/providers/provider-lmstudio.md @@ -0,0 +1,38 @@ +# 接入 LM Studio 使用 DeepSeek-R1 等模型 + +LMStudio 允许在本地电脑上部署模型(需要电脑硬件配置符合要求) + +### 下载并安装 LMStudio + +https://lmstudio.ai/download + +### 下载并运行模型 + +https://lmstudio.ai/models + +跟随 LMStudio 下载并运行想要的模型,如 deepseek-r1-qwen-7b: + +```bash +lms get deepseek-r1-qwen-7b +``` + +### 配置 AstrBot + +在 AstrBot 上: + +点击 配置->服务提供商配置->加号->openai + +API Base URL 填写 `http://localhost:1234/v1` + +API Key 填写 `lm-studio` + +> 对于 Mac/Windows 使用 Docker Desktop 部署 AstrBot 部署的用户,API Base URL 请填写为 `http://host.docker.internal:1234/v1`。 +> 对于 Linux 使用 Docker 部署 AstrBot 部署的用户,API Base URL 请填写为 `http://172.17.0.1:1234/v1`,或者将 `172.17.0.1` 替换为你的公网 IP IP(确保宿主机系统放行了 1234 端口)。 + +如果 LM Studio 使用了 Docker 部署,请确保 1234 端口已经映射到宿主机。 + +模型名填写上一步选好的 + +保存配置即可。 + +> 输入 /provider 查看 AstrBot 配置的模型 \ No newline at end of file diff --git a/docs/zh/providers/provider-ollama.md b/docs/zh/providers/provider-ollama.md new file mode 100644 index 000000000..d1b08c92e --- /dev/null +++ b/docs/zh/providers/provider-ollama.md @@ -0,0 +1,48 @@ +# 接入 Ollama + +🦙 Ollama 是一款免费、开源的应用程序,让您能在自己的电脑上运行大型语言模型(LLM)。(硬件需满足要求) + +## 下载并安装 Ollama + +您可以在 [https://ollama.com](https://ollama.com/download) 下载 Ollama。 + +## 选择想要使用的模型 + +在 https://ollama.com/search 上选择想要使用的模型。 + +在终端上 (Windows 上是 Powershell) 输入 `ollama pull ` 下载模型。 + +model_name 格式:`:`。如 `deepseek-r1:8b`。 + +> 8b 参数量模型需要至少 16GB 显存。有关配置和参数量的详细信息,请参阅其他文档。 + +拉取完成后,输入 `ollama list` 查看已经拉取的模型。 + +然后使用 `ollama run ` 运行模型。 + +## 配置 AstrBot + +打开 AstrBot 控制台 -> 服务提供商页面,点击新增模型提供商,找到并点击 `Ollama`。 +![image](https://files.astrbot.app/docs/source/images/ollama/image.png) + +保存配置即可。 + +::: tip + +对于 Mac/Windows 使用 Docker Desktop 部署 AstrBot 部署的用户,API Base URL 请填写为 `http://host.docker.internal:11434/v1`。\ +对于 Linux 使用 Docker 部署 AstrBot 部署的用户,API Base URL 请填写为 `http://172.17.0.1:11434/v1`,或者将 `172.17.0.1` 替换为你的公网 IP(确保宿主机系统放行了 11434 端口)。\ +如果 Ollama 使用了 Docker 部署,请确保 11434 端口已经映射到宿主机。 + +::: + + +## FAQ + +报错: +``` +AstrBot 请求失败。 +错误类型: NotFoundError +错误信息: Error code: 404 - {'error': {'message': 'model "llama3.1-8b" not found, try pulling it first', 'type': 'api_error', 'param': None, 'code': None}} +``` + +请先看上面的教程,用 `ollama pull ` 拉取模型,然后使用 `ollama run ` 运行模型。 diff --git a/docs/zh/providers/siliconflow.md b/docs/zh/providers/siliconflow.md new file mode 100644 index 000000000..279d2c9db --- /dev/null +++ b/docs/zh/providers/siliconflow.md @@ -0,0 +1,19 @@ +# 接入硅基流动 + +硅基流动依托自研推理引擎实现大模型高效推理加速,提供高效能、低成本的多品类大模型 API 服务,按量收费,助力应用开发轻松实现。 + +## 配置对话模型 + +在硅基流动 [API Keys](https://cloud.siliconflow.cn/me/account/ak) 页面创建一个新的 API Key,留存备用。 + +在硅基流动[模型页面](https://cloud.siliconflow.cn/me/models)选择需要使用的模型,留存模型名称备用。 + +进入 AstrBot WebUI,点击左栏 `服务提供商` -> `新增提供商` -> 选择 `硅基流动`。 + +粘贴上面创建和选择的 `API Key` 和 `模型名称`,点击保存,完成创建。您可以点击下方 `服务提供商可用性` 的 `刷新` 按钮测试配置是否成功。 + +![配置对话模型_1](https://files.astrbot.app/docs/source/images/siliconflow/image.png) + +## 应用对话模型 + +在 AstrBot WebUI,点击左栏 `配置文件`,找到 AI 配置中的 `默认聊天模型`,选择刚刚创建的 `siliconflow`(硅基流动) 提供商,点击保存。 \ No newline at end of file diff --git a/docs/zh/providers/start.md b/docs/zh/providers/start.md new file mode 100644 index 000000000..e1241462e --- /dev/null +++ b/docs/zh/providers/start.md @@ -0,0 +1,41 @@ +# 接入模型服务 + +AstrBot 适配了 OpenAI、Google GenAI、Anthropic 三种原生 API 格式。您可以接入任意符合这三种 API 格式之一的模型服务提供商。 + +> [!NOTE] +> 如果您位于中国大陆境内,我们强烈建议您使用符合当地法律法规的由**模型厂商官方提供的**或经过备案的模型服务提供商,例如: +> +> - [MoonshotAI](https://moonshot.cn/) +> - [GLM](https://bigmodel.cn/) +> - [MiniMax](https://www.minimax.io/) +> - [Qwen](https://qwen.ai/apiplatform) +> - [DeepSeek](https://deepseek.com/) +> +> 上述提供商均支持 OpenAI API 格式,您可以通过其文档中有关 “OpenAI 格式接入” 的说明,找到 API Base URL 及 API Key,然后将其填入 AstrBot 的提供商配置中。 +> +> 请注意,使用未经备案的第三方模型服务提供商可能会导致服务不可用、信息泄露或其他法律风险,请谨慎选择。更多内容,请阅读我们的最终用户许可协议([EULA](https://github.com/AstrBotDevs/AstrBot/blob/master/EULA.md))。 + +例如,您可以选择接入如下(但不限于)模型提供商提供的模型服务: + +- OpenAI 官方提供的模型服务([OpenAI 官方网站](https://openai.com/)) +- Anthropic 官方提供的模型服务([Anthropic 官方网站](https://www.anthropic.com/)) +- Google 提供的 Gemini 模型服务([Google Cloud 官方网站](https://cloud.google.com/)) +- OpenRouter 提供的模型服务([OpenRouter 官方网站](https://openrouter.ai/)) + +## 以 DeepSeek 为例的接入步骤 + +以 DeepSeek 为例,假设您已经注册并登录了 DeepSeek 账户,接入步骤如下: + +- 进入 [DeepSeek 控制台](https://platform.deepseek.com/)。 +- 点击左侧导航栏的 “API Keys” 菜单,创建一个新的 API Key,并复制该 Key。 +- 点击左侧导航栏下方的 “接口文档” 链接,进入 API 文档页面。 +- 在 API 文档页面中,找到 “OpenAI 兼容接口” 相关的内容,记下 API Base URL,例如 `https://api.deepseek.com/v1`。(如果没有 /v1,就请加上 /v1)。 +- 打开 AstrBot 控制台 -> 服务提供商页面,点击新增提供商,找到并点击 `OpenAI`(如果其中有您想要接入的提供商的类型,请优先点击那些类型,如 DeepSeek,我们会针对部分提供商做适配优化)。将 API Key 填入对话框表单的 `API Key` 处,将 API Base URL 填入 `API Base URL` 处。 +- 点击获取模型列表,找到您想要使用的模型名称,点击右侧 + 号,然后将右侧的出现的开关打开。 +- 进入配置文件页面,找到对话模型,点击右侧的选择按钮,选择刚刚添加的提供商和模型,点击屏幕右下角的保存配置按钮即可。 + +## 使用环境变量加载 Key + +> v4.13.0 之后引入 + +支持使用环境变量加载模型服务提供商的 API Key。在提供商配置页面,将 API Key 一栏填写为 `$环境变量名称` 的名称即可,例如填写 `$DEESEEK_API_KEY`。 \ No newline at end of file diff --git a/docs/zh/providers/tokenpony.md b/docs/zh/providers/tokenpony.md new file mode 100644 index 000000000..500161431 --- /dev/null +++ b/docs/zh/providers/tokenpony.md @@ -0,0 +1,23 @@ +# 接入 TokenPony(小马算力) + +## 配置对话模型 + +注册并登录小马算力 [TokenPony](https://www.tokenpony.cn/3YPyf) 。 + +在小马算力 [API Keys](https://www.tokenpony.cn/#/user/keys) 页面创建一个新的 API Key,留存备用。 + +在小马算力[模型页面](https://www.tokenpony.cn/#/model)选择需要使用的模型,留存模型名称备用。 + +进入 AstrBot WebUI,点击左栏 `服务提供商` -> `新增提供商` -> 选择 `小马算力` (需要版本 >= 4.3.3) + +![配置对话模型_1](https://files.astrbot.app/docs/source/images/tokenpony/image.png) + +> 如果没有看到 `小马算力` 选项,您也可以直接点击图中的 `接入 OpenAI`,并将 `API Base URL` 修改为 `https://api.tokenpony.cn/v1`。 + +粘贴上面创建和选择的 `API Key` 和 `模型名称`,点击保存,完成创建。您可以点击下方 `服务提供商可用性` 的 `刷新` 按钮测试配置是否成功。 + +## 应用对话模型 + +在 AstrBot WebUI,点击左栏 `配置文件`,找到 AI 配置中的 `默认聊天模型`,选择刚刚创建的 `tokenpony`(小马算力) 提供商,点击保存。 + +![配置对话模型_2](https://files.astrbot.app/docs/source/images/tokenpony/image_1.png) \ No newline at end of file diff --git a/docs/zh/use/agent-runner.md b/docs/zh/use/agent-runner.md new file mode 100644 index 000000000..95a4d27e0 --- /dev/null +++ b/docs/zh/use/agent-runner.md @@ -0,0 +1,52 @@ +# Agent 执行器 + +Agent 执行器是 AstrBot 中用于执行 Agent 的组件。 + +在 v4.7.0 版本之后,我们将 Dify、Coze、阿里云百炼应用这三个提供商迁移到了 Agent 执行器层面,减少了与 AstrBot 目前功能的一些冲突。请放心,如果您从旧版本升级到 v4.7.0 版本,您无需进行任何操作,AstrBot 会自动为您迁移。此后,AstrBot 也新增了 DeerFlow Agent 执行器支持。 + +AstrBot 目前支持五种 Agent 执行器: + +- AstrBot 内置 Agent 执行器 +- Dify Agent 执行器 +- Coze Agent 执行器 +- 阿里云百炼应用 Agent 执行器 +- DeerFlow Agent 执行器 + +默认情况下,AstrBot 内置 Agent 执行器为默认执行器。 + +## 为什么需要抽象出 Agent 执行器 + +在早期版本中,Dify、Coze、阿里云百炼应用这类「自带 Agent 能力」的平台,是作为普通 Chat Provider 集成进 AstrBot 的。实践下来会发现,它们和传统「只负责补全文本」的 Chat Provider 有本质差异,强行放在同一层会带来很多设计和使用上的冲突。因此,从 v4.7.0 起,我们将它们抽象为独立的 Agent 执行器(Agent Runner)。 + +从架构上看,可以理解为: + +- Chat Provider 负责「说话」; +- Agent 执行器负责「思考 + 做事」。 + +Agent 执行器会调用 Chat Provider 的接口,并根据 Chat Provider 的回复,进行多轮「感知 → 规划 → 执行动作 → 观察结果 → 再规划」的循环。 + +Chat Provider 本质上是一个 `单轮补全接口`,输入 prompt + 历史对话 + 工具列表,输出模型回复(文本、工具调用指令等)。 + +而 Agent Runner 通常是一个 `循环(Loop)`,接收用户意图、上下文与环境状态,基于策略 / 模型做出规划(Plan),选择并调用工具(Act),从环境中读取结果(Observe),再次理解结果、更新内部状态,决定下一步动作,重复上述过程,直到任务完成或超时。 + +![image](https://files.astrbot.app/docs/source/images/use/agent-runner/agent-arch.svg) + +Dify、Coze、百炼应用、DeerFlow 等平台已经内置了这个循环,如果把它们当成普通 Chat Provider,会和 AstrBot 的内置 Agent 执行器功能冲突。 + +## 使用 + +默认情况下,AstrBot 内置 Agent 执行器为默认执行器。使用默认执行器已经可以满足大部分需求,并且可以使用 AstrBot 的 MCP、知识库、网页搜索等功能。 + +如果你需要使用 Dify、Coze、百炼应用、DeerFlow 等平台的能力,可以创建一个 Agent 执行器,并选择相应的提供商。 + +## 创建 Agent 执行器 + +![image](https://files.astrbot.app/docs/source/images/use/agent-runner/image-1.png) + +在 WebUI 中,点击「模型提供商」->「新增提供商」,选择「Agent 执行器」,选择你想接入的平台或执行器类型,填写相关信息即可。 + +## 更换默认 Agent 执行器 + +![image](https://files.astrbot.app/docs/source/images/use/agent-runner/image.png) + +在 WebUI 中,点击「配置」->「Agent 执行方式」,将执行器类型更换为你刚刚创建的 Agent 执行器类型,然后选择 `XX Agent 执行器提供商 ID` 为你刚刚创建的 Agent 执行器提供商的 ID,点击保存即可。 diff --git a/docs/zh/use/astrbot-agent-sandbox.md b/docs/zh/use/astrbot-agent-sandbox.md new file mode 100644 index 000000000..68bbdec16 --- /dev/null +++ b/docs/zh/use/astrbot-agent-sandbox.md @@ -0,0 +1,90 @@ +# Agent 沙盒环境 ⛵️ + +> [!TIP] +> 此功能目前处于技术预览阶段,可能会存在一些 Bug。如果您遇到了问题,请在 [GitHub](https://github.com/AstrBotDevs/AstrBot/issues) 上提交 issue。 + +在 `v4.12.0` 版本及之后,AstrBot 引入了 Agent 沙盒环境,以替代之前的代码执行器功能。沙盒环境给 Agent 提供了更安全、更灵活的代码执行和自动化操作能力。 + +![](https://files.astrbot.app/docs/source/images/astrbot-agent-sandbox/image.png) + +## 启用沙盒环境 + +目前,沙盒环境仅支持通过 Docker 来运行。我们目前使用了 [Shipyard](https://github.com/AstrBotDevs/shipyard) 项目作为 AstrBot 的沙盒环境驱动器。未来,我们会支持更多类型的沙盒环境驱动器,如 e2b。 + +## 性能要求 + +AstrBot 给每个沙盒环境限制最高 1 CPU 和 512 MB 内存。 + +我们建议您的宿主机至少有 2 个 CPU 和 4 GB 内存,并开启 Swap,以保证多个沙盒环境实例可以稳定运行。 + +### 使用 Docker Compose 部署 AstrBot 和 Shipyard + +如果您还没有部署 AstrBot,或者想更换为我们推荐的带沙盒环境的部署方式,推荐使用 Docker Compose 来部署 AstrBot,代码如下: + +```bash +git clone https://github.com/AstrBotDevs/AstrBot +cd AstrBot +# 修改 compose-with-shipyard.yml 文件中的环境变量配置,例如 Shipyard 的 access token 等 +docker compose -f compose-with-shipyard.yml up -d +docker pull soulter/shipyard-ship:latest +``` + +这会启动一个包含 AstrBot 主程序和沙盒环境的 Docker Compose 服务。 + +### 单独部署 Shipyard + +如果您已经部署了 AstrBot,但没有部署沙盒环境,可以单独部署 Shipyard。 + +代码如下: + +```bash +mkdir astrbot-shipyard +cd astrbot-shipyard +wget https://raw.githubusercontent.com/AstrBotDevs/shipyard/refs/heads/main/pkgs/bay/docker-compose.yml -O docker-compose.yml +# 修改 compose-with-shipyard.yml 文件中的环境变量配置,例如 Shipyard 的 access token 等 +docker compose -f docker-compose.yml up -d +docker pull soulter/shipyard-ship:latest +``` + +部署成功后,上述命令会启动一个 Shipyard 服务,默认监听在 `http://:8156`。 + +> [!TIP] +> 如果您使用 Docker 部署 AstrBot,您也可以修改上面的 Compose 文件,将 Shipyard 的网络与 AstrBot 放在同一个 Docker 网络中,这样就不需要暴露 Shipyard 的端口到宿主机。 + +## 配置 AstrBot 使用沙盒环境 + +> [!TIP] +> 请确保您的 AstrBot 版本在 `v4.12.0` 及之后。 + +在 AstrBot 控制台,进入 “配置文件” 页面,找到 “Agent 沙箱环境”,启用沙箱环境开关。 + +在出现的配置项中, + +对于 `Shipyard API Endpoint`,如果您使用上述的 Docker Compose 部署方式,填写 `http://shipyard:8156` 即可。如果您是单独部署的 Shipyard,请填写对应的地址,例如 `http://:8156`。 + +对于 `Shipyard Access Token`,请填写您在部署 Shipyard 时配置的访问令牌。 + +对于 `Shipyard Ship 存活时间(秒)`,这个定义了每个沙箱环境实例的存活时间,默认值为 3600 秒(1 小时)。您可以根据需要调整这个值。 + +对于 `Shipyard Ship 会话复用上限`,这个定义了每个沙箱环境实例可以复用的最大会话数,默认值为 10。也就是 10 个会话会共享同一个沙箱环境实例。您可以根据需要调整这个值。 + +填写好之后,点击右下角 “保存” 即可。 + +## 关于 `Shipyard Ship 存活时间(秒)` + +沙箱环境实例的存活时间定义了每个实例在被销毁之前可以存在的最长时间,这个时间的设置需要根据您的使用场景以及资源来决定。 + +- 新的会话加入已有的沙箱环境实例时,该实例会自动延长存活时间到这个会话请求的 TTL。 +- 当对沙箱环境实例执行操作后,该实例会自动延长存活时间到当前时间加上 TTL。 + +## 关于沙盒环境的数据持久化 + +Shipyard 会给每个会话分配一个工作目录,在 `/home/<会话唯一 ID>` 目录下。 + +Shipyard 会自动将沙盒环境中的 /home 目录挂载到宿主机的 `${PWD}/data/shipyard/ship_mnt_data` 目录下,当沙盒环境实例被销毁后,如果某个会话继续请求调用沙箱,Shipyard 会重新创建一个新的沙盒环境实例,并将之前持久化的数据重新挂载进去,保证数据的连续性。 + +## 其他同类社区插件 + +### luosheng520qaq/astrobot_plugin_code_executor + +如果您资源有限,不希望使用沙盒环境来执行代码,可以尝试 luosheng520qaq 开发的 [astrobot_plugin_code_executor](https://github.com/luosheng520qaq/astrobot_plugin_code_executor) 插件。该插件会直接在宿主机上执行代码。插件已经尽力提升安全性,但仍需留意代码安全性问题。 \ No newline at end of file diff --git a/docs/zh/use/code-interpreter.md b/docs/zh/use/code-interpreter.md new file mode 100644 index 000000000..62d4e5ff3 --- /dev/null +++ b/docs/zh/use/code-interpreter.md @@ -0,0 +1,96 @@ +# 基于 Docker 的代码执行器 + +> [!WARNING] +> 已过时,请参考最新的 [Agent 沙盒环境](/use/astrbot-agent-sandbox.md) 文档。在 v4.12.0 之后,该功能不可用。 + +在 `v3.4.2` 版本及之后,AstrBot 支持代码执行器以强化 LLM 的能力,并实现一些自动化的操作。 + +> [!TIP] +> 此功能目前处于实验阶段,可能会有一些问题。如果您遇到了问题,请在 [GitHub](https://github.com/AstrBotDevs/AstrBot/issues) 上提交 issue。欢迎加群讨论:[322154837](https://qm.qq.com/cgi-bin/qm/qr?k=EYGsuUTfe00_iOu9JTXS7_TEpMkXOvwv&jump_from=webapi&authKey=uUEMKCROfsseS+8IzqPjzV3y1tzy4AkykwTib2jNkOFdzezF9s9XknqnIaf3CDft)。 + +如果您要使用此功能,请确保您的机器安装了 `Docker`。因为此功能需要启动专用的 Docker 沙箱环境以执行代码,以防止 LLM 生成恶意代码对您的机器造成损害。 + + +## Linux Docker 启动 AstrBot + +如果您使用 Docker 部署了 AstrBot,需要多做一些工作。 + +1. 您需要在启动 Docker 容器时,请将 `/var/run/docker.sock` 挂载到容器内部。这样 AstrBot 才能够启动沙箱容器。 + +```bash +sudo docker run -itd -p 6180-6200:6180-6200 -p 11451:11451 -v $PWD/data:/AstrBot/data -v /var/run/docker.sock:/var/run/docker.sock --name astrbot soulter/astrbot:latest +``` + +2. 在聊天时使用 `/pi absdir <绝对路径地址>` 设置您宿主机上 AstrBot 的 data 目录的所在目录的绝对路径。 + +例子: + +![image](https://files.astrbot.app/docs/source/images/code-interpreter/image-4.png) + +## Linux 手动源码 启动 AstrBot + +**如果你的 Docker 指令需要 sudo 权限来执行**,那么你需要在启动 AstrBot 时,使用 `sudo` 来启动,否则代码执行器会因为权限不足而无法调用 Docker。 + +```bash +sudo —E python3 main.py +``` + +## 使用 + +本功能使用的镜像是 `soulter/astrbot-code-interpreter-sandbox`,您可以在 [Docker Hub](https://hub.docker.com/r/soulter/astrbot-code-interpreter-sandbox) 上查看镜像的详细信息。 + +镜像中提供了常用的 Python 库: + +- Pillow +- requests +- numpy +- matplotlib +- scipy +- scikit-learn +- beautifulsoup4 +- pandas +- opencv-python +- python-docx +- python-pptx +- pymupdf +- mplfonts + +基本上能够实现的任务: + +- 图片编辑 +- 网页抓取等 +- 数据分析、简单的机器学习 +- 文档处理,如读写 Word、PPT、PDF 等 +- 数学计算,如画图、求解方程等 + +由于中国大陆无法访问 docker hub,因此如果您的环境在中国大陆,请使用 `/pi mirror` 来查看/设置镜像源。比如,截至本文档编写时,您可以使用 `cjie.eu.org` 作为镜像源。即设置 `/pi mirror cjie.eu.org`。 + +在第一次触发代码执行器时,AstrBot 会自动拉取镜像,这可能需要一些时间。请耐心等待。 + +镜像可能会不定时间更新以提供更多的功能,因此请定期查看镜像的更新。如果需要更新镜像,可以使用 `/pi repull` 命令重新拉取镜像。 + +> [!TIP] +> 如果一开始没有正常启动此功能,在启动成功之后,需要执行 `/tool on python_interpreter` 来开启此功能。 +> 您可以通过 `/tool ls` 查看所有的工具以及它们的启用状态。 + +![image](https://files.astrbot.app/docs/source/images/code-interpreter/image-3.png) + +## 图片和文件的输入 + +代码执行器除了能够识别和处理图片、文字任务,还能够识别您发送的文件,并且能够发送文件。 + +v3.4.34 后,使用 `/pi file` 指令开始上传文件。上传文件后,您可以使用 `/pi list` 查看您上传的文件,使用 `/pi clean` 清空您上传的文件。 + +上传的文件将会用于代码执行器的输入。 + +比如您希望对一张图片添加圆角,您可以使用 `/pi file` 上传图片,然后再提问:`请运行代码,对这张图片添加圆角`。 + +## Demo + +![image](https://files.astrbot.app/docs/source/images/code-interpreter/a3cd3a0e-aca5-41b2-aa52-66b568bd955b.png) + +![alt text](https://files.astrbot.app/docs/source/images/code-interpreter/image.png) + +![image](https://files.astrbot.app/docs/source/images/code-interpreter/image-1.png) + +![image](https://files.astrbot.app/docs/source/images/code-interpreter/image-2.png) diff --git a/docs/zh/use/command.md b/docs/zh/use/command.md new file mode 100644 index 000000000..067d37e21 --- /dev/null +++ b/docs/zh/use/command.md @@ -0,0 +1,5 @@ +# 内置指令 + +AstrBot 具有很多内置指令,它们通过插件的形式被导入。位于 `packages/astrbot` 目录下。 + +使用 `/help` 可以查看所有内置指令。 \ No newline at end of file diff --git a/docs/zh/use/context-compress.md b/docs/zh/use/context-compress.md new file mode 100644 index 000000000..1dc33bb7e --- /dev/null +++ b/docs/zh/use/context-compress.md @@ -0,0 +1,41 @@ +# 上下文压缩 + +在 v4.11.0 之后,AstrBot 引入了自动上下文压缩功能。 + +![alt text](https://files.astrbot.app/docs/source/images/context-compress/image.png) + +AstrBot 会在对话上下文达到**使用的对话模型上下文窗口的最大长度的 82% 时**,自动对上下文进行压缩,以确保在不丢失关键信息的情况下,尽可能多地保留对话内容。 + +## 压缩策略 + +目前有两种压缩策略 + +1. 按照对话轮数截断。这种策略会简单地删除最早的对话内容,直到上下文长度符合要求。您可以指定一次性丢弃的对话轮数,默认为 1 轮。这种策略为**默认策略**。 +2. 由 LLM 压缩上下文。这种策略会调用您指定的模型本身来总结和压缩对话内容,从而保留更多的关键信息。您可以指定压缩时使用的对话模型,如果不选择,将会自动回退到 “按照对话轮数截断” 策略。您可以设置压缩时保留最近对话轮数,默认为 4。您还可以自定义压缩时的提示词。默认提示词为: + +``` +Based on our full conversation history, produce a concise summary of key takeaways and/or project progress. +1. Systematically cover all core topics discussed and the final conclusion/outcome for each; clearly highlight the latest primary focus. +2. If any tools were used, summarize tool usage (total call count) and extract the most valuable insights from tool outputs. +3. If there was an initial user goal, state it first and describe the current progress/status. +4. Write the summary in the user's language. +``` + +在压缩一轮之后,AstrBot 会二次检查当前上下文长度是否符合要求。如果仍然不符合要求,则会采用对半砍策略,即将当前上下文内容砍掉一半,直到符合要求为止。 + +- AstrBot 会在每次对话请求前调用压缩器进行检查。 +- 当前版本下 AstrBot 不会在工具调用过程中进行上下文压缩,未来我们会支持这一功能,敬请期待。 + +## ‼️ 重要:模型上下文窗口设置 + +默认情况下,当您添加模型时,AstrBot 会自动根据模型的 id,从 [MODELS.DEV](https://models.dev/) 提供的接口中获取模型的上下文窗口大小。但由于模型种类繁多,部分提供商甚至会修改模型的 id,因此 AstrBot 不能自动推断出您所添加的模型的上下文窗口大小。 + +您可以手动在模型配置中设置模型的上下文窗口大小,参考下图: + +![alt text](https://files.astrbot.app/docs/source/images/context-compress/image1.png) + +> [!NOTE] +> 如果没有看到上图中的配置项,请您删除该模型,然后重新添加模型即可。 + +当模型上下文窗口大小被设置为 0 时,在每次请求时,AstrBot 仍会自动从 MODELS.DEV 获取模型的上下文窗口大小。如果仍为 0,则这次请求不会启用上下文压缩功能。 + diff --git a/docs/zh/use/custom-rules.md b/docs/zh/use/custom-rules.md new file mode 100644 index 000000000..20ff30f3e --- /dev/null +++ b/docs/zh/use/custom-rules.md @@ -0,0 +1,16 @@ +# 自定义规则 + +> [!NOTE] +> 下文的「消息会话来源」指的是 UMO。一个 UMO 唯一指定了一个消息平台下的具体的某个会话。 + +在 v4.7.0 版本之后,我们重构了 AstrBot 原来的「会话管理」功能为「自定义规则」功能。以减少和配置文件的冲突。 + +你可以把自定义规则理解为对指定消息来源更加灵活的自定义强制处理规则,其优先级高于配置文件。 + +例如,原本一个消息平台使用配置文件 “default”,这个消息平台下的所有会话都按照配置文件中的规则进行处理。如果你希望对某个会话来源 A 进行特殊处理,在原来,你需要单独创建一个配置文件,然后将 A 绑定到这个配置文件中。而现在,你只需要在 WebUI 的自定义规则页中创建一个自定义规则,然后选择消息来源 A 即可。你可以定义如下规则: + +1. 是否启用该消息会话来源的消息处理。如果不启用,其效果相当于将该消息会话来源拉入黑名单。 +2. 是否对该消息会话来源的消息启用 LLM。如果不启用,则不会使用 AI 能力。 +3. 是否对该消息会话来源的消息启用 TTS。如果不启用,则不会使用 TTS 能力。 +4. 对该消息会话来源配置特定的聊天模型、语音识别模型(STT)、语音合成模型(TTS)。 +5. 对该消息会话来源配置特定的人格。 \ No newline at end of file diff --git a/docs/zh/use/function-calling.md b/docs/zh/use/function-calling.md new file mode 100644 index 000000000..d1b076ac2 --- /dev/null +++ b/docs/zh/use/function-calling.md @@ -0,0 +1,52 @@ +--- +outline: deep +--- + +# 函数调用(Function-calling) + +## 简介 + +函数调用旨在提供大模型**调用外部工具的能力**,以此实现 Agentic 的一些功能。 + +比如,问大模型:帮我搜索一下关于“猫”的信息,大模型会调用用于搜索的外部工具,比如搜索引擎,然后返回搜索结果。 + +目前,支持的模型包括但远不限于 + +- GPT-5.x 系列 +- Gemini 3.x 系列 +- Claude 4.x 系列 +- Deepseek v3.2(deepseek-chat) +- Qwen 3.x 系列 + +2025年后推出的主流模型通常已支持函数调用。 + +不支持的模型比较常见的有 Deepseek-R1, Gemini 2.0 的 thinking 类等较老模型。 + +在 AstrBot 中,默认提供了网页搜索、待办提醒、代码执行器这些工具。很多插件,如: + +- astrbot_plugin_cloudmusic +- astrbot_plugin_bilibili +- ... + +等在提供传统的指令调用的基础上,也提供了函数调用的功能。 + +相关指令: + +- `/tool ls` 查看当前具有的工具列表 +- `/tool on` 开启某个工具 +- `/tool off` 关闭某个工具 +- `/tool off_all` 关闭所有工具 + +某些模型可能不支持函数调用,会返回诸如 `tool call is not supported`, `function calling is not supported`, `tool use is not supported` 等错误。在大多数情况下,AstrBot 能够检测到这种错误并自动帮您去除函数调用工具。如果你发现某个模型不支持函数调用,也可使用 `/tool off_all` 命令关闭所有工具,然后再次尝试。或者更换为支持函数调用的模型。 + + +下面是一些常见的工具调用 Demo: + +![image](https://files.astrbot.app/docs/source/images/function-calling/image.png) + +![image](https://files.astrbot.app/docs/source/images/function-calling/image-1.png) + + +## MCP + +请前往此文档 [AstrBot - MCP](/use/mcp) 查看。 \ No newline at end of file diff --git a/docs/zh/use/knowledge-base-old.md b/docs/zh/use/knowledge-base-old.md new file mode 100644 index 000000000..d2bfa7c78 --- /dev/null +++ b/docs/zh/use/knowledge-base-old.md @@ -0,0 +1,49 @@ +# AstrBot 知识库 + +![知识库预览](https://files.astrbot.app/docs/zh/use/image-3.png) + +## 配置嵌入模型 + +打开服务提供商页面,点击新增服务提供商,选择 Embedding。 + +目前 AstrBot 支持兼容 OpenAI API 和 Gemini API 的嵌入向量服务。 + +点击上面的提供商卡片进入配置页面,填写配置。 + +配置完成后,点击保存。 + +## 配置重排序模型(可选) + +重排序模型可以一定程度上提高最终召回结果的精度。 + +和嵌入模型的配置类似,打开服务提供商页面,点击新增服务提供商,选择重排序。有关重排序模型的更多信息请参考网络。 + +## 创建知识库 + +AstrBot 支持多知识库管理。在聊天时,您可以**自由指定知识库**。 + +进入知识库页面,点击创建知识库,如下图所示: + +![image](https://files.astrbot.app/docs/source/images/knowledge-base/image.png) + +填写相关信息。在嵌入模型下拉菜单中您将看到刚刚创建好的嵌入模型和重排序模型(重排序模型可选)。 + +> [!TIP] +> 一旦选择了一个知识库的嵌入模型,请不要再修改该提供商的**模型**或者**向量维度信息**,否则将**严重影响**该知识库的召回率甚至**报错**。 + +## 上传文件 + + + +## 附录 2:免费的嵌入模型申请 + +### PPIO 派欧云 + +1. 打开 [PPIO 派欧云官网](https://ppio.cn/user/register?invited_by=AIOONE),并注册账户(通过此链接注册的账户将会获得 15 元人民币的代金券)。 +2. 进入 [模型广场](https://ppio.cn/model-api/console),点击嵌入模型 +3. 点击 BAAI:BGE-M3 (截止至 2025-06-02,该模型在该平台免费)。 +4. 找到 API 接入指南,申请 Key。 +5. 填写 AstrBot OpenAI Embedding 模型提供商配置: + 1. API Key 为刚刚申请的 PPIO 的 API Key + 2. embedding api base 填写 `https://api.ppinfra.com/v3/openai` + 3. model 填写你选择的模型,此例子中为 `baai/bge-m3`。 diff --git a/docs/zh/use/knowledge-base.md b/docs/zh/use/knowledge-base.md new file mode 100644 index 000000000..d79336c25 --- /dev/null +++ b/docs/zh/use/knowledge-base.md @@ -0,0 +1,60 @@ +# AstrBot 知识库 + +> [!TIP] +> 需要 AstrBot 版本 >= 4.5.0。 +> +> 我们在 4.5.0 版本中重新设计了全新的知识库系统,AstrBot 将原生支持知识库功能。下文介绍的是新版知识库的使用方法。如果您使用的是之前的版本,请参考[旧版知识库使用文档](https://docs.astrbot.app/zh/use/knowledge-base-old), 我们建议您升级到最新版以获得更好的体验。 + +![知识库预览](https://files.astrbot.app/docs/zh/use/image-3.png) + +## 配置嵌入模型 + +打开服务提供商页面,点击新增服务提供商,选择 Embedding。 + +目前 AstrBot 支持兼容 OpenAI API 和 Gemini API 的嵌入向量服务。 + +点击上面的提供商卡片进入配置页面,填写配置。 + +配置完成后,点击保存。 + +## 配置重排序模型(可选) + +重排序模型可以一定程度上提高最终召回结果的精度。 + +和嵌入模型的配置类似,打开服务提供商页面,点击新增服务提供商,选择重排序。有关重排序模型的更多信息请参考网络。 + +## 创建知识库 + +AstrBot 支持多知识库管理。在聊天时,您可以**自由指定知识库**。 + +进入知识库页面,点击创建知识库,如下图所示: + +![image](https://files.astrbot.app/docs/source/images/knowledge-base/image.png) + +填写相关信息。在嵌入模型下拉菜单中您将看到刚刚创建好的嵌入模型和重排序模型(重排序模型可选)。 + +> [!TIP] +> 一旦选择了一个知识库的嵌入模型,请不要再修改该提供商的**模型**或者**向量维度信息**,否则将**严重影响**该知识库的召回率甚至**报错**。 + +## 上传文件 + +创建好知识库之后,可以为知识库上传文档。支持同时上传最多 10 个文件,单个文件大小不超过 128 MB。 + +![上传文件](https://files.astrbot.app/docs/zh/use/image-4.png) + +## 使用知识库 + +在配置文件中,可以为不同的配置文件指定不同的知识库。 + +## 附录 2:高性价比的嵌入模型申请 + +### PPIO + +1. 打开 [PPIO 派欧云官网](https://ppio.cn/user/register?invited_by=AIOONE),并注册账户(通过此链接注册的账户将会获得 15 元人民币的代金券)。 +2. 进入 [模型广场](https://ppio.cn/model-api/console),点击嵌入模型 +3. 点击 BAAI:BGE-M3 (截止至 2025-06-02,该模型在该平台免费)。 +4. 找到 API 接入指南,申请 Key。 +5. 填写 AstrBot OpenAI Embedding 模型提供商配置: + 1. API Key 为刚刚申请的 PPIO 的 API Key + 2. embedding api base 填写 `https://api.ppinfra.com/v3/openai` + 3. model 填写你选择的模型,此例子中为 `baai/bge-m3`。 diff --git a/docs/zh/use/mcp.md b/docs/zh/use/mcp.md new file mode 100644 index 000000000..79e3757fd --- /dev/null +++ b/docs/zh/use/mcp.md @@ -0,0 +1,101 @@ +# MCP + +MCP(Model Context Protocol,模型上下文协议) 是一种新的开放标准协议,用来在大模型和数据源之间建立安全双向的链接。简单来说,它将函数工具单独抽离出来作为一个独立的服务,AstrBot 通过 MCP 协议远程调用函数工具,函数工具返回结果给 AstrBot。 + +![image](https://files.astrbot.app/docs/source/images/function-calling/image3.png) + +AstrBot v3.5.0 支持 MCP 协议,可以添加多个 MCP 服务器、使用 MCP 服务器的函数工具。 + +![image](https://files.astrbot.app/docs/source/images/function-calling/image2.png) + +## 初始状态配置 + +MCP 服务器一般使用 `uv` 或者 `npm` 来启动,因此您需要安装这两个工具。 + +对于 `uv`,您可以直接通过 pip 来安装。可在 AstrBot WebUI 快捷安装: + +![image](https://files.astrbot.app/docs/zh/use/image.png) + +输入 `uv` 即可。 + +如果您使用 Docker 部署 AstrBot,也可以执行以下指令快捷安装。 + +```bash +docker exec astrbot python -m pip install uv +``` + +如果您通过源码部署 AstrBot,请在创建的虚拟环境内安装。 + +对于 `npm`,您需要安装 `node`。 + +如果您通过源码/一键安装部署 AstrBot,请参考 [Download Node.js](https://nodejs.org/en/download) 下载到您的本机。 + +如果您使用 Docker 部署 AstrBot,您需要在容器中安装 `node`(后期 AstrBot Docker 镜像将自带 `node`),请参考执行以下指令: + +```bash +sudo docker exec -it astrbot /bin/bash +apt update && apt install curl -y +export NVM_NODEJS_ORG_MIRROR=http://nodejs.org/dist +# Download and install nvm: +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash +\. "$HOME/.nvm/nvm.sh" +nvm install 22 +# Verify version: +node -v +nvm current +npm -v +npx -v +``` + +安装好 `node` 之后,需要重启 `AstrBot` 以应用新的环境变量。 + +## 安装 MCP 服务器 + +如果您使用 Docker 部署 AstrBot,请将 MCP 服务器安装在 data 目录下。 + +### 一个例子 + +我想安装一个查询 Arxiv 上论文的 MCP 服务器,发现了这个 Repo: [arxiv-mcp-server](https://github.com/blazickjp/arxiv-mcp-server),参考它的 README, + +我们抽取出需要的信息: + +```json +{ + "command": "uv", + "args": [ + "tool", + "run", + "arxiv-mcp-server", + "--storage-path", "data/arxiv" + ] +} +``` + +如果要使用的 MCP 服务器需要通过环境变量配置 Token 等信息,可以使用 `env` 这个工具: + +```json +{ + "command": "env", + "args": [ + "XXX_RESOURCE_FROM=local", + "XXX_API_URL=https://xxx.com", + "XXX_API_TOKEN=sk-xxxxx", + "uv", + "tool", + "run", + "xxx-mcp-server", + "--storage-path", "data/res" + ] +} +``` + +在 AstrBot WebUI 中设置: + +![image](https://files.astrbot.app/docs/zh/use/image-2.png) + +即可。 + +参考链接: + +1. 在这里了解如何使用 MCP: [Model Context Protocol](https://modelcontextprotocol.io/introduction) +2. 在这里获取常用的 MCP 服务器: [awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers/blob/main/README-zh.md#what-is-mcp), [Model Context Protocol servers](https://github.com/modelcontextprotocol/servers), [MCP.so](https://mcp.so) diff --git a/docs/zh/use/plugin.md b/docs/zh/use/plugin.md new file mode 100644 index 000000000..77f30eeba --- /dev/null +++ b/docs/zh/use/plugin.md @@ -0,0 +1,7 @@ +# AstrBot Star + +在 `3.4.0` 版本之后,AstrBot 将插件命名为 `Star`。AstrBot 是一个高度模块化的项目,通过插件可以发挥这种模块化的能力,实现各种功能。 + +使用 `/plugin` 可以看到所有插件。在管理面板中也可管理已经安装的插件。 + +如果想自己开发插件,详见 [几行代码实现一个插件](/dev/star/plugin)。 \ No newline at end of file diff --git a/docs/zh/use/proactive-agent.md b/docs/zh/use/proactive-agent.md new file mode 100644 index 000000000..61fc64b4f --- /dev/null +++ b/docs/zh/use/proactive-agent.md @@ -0,0 +1,53 @@ +# 主动型能力 + +AstrBot 引入了主动 Agent(Proactive Agent)系统,使 AstrBot 不仅能被动响应用户,还能通过给自己下达未来的任务来在未来的指定时刻主动执行任务并向用户主动反馈结果(文本、图片、文件都可)。 + +![](https://files.astrbot.app/docs/source/images/proactive-agent/image.png) + +在 v4.14.0 引入,目前是**实验性功能**,未稳定。 + +## 未来任务 (FutureTask) + +主 Agent 现在可以管理一个全局的 **Cron Job 列表**,为未来的自己设置任务。 + +### 功能特点 + +- **自我唤醒**:AstrBot 会在预定时间自动唤醒并执行任务。 +- **任务反馈**:执行完成后,AstrBot 会将结果告知任务布置方。 +- **WebUI 管理**:你可以在 WebUI 的“定时任务”页面查看、编辑或删除已设置的任务。 + +### 如何使用 + +> [!TIP] +> 首先,确保配置中 “主动型能力” 已启用。 + +主 Agent 拥有管理定时任务的能力。你可以直接对它说: +- “明天早上 8 点提醒我开会” +- “每周五下午 5 点总结本周的工作日志” +- “帮我定一个 10 分钟后的闹钟” + +主 Agent 会调用内置的定时任务工具来安排这些计划。 + +你可以在 AstrBot WebUI 左侧导航栏中点击 **未来任务** 来查看和管理所有未来任务。 + +![](https://files.astrbot.app/docs/source/images/proactive-agent/image-1.png) + +### 支持的平台 + +“定时任务”的设置支持所有平台,然而,由于部分平台没有开放主动消息推送的 API,因此只有以下平台支持 AstrBot 主动向用户推送结果: + +- Telegram +- OneBot v11 +- Slack +- 飞书 (Lark) +- Discord +- Misskey +- Satori + +## 多媒体消息的发送 + +为了方便 Agent 直接向用户发送图片、音频、视频等文件,AstrBot 默认提供了一个 `send_message_to_user` 工具。 + +### 功能特点 +- **直接发送**:Agent 可以直接将生成或获取的多媒体文件发送给用户,而无需通过复杂的文本转换。 +- **支持多种格式**:支持图片、文件、音频、视频等。 diff --git a/docs/zh/use/skills.md b/docs/zh/use/skills.md new file mode 100644 index 000000000..de7b7a97e --- /dev/null +++ b/docs/zh/use/skills.md @@ -0,0 +1,38 @@ +# Anthropic Skills + +Anthropic 推出的 Agent Skills(智能体技能)是一套模块化的功能扩展标准,旨在将 Claude 从一个“通用聊天机器人”转变为具备特定领域专业知识的“任务执行者”。Skills 是包含指令、脚本、元数据和参考资源的结构化文件夹。它不仅仅是提示词(Prompt),更像是一本专门的“操作手册”,在 Agent 需要执行特定任务时才会动态加载。Tool 是模型用来与外部世界交互的“具体工具/函数接口”,而 Skill 是将指令、模板和工具组合在一起的“标准化任务执行手册”。传统 Tool 需要在对话开始时一次性将所有 API 定义填入 Prompt。如果工具超过 50 个,可能还没开始说话就消耗了数万个 Token,导致响应变慢且昂贵。 + +AstrBot 在 v4.13.0 之后引入了对 Anthropic Skills 的支持,使得用户可以轻松集成和使用各种预定义的技能模块,提升 Agent 在特定任务上的表现。 + +## 关键特性 + +- 按需加载 (Progressive Disclosure):模型初始只加载技能名称和简短描述。只有当任务匹配时,才会加载详细的 SKILL.md 指令,从而节省上下文窗口并降低成本。 +- 高度可复用:技能可以在不同的 Claude API 项目、Claude Code 或 Claude.ai 中通用。 +- 执行能力:技能可以包含可执行代码脚本,配合 Anthropic 代码执行环境(Code Execution)直接生成或处理文件。 + +## 上传 Skills 到 AstrBot + +进入 AstrBot 管理面板,导航到 `插件` 页面,找到 `Skills`。 + +![Skills](https://files.astrbot.app/docs/source/images/skills/image.png) + +你可以上传 Skills,上传格式要求如下: + +1. 是一个 .zip 压缩包 +2. **解压后是一个 Skill 文件夹,Skill 文件夹的名字即为这个 Skill 在 AstrBot 中的标识,请用英文命名**。 +3. Skill 文件夹内必须包含一个名为 `SKILL.md` 的文件,且该文件内容最好符合 Anthropic Skills 规范。你可以参考 [Anthropic 技能](https://code.claude.com/docs/zh-CN/skills) + +## 在 AstrBot 使用 Skills + +Skills 提供了 Agent 操作说明书,并且内容通常包含 Python 代码段、脚本等可执行内容。因此,Agent 需要一个**执行环境**。 + +目前,AstrBot 提供两种执行环境: + +- Local(Agent 将在你的 AstrBot 运行环境中运行。**请谨慎使用,因为这会允许 Agent 在你的环境执行任意代码,可能带来安全风险**) +- Sandbox (Agent 在隔离化的沙盒环境中运行。**需要先启动 AstrBot 沙盒模式**,请参考:[沙盒模式](/use/astrbot-agent-sandbox),如果这个模式下不启动沙盒模式,将不会将 Skills 传给 Agent) + +你可以在 `配置` 页面 - 使用电脑能力 中选择默认的执行环境。 + +> [!NOTE] +> 需要说明的是,如果您使用 Local 作为执行环境,AstrBot 目前仅允许 **AstrBot 管理员**请求时才真正让 Agent 操作你的本地环境,普通用户将会被禁止,Agent 将无法通过 Shell、Python 等 Tool 在本地环境执行代码,会收到相应的权限限制提示,如 `Sorry, I cannot execute code on your local environment due to permission restrictions.`。 + diff --git a/docs/zh/use/subagent.md b/docs/zh/use/subagent.md new file mode 100644 index 000000000..5c2a20d72 --- /dev/null +++ b/docs/zh/use/subagent.md @@ -0,0 +1,56 @@ +# Agent Handsoff 与 Subagent + +SubAgent 编排是 AstrBot 提供的一种高级 Agent 组织方式。它允许你将复杂的任务分解给多个专门的子 Agent(SubAgent)来完成,从而降低主 Agent 的 Prompt 长度,提高任务执行的成功率。 + +在 v4.14.0 引入,目前是**实验性功能**,未稳定。 + +![](https://files.astrbot.app/docs/source/images/subagent/image.png) + +## 动机 + +在传统的架构中,所有的工具(Tools)都直接挂载在主 Agent 上。当工具数量较多时,会带来以下问题: +1. **Prompt 爆炸**:主 Agent 需要在 System Prompt 中包含所有工具的描述,导致上下文占用过多。 +2. **调用失误**:面对大量工具,LLM 容易混淆工具用途或产生错误的调用参数。 +3. **逻辑复杂**:主 Agent 既要负责对话,又要负责组织和调用大量工具,负担过重。 + +通过 SubAgent 编排,主 Agent 仅负责与用户对话以及**任务委派**。具体的工具调用由专门的 SubAgent 负责。 + +## 工作原理 + +1. **主 Agent 委派**:开启 SubAgent 模式后,主 Agent 只能看到一系列名为 `transfer_to_` 的委派工具。 +2. **任务移交**:当主 Agent 认为需要执行某项任务时,它会调用对应的委派工具,将任务描述传递给 SubAgent。 +3. **子 Agent 执行**:SubAgent 接收到任务后,使用其挂载的工具进行操作,并将结果整理后回传给主 Agent。 +4. **结果反馈**:主 Agent 收到 SubAgent 的执行结果,继续与用户对话。 + +![](https://files.astrbot.app/docs/source/images/subagent/1.png) + +## 配置方法 + +在 AstrBot WebUI 中,点击左侧导航栏的 **SubAgent 编排**。 + +### 1. 启用 SubAgent 模式 + +在页面顶部开启“启用 SubAgent 编排”。 + +### 2. 创建 SubAgent + +点击“新增 SubAgent”按钮: + +- **Agent 名称**:用于生成委派工具名(如 `transfer_to_weather`)。建议使用英文小写和下划线。 +- **选择 Persona**:选择一个预设的 Persona,即人格,作为该子 Agent 的基础性格、行为指导和可以使用的 Tools 集合。你可以在“人格设定”页面创建和管理 Persona。 +- **对主 LLM 的描述**:这段描述会告诉主 Agent 这个子 Agent 擅长做什么,以便主 Agent 准确委派。 +- **分配工具**:选择该子 Agent 可以调用的工具。 +- **Provider 覆盖(可选)**:你可以为特定的子 Agent 指定不同的模型提供商。例如,主 Agent 使用 GPT-4o,而负责简单查询的子 Agent 使用 GPT-4o-mini 以节省成本。 + +## 最佳实践 + +- **职责单一**:每个 SubAgent 应该只负责一类相关的任务(如:搜索、文件处理、智能家居控制)。 +- **清晰的描述**:给主 Agent 的描述应当简洁明了,突出该子 Agent 的核心能力。 +- **分层管理**:对于极其复杂的任务,可以考虑多级委派(如果需要)。 + +## 已知问题 + +SubAgent 系统目前是**实验性功能**,未稳定。 + +1. 目前无法隔离人格的 Skills。 +2. 子 Agent 的对话历史暂时不会被保存。 diff --git a/docs/zh/use/unified-webhook.md b/docs/zh/use/unified-webhook.md new file mode 100644 index 000000000..cbfdd30a9 --- /dev/null +++ b/docs/zh/use/unified-webhook.md @@ -0,0 +1,32 @@ +# 统一 Webhook 模式 + +在 v4.8.0 版本开始,AstrBot 支持统一 Webhook 模式 (unified_webhook_mode)。开启该模式后,所有支持该模式的平台适配器都将使用同一个 Webhook 回调接口,从而简化了反向代理和域名配置,不再需要给每一个机器人适配器单独配置端口、域名和反向代理。 + +支持统一 Webhook 模式的平台适配器包括: + +- Slack Webhook 模式 +- 微信公众平台 +- 企业微信客服机器人 +- 企业微信智能机器人 +- 微信客服机器人 +- QQ 官方机器人 Webhook 模式 +- ... + +## 如何使用统一 Webhook 模式 + +1. 拥有一个域名(如 example.com)和公网 IP 服务器 +2. 配置 DNS 解析(如 astrbot.example.com) +3. 配置反向代理,将域名的 80 或 443 端口请求转发到 AstrBot 的 WebUI 端口(默认为 6185) +4. 前往 AstrBot `配置文件` 页,点击 `系统`,将 `对外可达的回调接口地址` 为配置的 URL 地址。(如 https://astrbot.example.com),点击保存,等待重启。 + + +在之后配置各个平台适配器时,选择开启 `统一 Webhook 模式 (unified_webhook_mode)`。 + +> [!TIP] +> 如果您正在尝试更新 v4.8.0 之前配置的机器人适配器,你可能无法看到 `统一 Webhook 模式 (unified_webhook_mode)` 选项。请重新创建一个新的适配器实例,即可看到该选项。 + +![unified_webhook](https://files.astrbot.app/docs/source/images/use/unified-webhook-config.png) + +开启该模式后,AstrBot 会为你生成一个唯一的 Webhook 回调链接,你只需要将该链接填写到各个平台的回调地址处即可。 + +![unified_webhook](https://files.astrbot.app/docs/source/images/use/unified-webhook.png) diff --git a/docs/zh/use/websearch.md b/docs/zh/use/websearch.md new file mode 100644 index 000000000..93200c44b --- /dev/null +++ b/docs/zh/use/websearch.md @@ -0,0 +1,34 @@ +# 网页搜索 + +网页搜索功能旨在提供大模型调用 Google,Bing,搜狗等搜索引擎以获取世界最近信息的能力,一定程度上能够提高大模型的回复准确度,减少幻觉。 + +AstrBot 内置的网页搜索功能依赖大模型提供 `函数调用` 能力。如果你不了解函数调用,请参考:[函数调用](/use/websearch)。 + +在使用支持函数调用的大模型且开启了网页搜索功能的情况下,您可以试着说: + +- `帮我搜索一下 xxx` +- `帮我总结一下这个链接:https://soulter.top` +- `查一下 xxx` +- `最近 xxxx` + +等等带有搜索意味的提示让大模型触发调用搜索工具。 + +AstrBot 支持 3 种网页搜索源接入方式:`默认`、`Tavily`、`百度 AI 搜索`。 + +前者使用 AstrBot 内置的网页搜索请求器请求 Google、Bing、搜狗搜索引擎,在能够使用 Google 的网络环境下表现最佳。**我们推荐使用 Tavily**。 + +![image](https://files.astrbot.app/docs/source/images/websearch/image.png) + +进入 `配置`,下拉找到网页搜索,您可选择 `default`(默认,不推荐) 或 `Tavily`。 + +### default(不推荐) + +如果您的设备在国内并且有代理,可以开启代理并在 `管理面板-其他配置-HTTP代理` 填入 HTTP 代理地址以应用代理。 + +### Tavily + +前往 [Tavily](https://app.tavily.com/home) 得到 API Key,然后填写在相应的配置项。 + +如果您使用 Tavily 作为网页搜索源,在 AstrBot ChatUI 上将会获得更好的体验优化,包括引用来源展示等: + +![](https://files.astrbot.app/docs/source/images/websearch/image1.png) \ No newline at end of file diff --git a/docs/zh/use/webui.md b/docs/zh/use/webui.md new file mode 100644 index 000000000..f52f4a3ff --- /dev/null +++ b/docs/zh/use/webui.md @@ -0,0 +1,79 @@ +# 管理面板 + +AstrBot 管理面板具有管理插件、查看日志、可视化配置、查看统计信息等功能。 + +![image](https://files.astrbot.app/docs/source/images/webui/image-4.png) + +## 管理面板的访问 + +当启动 AstrBot 之后,你可以通过浏览器访问 `http://localhost:6185` 来访问管理面板。 + +> [!TIP] +> - 如果你正在云服务器上部署 AstrBot,需要将 `localhost` 替换为你的服务器 IP 地址。 + +## 登录 + +默认用户名和密码是 `astrbot` 和 `astrbot`。 + +## 可视化配置 + +在管理面板中,你可以通过可视化配置来配置 AstrBot 的插件。点击左栏 `配置` 即可进入配置页面。 + +![image](https://files.astrbot.app/docs/source/images/webui/image-3.png) + +当修改完配置后,你需要点击右下角 `保存` 按钮才能成功保存配置。 + +使用右下角第一个圆形按钮可以切换至 `代码编辑配置`。在 `代码编辑配置` 中,你可以直接编辑配置文件。 + +编辑完后首先点击`应用此配置`,此时配置将应用到可视化配置中,然后再点击右下角`保存`按钮来保存配置。如果你不点击`应用此配置`,那么你的修改将不会生效。 + +![alt text](https://files.astrbot.app/docs/source/images/webui/image-5.png) + +## 插件 + +在管理面板中,你可以通过左栏的 `插件` 来查看已安装的插件,以及安装新插件。 + +点击插件市场标签栏,你可以浏览由 AstrBot 官方上架的插件。 + +![image](https://files.astrbot.app/docs/source/images/webui/image-1.png) + +你也可以点击右下角 + 按钮,以 URL / 文件上传的方式手动安装插件。 + +> 由于插件更新机制,AstrBot Team 无法完全保证插件市场中插件的安全性,请您仔细甄别。因为插件原因造成损失的,AstrBot Team 不予负责。 + +### 插件加载失败处理 + +如果插件加载失败,管理面板会显示错误信息,并提供 **“尝试一键重载修复”** 按钮。这允许你在修复环境(如安装缺失依赖)或修改代码后,无需重启整个程序即可快速重新加载插件。 + +## 指令管理 + +通过左侧菜单 `指令管理`,可以集中管理所有已注册的指令,默认不显示系统插件。 + +支持按插件、类型(指令 / 指令组 / 子指令)、权限与状态过滤,配合搜索框快速定位。指令组行可展开查看子指令,徽章显示子指令数量,子指令行会缩进区分层级。 + +可以对每个指令 启用/禁用、重命名。 + +## 追踪 (Trace) + +在管理面板的 `Trace` 页面中,你可以实时查看 AstrBot 的运行追踪记录。这对于调试模型调用路径、工具调用过程等非常有用。 + +你可以通过页面顶部的开关来启用或禁用追踪记录。 + +> [!NOTE] +> 当前仅记录部分 AstrBot 主 Agent 的模型调用路径,后续会不断完善。 + +## 更新管理面板 + +在 AstrBot 启动时,会自动检查管理面板是否需要更新,如果需要,第一条日志(黄色)会进行提示。 + +使用 `/dashboard_update` 命令可以手动更新管理面板(管理员指令)。 + +管理面板文件在 data/dist 目录下。如果需要手动替换,请在 https://github.com/AstrBotDevs/AstrBot/releases/ 下载 `dist.zip` 然后解压到 data 目录下。 + +## 自定义 WebUI 端口 + +修改 data/cmd_config.json 文件内 `dashboard` 配置中的 `port`。 + +## 忘记密码 + +修改 data/cmd_config.json 文件内 `dashboard` 配置中的 `password`,将 password 整个键值对删除。 diff --git a/docs/zh/what-is-astrbot.md b/docs/zh/what-is-astrbot.md new file mode 100644 index 000000000..349bbbfb3 --- /dev/null +++ b/docs/zh/what-is-astrbot.md @@ -0,0 +1,37 @@ +--- +outline: deep +--- + +# 👋 I'm AstrBot + +## 简介 + +AstrBot 是一个开源的一站式 Agentic 个人和群聊助手,可在 QQ、Telegram、企业微信、飞书、钉钉、Slack、等数十款主流即时通讯软件上部署,此外还内置类似 OpenWebUI 的轻量化 ChatUI,为个人、开发者和团队打造可靠、可扩展的对话式智能基础设施。无论是个人 AI 伙伴、智能客服、自动化助手,还是企业知识库,AstrBot 都能在你的即时通讯软件平台的工作流中快速构建 AI 应用。 + +## 文档概览 + +本文档分为以下几个部分: + +- **部署**。我们提供多种方式帮助您把 AstrBot 快速部署到云服务器或本地机器上。 +- **消息平台接入**。我们提供 18+ 主流即时通讯软件的接入指南,帮助您把 AstrBot 连接到您喜欢的 IM 平台。 +- **AI 模型提供商接入**。我们支持各种 AI 模型提供商的接入,您可以选择使用 AstrBot 内置的 Agent 执行器,也可以接入第三方的 Agent 执行器服务,例如 Dify、Coze、阿里云百炼应用、DeerFlow 等,或者自己开发 Agent 执行器。 +- **使用指南**。我们提供了丰富的使用指南,帮助您充分利用 AstrBot 的各种功能,例如插件、工具调用、知识库、MCP、Skills、Agent 沙箱环境等。 + +## 快速开始 + +> 您也可以使用 [☁️ 雨云部署](/deploy/astrbot/rainyun) 来一键部署 AstrBot,无需自行配置。 + +- 部署 AstrBot:阅读部署指南,快速在本地机器或云服务器上部署 AstrBot。 +- 连接 IM 平台:按照说明将 AstrBot 连接到您喜欢的 IM 平台,如 Discord、Telegram、Slack 等。 +- 配置 AI 模型:AstrBot 支持各种 AI 模型。请参阅 [连接模型服务](/config/providers/start) + +## 它是如何实现的? + +下面的拓扑图基本简述了 AstrBot 的架构。 + +![Architecture](https://files.astrbot.app/docs/source/images/what-is-astrbot/image.png) + +## 说明 + +- AstrBot 是一个非盈利项目,由全世界热心开源贡献者维护,并受 [AGPL-v3](https://www.chinasona.org/gnu/agpl-3.0-cn.html) 开源许可证保护。如果您对 AstrBot 进行了修改并将其用于提供具有商业盈利性质的网络服务,您必须开源所做的修改。详细联系 [community@astrbot.app](mailto:community@astrbot.app)。 +- 使用此项目前,请务必阅读本项目的最终用户许可协议(EULA):[最终用户许可协议](https://github.com/AstrBotDevs/AstrBot/blob/master/EULA.md)。如果您不同意该协议的任何条款,请勿使用本项目。