Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8028e9e9a6 | |||
| 817f20ea01 | |||
| ad5579a2f4 | |||
| 81a689a79b | |||
| 1893dd8336 | |||
| 021ca8175b | |||
| 39d6207fe1 | |||
| 23ce687229 | |||
| 3715312fd2 | |||
| 8196922cac | |||
| 8089ad91da | |||
| 2930cc3fd8 | |||
| 0e841a8b25 |
+3
-1
@@ -1,6 +1,7 @@
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
# github actions
|
||||
.git
|
||||
.github/
|
||||
.*ignore
|
||||
# User-specific stuff
|
||||
@@ -19,4 +20,5 @@ data/
|
||||
changelogs/
|
||||
tests/
|
||||
.ruff_cache/
|
||||
.astrbot
|
||||
.astrbot
|
||||
astrbot.lock
|
||||
+8
-8
@@ -18,15 +18,15 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - && \
|
||||
apt-get install -y --no-install-recommends nodejs && \
|
||||
echo "3.11" > .python-version && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
RUN apt-get update && apt-get install -y curl gnupg \
|
||||
&& curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - \
|
||||
&& apt-get install -y nodejs
|
||||
|
||||
RUN python -m pip install --no-cache-dir uv && \
|
||||
uv pip install socksio pilk --no-cache-dir --system
|
||||
RUN python -m pip install uv \
|
||||
&& echo "3.11" > .python-version
|
||||
RUN uv pip install -r requirements.txt --no-cache-dir --system
|
||||
RUN uv pip install socksio uv pilk --no-cache-dir --system
|
||||
|
||||
EXPOSE 6185
|
||||
EXPOSE 6186
|
||||
|
||||
CMD ["uv", "run", "main.py"]
|
||||
CMD ["python", "main.py"]
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /AstrBot
|
||||
|
||||
COPY . /AstrBot/
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
build-essential \
|
||||
python3-dev \
|
||||
libffi-dev \
|
||||
libssl-dev \
|
||||
curl \
|
||||
unzip \
|
||||
ca-certificates \
|
||||
bash \
|
||||
git \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
ENV NVM_DIR="/root/.nvm" \
|
||||
NODE_VERSION=22
|
||||
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash && \
|
||||
. "$NVM_DIR/nvm.sh" && \
|
||||
nvm install $NODE_VERSION && \
|
||||
nvm use $NODE_VERSION && \
|
||||
nvm alias default $NODE_VERSION && \
|
||||
node -v && npm -v && \
|
||||
echo "3.11" > .python-version
|
||||
ENV PATH="$NVM_DIR/versions/node/v$(node -v | cut -d 'v' -f 2)/bin:$PATH"
|
||||
|
||||
RUN python -m pip install --no-cache-dir uv
|
||||
|
||||
# 安装项目依赖(根据指南,使用 uv sync)
|
||||
RUN uv sync --no-cache
|
||||
|
||||
EXPOSE 6185
|
||||
EXPOSE 6186
|
||||
|
||||
CMD ["uv", "run", "main.py"]
|
||||
@@ -119,83 +119,73 @@ uv run main.py
|
||||
|
||||
<a href="https://discord.gg/hAVk6tgV36"><img alt="Discord_community" src="https://img.shields.io/badge/Discord-AstrBot-purple?style=for-the-badge&color=76bad9"></a>
|
||||
|
||||
## ⚡ 消息平台支持情况
|
||||
## 支持的消息平台
|
||||
|
||||
**官方维护**
|
||||
|
||||
| 平台 | 支持性 |
|
||||
| -------- | ------- |
|
||||
| QQ(官方平台) | ✔ |
|
||||
| QQ(OneBot) | ✔ |
|
||||
| Telegram | ✔ |
|
||||
| 企微应用 | ✔ |
|
||||
| 企微智能机器人 | ✔ |
|
||||
| 微信客服 | ✔ |
|
||||
| 微信公众号 | ✔ |
|
||||
| 飞书 | ✔ |
|
||||
| 钉钉 | ✔ |
|
||||
| Slack | ✔ |
|
||||
| Discord | ✔ |
|
||||
| Satori | ✔ |
|
||||
| Misskey | ✔ |
|
||||
| Whatsapp | 将支持 |
|
||||
| LINE | 将支持 |
|
||||
- QQ (官方平台 & OneBot)
|
||||
- Telegram
|
||||
- 企微应用 & 企微智能机器人
|
||||
- 微信客服 & 微信公众号
|
||||
- 飞书
|
||||
- 钉钉
|
||||
- Slack
|
||||
- Discord
|
||||
- Satori
|
||||
- Misskey
|
||||
- Whatsapp (将支持)
|
||||
- LINE (将支持)
|
||||
|
||||
**社区维护**
|
||||
|
||||
| 平台 | 支持性 |
|
||||
| -------- | ------- |
|
||||
| [KOOK](https://github.com/wuyan1003/astrbot_plugin_kook_adapter) | ✔ |
|
||||
| [VoceChat](https://github.com/HikariFroya/astrbot_plugin_vocechat) | ✔ |
|
||||
| [Bilibili 私信](https://github.com/Hina-Chat/astrbot_plugin_bilibili_adapter) | ✔ |
|
||||
| [wxauto](https://github.com/luosheng520qaq/wxauto-repost-onebotv11) | ✔ |
|
||||
- [KOOK](https://github.com/wuyan1003/astrbot_plugin_kook_adapter)
|
||||
- [VoceChat](https://github.com/HikariFroya/astrbot_plugin_vocechat)
|
||||
- [Bilibili 私信](https://github.com/Hina-Chat/astrbot_plugin_bilibili_adapter)
|
||||
- [wxauto](https://github.com/luosheng520qaq/wxauto-repost-onebotv11)
|
||||
|
||||
## ⚡ 提供商支持情况
|
||||
## 支持的模型服务
|
||||
|
||||
**大模型服务**
|
||||
|
||||
| 名称 | 支持性 | 备注 |
|
||||
| -------- | ------- | ------- |
|
||||
| OpenAI | ✔ | 支持任何兼容 OpenAI API 的服务 |
|
||||
| Anthropic | ✔ | |
|
||||
| Google Gemini | ✔ | |
|
||||
| Moonshot AI | ✔ | |
|
||||
| 智谱 AI | ✔ | |
|
||||
| DeepSeek | ✔ | |
|
||||
| Ollama | ✔ | 本地部署 DeepSeek 等开源语言模型 |
|
||||
| LM Studio | ✔ | 本地部署 DeepSeek 等开源语言模型 |
|
||||
| [优云智算](https://www.compshare.cn/?ytag=GPU_YY-gh_astrbot&referral_code=FV7DcGowN4hB5UuXKgpE74) | ✔ | |
|
||||
| [302.AI](https://share.302.ai/rr1M3l) | ✔ | |
|
||||
| [小马算力](https://www.tokenpony.cn/3YPyf) | ✔ | |
|
||||
| 硅基流动 | ✔ | |
|
||||
| PPIO 派欧云 | ✔ | |
|
||||
| ModelScope | ✔ | |
|
||||
| OneAPI | ✔ | |
|
||||
| Dify | ✔ | |
|
||||
| 阿里云百炼应用 | ✔ | |
|
||||
| Coze | ✔ | |
|
||||
- OpenAI 及兼容服务
|
||||
- Anthropic
|
||||
- Google Gemini
|
||||
- Moonshot AI
|
||||
- 智谱 AI
|
||||
- DeepSeek
|
||||
- Ollama (本地部署)
|
||||
- LM Studio (本地部署)
|
||||
- [优云智算](https://www.compshare.cn/?ytag=GPU_YY-gh_astrbot&referral_code=FV7DcGowN4hB5UuXKgpE74)
|
||||
- [302.AI](https://share.302.ai/rr1M3l)
|
||||
- [小马算力](https://www.tokenpony.cn/3YPyf)
|
||||
- [硅基流动](https://docs.siliconflow.cn/cn/usercases/use-siliconcloud-in-astrbot)
|
||||
- [PPIO 派欧云](https://ppio.com/user/register?invited_by=AIOONE)
|
||||
- ModelScope
|
||||
- OneAPI
|
||||
|
||||
**LLMOps 平台**
|
||||
|
||||
- Dify
|
||||
- 阿里云百炼应用
|
||||
- Coze
|
||||
|
||||
**语音转文本服务**
|
||||
|
||||
| 名称 | 支持性 | 备注 |
|
||||
| -------- | ------- | ------- |
|
||||
| Whisper | ✔ | 支持 API、本地部署 |
|
||||
| SenseVoice | ✔ | 本地部署 |
|
||||
- OpenAI Whisper
|
||||
- SenseVoice
|
||||
|
||||
**文本转语音服务**
|
||||
|
||||
| 名称 | 支持性 | 备注 |
|
||||
| -------- | ------- | ------- |
|
||||
| OpenAI TTS | ✔ | |
|
||||
| Gemini TTS | ✔ | |
|
||||
| GSVI | ✔ | GPT-Sovits-Inference |
|
||||
| GPT-SoVITs | ✔ | GPT-Sovits |
|
||||
| FishAudio | ✔ | |
|
||||
| Edge TTS | ✔ | Edge 浏览器的免费 TTS |
|
||||
| 阿里云百炼 TTS | ✔ | |
|
||||
| Azure TTS | ✔ | |
|
||||
| Minimax TTS | ✔ | |
|
||||
| 火山引擎 TTS | ✔ | |
|
||||
- OpenAI TTS
|
||||
- Gemini TTS
|
||||
- GPT-Sovits-Inference
|
||||
- GPT-Sovits
|
||||
- FishAudio
|
||||
- Edge TTS
|
||||
- 阿里云百炼 TTS
|
||||
- Azure TTS
|
||||
- Minimax TTS
|
||||
- 火山引擎 TTS
|
||||
|
||||
## ❤️ 贡献
|
||||
|
||||
@@ -229,7 +219,7 @@ pre-commit install
|
||||
|
||||
## ⭐ Star History
|
||||
|
||||
> [!TIP]
|
||||
> [!TIP]
|
||||
> 如果本项目对您的生活 / 工作产生了帮助,或者您关注本项目的未来发展,请给项目 Star,这是我们维护这个开源项目的动力 <3
|
||||
|
||||
<div align="center">
|
||||
|
||||
@@ -4,7 +4,7 @@ import os
|
||||
|
||||
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
||||
|
||||
VERSION = "4.5.2"
|
||||
VERSION = "4.5.6"
|
||||
DB_PATH = os.path.join(get_astrbot_data_path(), "data_v4.db")
|
||||
|
||||
# 默认配置
|
||||
|
||||
@@ -296,7 +296,15 @@ class ToolsRoute(Route):
|
||||
"""获取所有注册的工具列表"""
|
||||
try:
|
||||
tools = self.tool_mgr.func_list
|
||||
tools_dict = [tool.__dict__() for tool in tools]
|
||||
tools_dict = [
|
||||
{
|
||||
"name": tool.name,
|
||||
"description": tool.description,
|
||||
"parameters": tool.parameters,
|
||||
"active": tool.active,
|
||||
}
|
||||
for tool in tools
|
||||
]
|
||||
return Response().ok(data=tools_dict).__dict__
|
||||
except Exception as e:
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
## What's Changed
|
||||
|
||||
> hotfix version of 4.5.2
|
||||
|
||||
1. 修复:修正 `get_tool_list` 方法中工具字典推导式的错误导致的 WebUI MCP 页面工具列表无法显示的问题。
|
||||
@@ -0,0 +1,5 @@
|
||||
## What's Changed
|
||||
|
||||
1. 修复:Docker 镜像部分依赖问题导致某些情况下无法启动容器的问题;
|
||||
2. 优化:插件卡片样式
|
||||
3. 修复:部分情况下 Windows 一键启动部署时,更新 / 部署失败的问题;
|
||||
@@ -0,0 +1,3 @@
|
||||
## What's Changed
|
||||
|
||||
1. 修复:部署失败
|
||||
@@ -0,0 +1,3 @@
|
||||
## What's Changed
|
||||
|
||||
1. 修复:构建失败
|
||||
@@ -79,6 +79,14 @@
|
||||
"devDocs": "Extension Development Docs",
|
||||
"submitRepo": "Submit Extension Repository"
|
||||
},
|
||||
"sort": {
|
||||
"default": "Default",
|
||||
"stars": "Stars",
|
||||
"author": "Author",
|
||||
"updated": "Last Updated",
|
||||
"ascending": "Ascending",
|
||||
"descending": "Descending"
|
||||
},
|
||||
"tags": {
|
||||
"danger": "Danger"
|
||||
},
|
||||
|
||||
@@ -79,6 +79,14 @@
|
||||
"devDocs": "插件开发文档",
|
||||
"submitRepo": "提交插件仓库"
|
||||
},
|
||||
"sort": {
|
||||
"default": "默认排序",
|
||||
"stars": "Star数",
|
||||
"author": "作者名",
|
||||
"updated": "更新时间",
|
||||
"ascending": "升序",
|
||||
"descending": "降序"
|
||||
},
|
||||
"tags": {
|
||||
"danger": "危险"
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@ import { pinyin } from 'pinyin-pro';
|
||||
import { useCommonStore } from '@/stores/common';
|
||||
import { useI18n, useModuleI18n } from '@/i18n/composables';
|
||||
|
||||
import { ref, computed, onMounted, reactive, inject } from 'vue';
|
||||
import { ref, computed, onMounted, reactive, inject, watch } from 'vue';
|
||||
|
||||
|
||||
const commonStore = useCommonStore();
|
||||
@@ -53,6 +53,10 @@ const isListView = ref(false);
|
||||
const pluginSearch = ref("");
|
||||
const loading_ = ref(false);
|
||||
|
||||
// 分页相关
|
||||
const currentPage = ref(1);
|
||||
const itemsPerPage = ref(6); // 每页显示6个卡片 (2行 x 3列,避免滚动)
|
||||
|
||||
// 危险插件确认对话框
|
||||
const dangerConfirmDialog = ref(false);
|
||||
const selectedDangerPlugin = ref(null);
|
||||
@@ -68,8 +72,11 @@ const upload_file = ref(null);
|
||||
const uploadTab = ref('file');
|
||||
const showPluginFullName = ref(false);
|
||||
const marketSearch = ref("");
|
||||
const debouncedMarketSearch = ref("");
|
||||
const filterKeys = ['name', 'desc', 'author'];
|
||||
const refreshingMarket = ref(false);
|
||||
const sortBy = ref('default'); // default, stars, author, updated
|
||||
const sortOrder = ref('desc'); // desc (降序) or asc (升序)
|
||||
|
||||
// 插件市场拼音搜索
|
||||
const normalizeStr = (s) => (s ?? '').toString().toLowerCase().trim();
|
||||
@@ -153,6 +160,71 @@ const pinnedPlugins = computed(() => {
|
||||
return pluginMarketData.value.filter(plugin => plugin?.pinned);
|
||||
});
|
||||
|
||||
// 过滤后的插件市场数据(带搜索)
|
||||
const filteredMarketPlugins = computed(() => {
|
||||
if (!debouncedMarketSearch.value) {
|
||||
return pluginMarketData.value;
|
||||
}
|
||||
|
||||
const search = debouncedMarketSearch.value.toLowerCase();
|
||||
return pluginMarketData.value.filter(plugin => {
|
||||
// 使用自定义过滤器
|
||||
return marketCustomFilter(plugin.name, search, plugin) ||
|
||||
marketCustomFilter(plugin.desc, search, plugin) ||
|
||||
marketCustomFilter(plugin.author, search, plugin);
|
||||
});
|
||||
});
|
||||
|
||||
// 所有插件列表,推荐插件排在前面
|
||||
const sortedPlugins = computed(() => {
|
||||
let plugins = [...filteredMarketPlugins.value];
|
||||
|
||||
// 根据排序选项排序
|
||||
if (sortBy.value === 'stars') {
|
||||
// 按 star 数排序
|
||||
plugins.sort((a, b) => {
|
||||
const starsA = a.stars ?? 0;
|
||||
const starsB = b.stars ?? 0;
|
||||
return sortOrder.value === 'desc' ? starsB - starsA : starsA - starsB;
|
||||
});
|
||||
} else if (sortBy.value === 'author') {
|
||||
// 按作者名字典序排序
|
||||
plugins.sort((a, b) => {
|
||||
const authorA = (a.author ?? '').toLowerCase();
|
||||
const authorB = (b.author ?? '').toLowerCase();
|
||||
const result = authorA.localeCompare(authorB);
|
||||
return sortOrder.value === 'desc' ? -result : result;
|
||||
});
|
||||
} else if (sortBy.value === 'updated') {
|
||||
// 按更新时间排序
|
||||
plugins.sort((a, b) => {
|
||||
const dateA = a.updated_at ? new Date(a.updated_at).getTime() : 0;
|
||||
const dateB = b.updated_at ? new Date(b.updated_at).getTime() : 0;
|
||||
return sortOrder.value === 'desc' ? dateB - dateA : dateA - dateB;
|
||||
});
|
||||
} else {
|
||||
// default: 推荐插件排在前面
|
||||
const pinned = plugins.filter(plugin => plugin?.pinned);
|
||||
const notPinned = plugins.filter(plugin => !plugin?.pinned);
|
||||
return [...pinned, ...notPinned];
|
||||
}
|
||||
|
||||
return plugins;
|
||||
});
|
||||
|
||||
// 分页计算属性
|
||||
const displayItemsPerPage = 9; // 固定每页显示6个卡片(2行)
|
||||
|
||||
const totalPages = computed(() => {
|
||||
return Math.ceil(sortedPlugins.value.length / displayItemsPerPage);
|
||||
});
|
||||
|
||||
const paginatedPlugins = computed(() => {
|
||||
const start = (currentPage.value - 1) * displayItemsPerPage;
|
||||
const end = start + displayItemsPerPage;
|
||||
return sortedPlugins.value.slice(start, end);
|
||||
});
|
||||
|
||||
// 方法
|
||||
const toggleShowReserved = () => {
|
||||
showReserved.value = !showReserved.value;
|
||||
@@ -534,6 +606,7 @@ const refreshPluginMarket = async () => {
|
||||
trimExtensionName();
|
||||
checkAlreadyInstalled();
|
||||
checkUpdate();
|
||||
currentPage.value = 1; // 重置到第一页
|
||||
|
||||
toast(tm('messages.refreshSuccess'), "success");
|
||||
} catch (err) {
|
||||
@@ -575,6 +648,20 @@ onMounted(async () => {
|
||||
}
|
||||
});
|
||||
|
||||
// 搜索防抖处理
|
||||
let searchDebounceTimer = null;
|
||||
watch(marketSearch, (newVal) => {
|
||||
if (searchDebounceTimer) {
|
||||
clearTimeout(searchDebounceTimer);
|
||||
}
|
||||
|
||||
searchDebounceTimer = setTimeout(() => {
|
||||
debouncedMarketSearch.value = newVal;
|
||||
// 搜索时重置到第一页
|
||||
currentPage.value = 1;
|
||||
}, 300); // 300ms 防抖延迟
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
@@ -786,10 +873,12 @@ onMounted(async () => {
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" md="6" lg="4" v-for="extension in filteredPlugins" :key="extension.name"
|
||||
<v-col cols="12" md="6" lg="4" v-for="extension in filteredPlugins" :key="extension.name"
|
||||
class="pb-2">
|
||||
<ExtensionCard :extension="extension" class="rounded-lg" style="background-color: rgb(var(--v-theme-mcpCardBg));"
|
||||
@configure="openExtensionConfig(extension.name)" @uninstall="(ext, options) => uninstallExtension(ext.name, options)"
|
||||
<ExtensionCard :extension="extension" class="rounded-lg"
|
||||
style="background-color: rgb(var(--v-theme-mcpCardBg));"
|
||||
@configure="openExtensionConfig(extension.name)"
|
||||
@uninstall="(ext, options) => uninstallExtension(ext.name, options)"
|
||||
@update="updateExtension(extension.name)" @reload="reloadPlugin(extension.name)"
|
||||
@toggle-activation="extension.activated ? pluginOff(extension) : pluginOn(extension)"
|
||||
@view-handlers="showPluginInfo(extension)" @view-readme="viewReadme(extension)">
|
||||
@@ -809,85 +898,158 @@ onMounted(async () => {
|
||||
@click="dialog = true" color="darkprimary">
|
||||
</v-btn>
|
||||
|
||||
<div v-if="pinnedPlugins.length > 0" class="mt-4">
|
||||
<h2>{{ tm('market.recommended') }}</h2>
|
||||
<v-row style="margin-top: 8px;">
|
||||
<v-col cols="12" md="6" lg="6" v-for="plugin in pinnedPlugins" :key="plugin.name">
|
||||
<ExtensionCard :extension="plugin" class="h-120 rounded-lg" market-mode="true" :highlight="true"
|
||||
@install="handleInstallPlugin(plugin)" @view-readme="open(plugin.repo)">
|
||||
</ExtensionCard>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<div class="d-flex align-center mb-2" style="justify-content: space-between;">
|
||||
<h2>{{ tm('market.allPlugins') }}</h2>
|
||||
<div class="d-flex align-center">
|
||||
<v-btn variant="tonal" size="small" @click="refreshPluginMarket" :loading="refreshingMarket"
|
||||
class="mr-2">
|
||||
<div class="d-flex align-center mb-2" style="justify-content: space-between; flex-wrap: wrap; gap: 8px;">
|
||||
<div class="d-flex align-center" style="gap: 6px;">
|
||||
<h2>{{ tm('market.allPlugins') }}({{ filteredMarketPlugins.length }})</h2>
|
||||
<v-btn icon variant="text" @click="refreshPluginMarket" :loading="refreshingMarket">
|
||||
<v-icon>mdi-refresh</v-icon>
|
||||
{{ tm('buttons.refresh') }}
|
||||
</v-btn>
|
||||
<v-switch v-model="showPluginFullName" :label="tm('market.showFullName')" hide-details
|
||||
density="compact" style="margin-left: 12px" />
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-center" style="gap: 8px; flex-wrap: wrap;">
|
||||
<v-pagination v-model="currentPage" :length="totalPages" :total-visible="5" size="small"
|
||||
density="comfortable"></v-pagination>
|
||||
|
||||
<!-- 排序选择器 -->
|
||||
<v-select v-model="sortBy" :items="[
|
||||
{ title: tm('sort.default'), value: 'default' },
|
||||
{ title: tm('sort.stars'), value: 'stars' },
|
||||
{ title: tm('sort.author'), value: 'author' },
|
||||
{ title: tm('sort.updated'), value: 'updated' }
|
||||
]" density="compact" variant="outlined" hide-details style="max-width: 150px;">
|
||||
<template v-slot:prepend-inner>
|
||||
<v-icon size="small">mdi-sort</v-icon>
|
||||
</template>
|
||||
</v-select>
|
||||
|
||||
<!-- 排序方向切换按钮 -->
|
||||
<v-btn icon v-if="sortBy !== 'default'" @click="sortOrder = sortOrder === 'desc' ? 'asc' : 'desc'"
|
||||
variant="text" density="compact">
|
||||
<v-icon>{{ sortOrder === 'desc' ? 'mdi-sort-descending' : 'mdi-sort-ascending'
|
||||
}}</v-icon>
|
||||
<v-tooltip activator="parent" location="top">
|
||||
{{ sortOrder === 'desc' ? tm('sort.descending') : tm('sort.ascending') }}
|
||||
</v-tooltip>
|
||||
</v-btn>
|
||||
<!-- <v-switch v-model="showPluginFullName" :label="tm('market.showFullName')" hide-details
|
||||
density="compact" style="margin-left: 12px" /> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-col cols="12" md="12" style="padding: 0px;">
|
||||
<v-data-table :headers="pluginMarketHeaders" :items="pluginMarketData" item-key="name" style="border-radius: 10px;"
|
||||
:loading="loading_" v-model:search="marketSearch" :filter-keys="filterKeys"
|
||||
:custom-filter="marketCustomFilter">
|
||||
<template v-slot:item.name="{ item }">
|
||||
<div class="d-flex align-center"
|
||||
style="overflow-x: auto; scrollbar-width: thin; scrollbar-track-color: transparent;">
|
||||
<img v-if="item.logo" :src="item.logo"
|
||||
style="height: 80px; width: 80px; margin-right: 8px; border-radius: 8px; margin-top: 8px; margin-bottom: 8px;"
|
||||
alt="logo">
|
||||
<a :href="item?.repo" style="color: var(--v-theme-primaryText, #000);
|
||||
text-decoration:none">
|
||||
<div v-if="item.display_name">
|
||||
<span class="d-block">{{ item.display_name }}</span>
|
||||
<small style="color: grey; font-size: 60%;">({{ item.name }})</small>
|
||||
<v-row style="min-height: 26rem;">
|
||||
<v-col v-for="plugin in paginatedPlugins" :key="plugin.name" cols="12" md="6" lg="4">
|
||||
<v-card class="rounded-lg d-flex flex-column" elevation="0"
|
||||
style=" height: 12rem; position: relative;">
|
||||
|
||||
<!-- 推荐标记 -->
|
||||
<v-chip v-if="plugin?.pinned" color="warning" size="x-small" label
|
||||
style="position: absolute; right: 8px; top: 8px; z-index: 10; height: 20px; font-weight: bold;">
|
||||
🥳 推荐
|
||||
</v-chip>
|
||||
|
||||
<v-card-text
|
||||
style="padding: 12px; padding-bottom: 8px; display: flex; gap: 12px; width: 100%; flex: 1; overflow: hidden;">
|
||||
<div v-if="plugin?.logo" style="flex-shrink: 0;">
|
||||
<img :src="plugin.logo" :alt="plugin.name"
|
||||
style="height: 75px; width: 75px; border-radius: 8px; object-fit: cover;" />
|
||||
</div>
|
||||
|
||||
<div style="flex: 1; overflow: hidden; display: flex; flex-direction: column;">
|
||||
<!-- Display Name -->
|
||||
<div class="font-weight-bold"
|
||||
style="margin-bottom: 4px; line-height: 1.3; font-size: 1.2rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||
<span style="overflow: hidden; text-overflow: ellipsis;">
|
||||
{{ plugin.display_name?.length ? plugin.display_name :
|
||||
(showPluginFullName ? plugin.name : plugin.trimmedName) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Author with link -->
|
||||
<div class="d-flex align-center" style="gap: 4px; margin-bottom: 6px;">
|
||||
<v-icon icon="mdi-account" size="x-small"
|
||||
style="color: rgba(var(--v-theme-on-surface), 0.5);"></v-icon>
|
||||
<a v-if="plugin?.social_link" :href="plugin.social_link" target="_blank"
|
||||
class="text-subtitle-2 font-weight-medium"
|
||||
style="text-decoration: none; color: rgb(var(--v-theme-primary)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||
{{ plugin.author }}
|
||||
</a>
|
||||
<span v-else class="text-subtitle-2 font-weight-medium"
|
||||
style="color: rgb(var(--v-theme-primary)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||
{{ plugin.author }}
|
||||
</span>
|
||||
<div class="d-flex align-center text-subtitle-2 ml-2"
|
||||
style="color: rgba(var(--v-theme-on-surface), 0.7);">
|
||||
<v-icon icon="mdi-source-branch" size="x-small" style="margin-right: 2px;"></v-icon>
|
||||
<span>{{ plugin.version }}</span>
|
||||
</div>
|
||||
<span v-else>{{ showPluginFullName ? item.name : item.trimmedName }}</span>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:item.desc="{ item }">
|
||||
<small>
|
||||
{{ item.desc }}
|
||||
</small>
|
||||
</template>
|
||||
<template v-slot:item.author="{ item }">
|
||||
<div style="font-size: 12px;">
|
||||
<span v-if="item?.social_link"><a :href="item?.social_link">{{ item.author }}</a></span>
|
||||
<span v-else>{{ item.author }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:item.stars="{ item }">
|
||||
<span>{{ item.stars }}</span>
|
||||
</template>
|
||||
<template v-slot:item.updated_at="{ item }">
|
||||
<small>{{ new Date(item.updated_at).toLocaleString() }}</small>
|
||||
</template>
|
||||
<template v-slot:item.tags="{ item }">
|
||||
<span v-if="item.tags.length === 0">-</span>
|
||||
<v-chip v-for="tag in item.tags" :key="tag" :color="tag === 'danger' ? 'error' : 'primary'"
|
||||
size="x-small" v-show="tag !== 'danger'" class="ma-1">
|
||||
{{ tag }}</v-chip>
|
||||
</template>
|
||||
<template v-slot:item.actions="{ item }">
|
||||
<v-btn class="text-none mr-2" size="x-small" icon variant="text"
|
||||
@click="open(item.repo)"><v-icon>mdi-github</v-icon></v-btn>
|
||||
<v-btn v-if="!item.installed" class="text-none mr-2" size="x-small" icon variant="text"
|
||||
@click="handleInstallPlugin(item)">
|
||||
<v-icon>mdi-download</v-icon></v-btn>
|
||||
<v-btn v-else class="text-none mr-2" size="x-small" icon variant="text"
|
||||
disabled><v-icon>mdi-check</v-icon></v-btn>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</v-col>
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
<div class="text-caption"
|
||||
style="overflow: scroll; color: rgba(var(--v-theme-on-surface), 0.6); line-height: 1.3; margin-bottom: 6px; flex: 1;">
|
||||
{{ plugin.desc }}
|
||||
</div>
|
||||
|
||||
<!-- Stats: Stars & Updated & Version -->
|
||||
<div class="d-flex align-center" style="gap: 8px; margin-top: auto;">
|
||||
<div v-if="plugin.stars !== undefined" class="d-flex align-center text-subtitle-2"
|
||||
style="color: rgba(var(--v-theme-on-surface), 0.7);">
|
||||
<v-icon icon="mdi-star" size="x-small" style="margin-right: 2px;"></v-icon>
|
||||
<span>{{ plugin.stars }}</span>
|
||||
</div>
|
||||
<div v-if="plugin.updated_at" class="d-flex align-center text-subtitle-2"
|
||||
style="color: rgba(var(--v-theme-on-surface), 0.7);">
|
||||
<v-icon icon="mdi-clock-outline" size="x-small" style="margin-right: 2px;"></v-icon>
|
||||
<span>{{ new Date(plugin.updated_at).toLocaleString() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
|
||||
<!-- Actions -->
|
||||
<v-card-actions style="gap: 6px; padding: 8px 12px; padding-top: 0;">
|
||||
<v-chip v-for="tag in plugin.tags?.slice(0, 2)" :key="tag"
|
||||
:color="tag === 'danger' ? 'error' : 'primary'" label size="x-small" style="height: 20px;">
|
||||
{{ tag === 'danger' ? tm('tags.danger') : tag }}
|
||||
</v-chip>
|
||||
<v-menu v-if="plugin.tags && plugin.tags.length > 2" open-on-hover offset-y>
|
||||
<template v-slot:activator="{ props: menuProps }">
|
||||
<v-chip v-bind="menuProps" color="grey" label size="x-small"
|
||||
style="height: 20px; cursor: pointer;">
|
||||
+{{ plugin.tags.length - 2 }}
|
||||
</v-chip>
|
||||
</template>
|
||||
<v-list density="compact">
|
||||
<v-list-item v-for="tag in plugin.tags.slice(2)" :key="tag">
|
||||
<v-chip :color="tag === 'danger' ? 'error' : 'primary'" label size="small">
|
||||
{{ tag === 'danger' ? tm('tags.danger') : tag }}
|
||||
</v-chip>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn v-if="plugin?.repo" color="secondary" size="x-small" variant="tonal" :href="plugin.repo"
|
||||
target="_blank" style="height: 24px;">
|
||||
<v-icon icon="mdi-github" start size="x-small"></v-icon>
|
||||
仓库
|
||||
</v-btn>
|
||||
<v-btn v-if="!plugin?.installed" color="primary" size="x-small"
|
||||
@click="handleInstallPlugin(plugin)" variant="flat" style="height: 24px;">
|
||||
{{ tm('buttons.install') }}
|
||||
</v-btn>
|
||||
<v-chip v-else color="success" size="x-small" label style="height: 20px;">
|
||||
✓ {{ tm('status.installed') }}
|
||||
</v-chip>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- 底部分页控件 -->
|
||||
<div class="d-flex justify-center mt-4" v-if="totalPages > 1">
|
||||
<v-pagination v-model="currentPage" :length="totalPages" :total-visible="7" size="small"></v-pagination>
|
||||
</div>
|
||||
</div>
|
||||
</v-tab-item>
|
||||
|
||||
@@ -900,7 +1062,7 @@ onMounted(async () => {
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<v-col v-if="activeTab === 'market'" style="margin-bottom: 16px;" cols="12" md="12">
|
||||
<v-col v-if="activeTab === 'market'" cols="12" md="12">
|
||||
<small><a href="https://astrbot.app/dev/plugin.html">{{ tm('market.devDocs') }}</a></small> |
|
||||
<small> <a href="https://github.com/AstrBotDevs/AstrBot_Plugins_Collection">{{ tm('market.submitRepo')
|
||||
}}</a></small>
|
||||
@@ -997,10 +1159,7 @@ onMounted(async () => {
|
||||
:repo-url="readmeDialog.repoUrl" />
|
||||
|
||||
<!-- 卸载插件确认对话框(列表模式用) -->
|
||||
<UninstallConfirmDialog
|
||||
v-model="showUninstallDialog"
|
||||
@confirm="handleUninstallConfirm"
|
||||
/>
|
||||
<UninstallConfirmDialog v-model="showUninstallDialog" @confirm="handleUninstallConfirm" />
|
||||
|
||||
<!-- 危险插件确认对话框 -->
|
||||
<v-dialog v-model="dangerConfirmDialog" width="500" persistent>
|
||||
|
||||
+74
-90
@@ -1,107 +1,100 @@
|
||||
[project]
|
||||
name = "AstrBot"
|
||||
dynamic = ["version"]
|
||||
description = "易上手的多平台 LLM 聊天机器人及开发框架"
|
||||
version = "4.5.6"
|
||||
description = "Easy-to-use multi-platform LLM chatbot and development framework"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
keywords = [
|
||||
"Astrbot",
|
||||
"Astrbot Module",
|
||||
"Astrbot Plugin"
|
||||
]
|
||||
keywords = ["Astrbot", "Astrbot Module", "Astrbot Plugin"]
|
||||
|
||||
dependencies = [
|
||||
"aiocqhttp>=1.4.4",
|
||||
"aiodocker>=0.24.0",
|
||||
"aiohttp>=3.11.18",
|
||||
"aiosqlite>=0.21.0",
|
||||
"anthropic>=0.51.0",
|
||||
"apscheduler>=3.11.0",
|
||||
"beautifulsoup4>=4.13.4",
|
||||
"certifi>=2025.4.26",
|
||||
"chardet~=5.1.0",
|
||||
"colorlog>=6.9.0",
|
||||
"cryptography>=44.0.3",
|
||||
"dashscope>=1.23.2",
|
||||
"defusedxml>=0.7.1",
|
||||
"deprecated>=1.2.18",
|
||||
"dingtalk-stream>=0.22.1",
|
||||
"docstring-parser>=0.16",
|
||||
"faiss-cpu==1.10.0",
|
||||
"filelock>=3.18.0",
|
||||
"google-genai>=1.14.0",
|
||||
"lark-oapi>=1.4.15",
|
||||
"lxml-html-clean>=0.4.2",
|
||||
"mcp>=1.8.0",
|
||||
"openai>=1.78.0",
|
||||
"ormsgpack>=1.9.1",
|
||||
"pillow>=11.2.1",
|
||||
"pip>=25.1.1",
|
||||
"psutil>=5.8.0",
|
||||
"py-cord>=2.6.1",
|
||||
"pydantic~=2.10.3",
|
||||
"pydub>=0.25.1",
|
||||
"pyjwt>=2.10.1",
|
||||
"python-telegram-bot>=22.0",
|
||||
"qq-botpy>=1.2.1",
|
||||
"quart>=0.20.0",
|
||||
"readability-lxml>=0.8.4.1",
|
||||
"silk-python>=0.2.6",
|
||||
"slack-sdk>=3.35.0",
|
||||
"sqlalchemy[asyncio]>=2.0.41",
|
||||
"sqlmodel>=0.0.24",
|
||||
"telegramify-markdown>=0.5.1",
|
||||
"watchfiles>=1.0.5",
|
||||
"websockets>=15.0.1",
|
||||
"wechatpy>=1.8.18",
|
||||
"audioop-lts ; python_full_version >= '3.13'",
|
||||
"click>=8.2.1",
|
||||
"pypdf>=6.1.1",
|
||||
"aiofiles>=25.1.0",
|
||||
"rank-bm25>=0.2.2",
|
||||
"jieba>=0.42.1",
|
||||
"markitdown-no-magika[docx,xls,xlsx]>=0.1.2",
|
||||
"xinference-client",
|
||||
"aiocqhttp>=1.4.4",
|
||||
"aiodocker>=0.24.0",
|
||||
"aiohttp>=3.11.18",
|
||||
"aiosqlite>=0.21.0",
|
||||
"anthropic>=0.51.0",
|
||||
"apscheduler>=3.11.0",
|
||||
"beautifulsoup4>=4.13.4",
|
||||
"certifi>=2025.4.26",
|
||||
"chardet~=5.1.0",
|
||||
"colorlog>=6.9.0",
|
||||
"cryptography>=44.0.3",
|
||||
"dashscope>=1.23.2",
|
||||
"defusedxml>=0.7.1",
|
||||
"deprecated>=1.2.18",
|
||||
"dingtalk-stream>=0.22.1",
|
||||
"docstring-parser>=0.16",
|
||||
"faiss-cpu==1.10.0",
|
||||
"filelock>=3.18.0",
|
||||
"google-genai>=1.14.0",
|
||||
"lark-oapi>=1.4.15",
|
||||
"lxml-html-clean>=0.4.2",
|
||||
"mcp>=1.8.0",
|
||||
"openai>=1.78.0",
|
||||
"ormsgpack>=1.9.1",
|
||||
"pillow>=11.2.1",
|
||||
"pip>=25.1.1",
|
||||
"psutil>=5.8.0",
|
||||
"py-cord>=2.6.1",
|
||||
"pydantic~=2.10.3",
|
||||
"pydub>=0.25.1",
|
||||
"pyjwt>=2.10.1",
|
||||
"python-telegram-bot>=22.0",
|
||||
"qq-botpy>=1.2.1",
|
||||
"quart>=0.20.0",
|
||||
"readability-lxml>=0.8.4.1",
|
||||
"silk-python>=0.2.6",
|
||||
"slack-sdk>=3.35.0",
|
||||
"sqlalchemy[asyncio]>=2.0.41",
|
||||
"sqlmodel>=0.0.24",
|
||||
"telegramify-markdown>=0.5.1",
|
||||
"watchfiles>=1.0.5",
|
||||
"websockets>=15.0.1",
|
||||
"wechatpy>=1.8.18",
|
||||
"audioop-lts ; python_full_version >= '3.13'",
|
||||
"click>=8.2.1",
|
||||
"pypdf>=6.1.1",
|
||||
"aiofiles>=25.1.0",
|
||||
"rank-bm25>=0.2.2",
|
||||
"jieba>=0.42.1",
|
||||
"markitdown-no-magika[docx,xls,xlsx]>=0.1.2",
|
||||
"xinference-client",
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"commitizen>=4.9.1",
|
||||
"pytest>=8.4.1",
|
||||
"pytest-asyncio>=1.1.0",
|
||||
"pytest-cov>=6.2.1",
|
||||
"ruff>=0.12.8",
|
||||
"commitizen>=4.9.1",
|
||||
"pytest>=8.4.1",
|
||||
"pytest-asyncio>=1.1.0",
|
||||
"pytest-cov>=6.2.1",
|
||||
"ruff>=0.12.8",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
astrbot = "astrbot.cli.__main__:cli"
|
||||
|
||||
[tool.ruff]
|
||||
exclude = [
|
||||
"astrbot/core/utils/t2i/local_strategy.py",
|
||||
"astrbot/api/all.py",
|
||||
]
|
||||
exclude = ["astrbot/core/utils/t2i/local_strategy.py", "astrbot/api/all.py"]
|
||||
line-length = 88
|
||||
target-version = "py310"
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = [
|
||||
"F", # Pyflakes
|
||||
"W", # pycodestyle warnings
|
||||
"E", # pycodestyle errors
|
||||
"F", # Pyflakes
|
||||
"W", # pycodestyle warnings
|
||||
"E", # pycodestyle errors
|
||||
"ASYNC", # flake8-async
|
||||
"C4", # flake8-comprehensions
|
||||
"Q", # flake8-quotes
|
||||
"I", # import-order
|
||||
"UP", # pyupgrade
|
||||
"C4", # flake8-comprehensions
|
||||
"Q", # flake8-quotes
|
||||
"I", # import-order
|
||||
"UP", # pyupgrade
|
||||
# "SIM", # flake8-simplify
|
||||
]
|
||||
ignore = [
|
||||
"F403",
|
||||
"F405",
|
||||
"E501",
|
||||
"ASYNC230" # TODO: handle ASYNC230 in AstrBot
|
||||
"F403",
|
||||
"F405",
|
||||
"E501",
|
||||
"ASYNC230", # TODO: handle ASYNC230 in AstrBot
|
||||
]
|
||||
|
||||
[tool.pyright]
|
||||
@@ -109,18 +102,9 @@ typeCheckingMode = "basic"
|
||||
pythonVersion = "3.10"
|
||||
reportMissingTypeStubs = false
|
||||
reportMissingImports = false
|
||||
include = ["astrbot","packages"]
|
||||
include = ["astrbot", "packages"]
|
||||
exclude = ["dashboard", "node_modules", "dist", "data", "tests"]
|
||||
|
||||
[tool.hatch.version]
|
||||
source = "uv-dynamic-versioning"
|
||||
|
||||
[tool.uv-dynamic-versioning]
|
||||
vcs = "git"
|
||||
style = "pep440"
|
||||
bump = true
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling", "uv-dynamic-versioning"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
Reference in New Issue
Block a user