Skip to content
Merged

Dev #20

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
23b2b09
test
Cat-bl Jun 15, 2026
32ea0df
test
Cat-bl Jun 15, 2026
e760863
test
Cat-bl Jun 15, 2026
ae7efe2
test
Cat-bl Jun 15, 2026
6366868
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 15, 2026
16a4aaf
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 15, 2026
9611481
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 15, 2026
d706771
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 15, 2026
a2fc9f9
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 15, 2026
1e1dae6
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 16, 2026
f93ac5f
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 16, 2026
bf174ac
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 16, 2026
8c0257a
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 16, 2026
31a4642
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 16, 2026
bf0d2bb
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 16, 2026
c1c8ef3
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 16, 2026
f4ba842
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 17, 2026
7b34d6d
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 18, 2026
c27fbe1
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 18, 2026
04f8eb4
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 18, 2026
1155386
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 18, 2026
18012c2
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 18, 2026
a8275d6
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 18, 2026
c236d8d
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 21, 2026
1aaa3e2
空间工具优化
Cat-bl Jun 21, 2026
0ccfffb
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 21, 2026
dea7b26
fix
Cat-bl Jun 22, 2026
0d4e40f
本地工具流式、非流格式代码优化
Cat-bl Jun 22, 2026
9e4d320
Merge branch 'dev' of https://github.com/Cat-bl/bl-chat-plugin into dev
Cat-bl Jun 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 76 additions & 2 deletions functions/functions_tools/AiMindMapTool.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,7 @@ export class AiMindMapTool extends AbstractTool {
body: JSON.stringify(requestData),
})

const jsonData = await response.json()
const markdownContent = jsonData.choices?.[0]?.message.content
const markdownContent = await this.parseResponse(response)
// logger.info(markdownContent,77)
if (!markdownContent) {
console.error('API返回空内容');
Expand Down Expand Up @@ -205,4 +204,79 @@ export class AiMindMapTool extends AbstractTool {
return `生成失败: ${error.message}`;
}
}

// 自动检测并解析响应(兼容流式 + 非流式)
async parseResponse(response) {
if (!response.ok) {
throw new Error(`API请求失败: ${response.status} ${response.statusText}`);
}

const contentType = response.headers.get('content-type') || '';

// 检测流式响应(优先级高,避免先 json() 消费 body)
if (contentType.includes('text/event-stream') || contentType.includes('stream')) {
return await this.handleStreamResponse(response);
}

// 检测 JSON 响应
if (contentType.includes('application/json')) {
const data = await response.json();
return data.choices?.[0]?.message?.content || '';
}

// Content-Type 未明确时,先读 body 一次性判断
const text = await response.text();
// 尝试按 SSE 格式解析
if (text.includes('data: ')) {
return this.parseSSEText(text);
}
// 尝试按 JSON 解析
try {
const data = JSON.parse(text);
return data.choices?.[0]?.message?.content || '';
} catch {
throw new Error('无法解析响应格式');
}
}

// 从已读取的文本中解析 SSE 格式
parseSSEText(text) {
let content = "";
for (const line of text.split("\n")) {
if (!line.startsWith("data: ")) continue;
const dataStr = line.slice(6).trim();
if (dataStr === "[DONE]") break;
try {
const data = JSON.parse(dataStr);
content += data?.choices?.[0]?.delta?.content || "";
} catch { }
}
if (!content) throw new Error("未接收到有效内容");
return content;
}

async handleStreamResponse(response) {
const reader = response.body.getReader();
const decoder = new TextDecoder();
let content = "";

while (true) {
const { value, done } = await reader.read();
if (done) break;

for (const line of decoder.decode(value).split("\n")) {
if (!line.startsWith("data: ")) continue;
const dataStr = line.slice(6).trim();
if (dataStr === "[DONE]") break;

try {
const data = JSON.parse(dataStr);
content += data?.choices?.[0]?.delta?.content || "";
} catch { }
}
}

if (!content) throw new Error("未接收到有效内容");
return content;
}
}
59 changes: 53 additions & 6 deletions functions/functions_tools/BananaTool.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export class BananaTool extends AbstractTool {
}

async func(opts, e) {
const STREAM = false;
const config = this.loadConfig();
const { prompt, images: rawImages } = opts;

Expand All @@ -56,14 +55,11 @@ export class BananaTool extends AbstractTool {
body: JSON.stringify({
model: model || "gemini-3-pro-image-preview",
messages: [{ role: "user", content: imgurls }],
stream: STREAM,
stream: false,
}),
});

const imageUrl = STREAM
? await this.handleStreamResponse(response)
: await this.handleNormalResponse(response);

const imageUrl = await this.parseResponse(response);
const processedUrl = this.extractImageUrl(imageUrl);

if (processedUrl) {
Expand Down Expand Up @@ -110,6 +106,57 @@ export class BananaTool extends AbstractTool {
return messages;
}

// 自动检测并解析响应(兼容流式 + 非流式)
async parseResponse(response) {
if (!response.ok) {
throw new Error(`API请求失败: ${response.status} ${response.statusText}`);
}

const contentType = response.headers.get('content-type') || '';

// 检测流式响应(优先级高,避免先 json() 消费 body)
if (contentType.includes('text/event-stream') || contentType.includes('stream')) {
return await this.handleStreamResponse(response);
}

// 检测 JSON 响应
if (contentType.includes('application/json')) {
return await this.handleNormalResponse(response);
}

// Content-Type 未明确时,先读 body 一次性判断
const text = await response.text();
// 尝试按 SSE 格式解析
if (text.includes('data: ')) {
return this.parseSSEText(text);
}
// 尝试按 JSON 解析
try {
const data = JSON.parse(text);
return data?.choices?.[0]?.message?.images?.[0]?.image_url?.url ||
data?.choices?.[0]?.message?.images?.[0]?.url ||
data?.choices?.[0]?.message?.content;
} catch {
throw new Error('无法解析响应格式');
}
}

// 从已读取的文本中解析 SSE 格式
parseSSEText(text) {
let content = "";
for (const line of text.split("\n")) {
if (!line.startsWith("data: ")) continue;
const dataStr = line.slice(6).trim();
if (dataStr === "[DONE]") break;
try {
const data = JSON.parse(dataStr);
content += data?.choices?.[0]?.delta?.content || "";
} catch { }
}
if (!content) throw new Error("未接收到有效内容");
return content;
}

// 处理流式响应
async handleStreamResponse(response) {
if (!response.ok || !response.body) {
Expand Down
79 changes: 77 additions & 2 deletions functions/functions_tools/GoogleAnalysisTool.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,9 @@ export class GoogleImageAnalysisTool extends AbstractTool {
body: JSON.stringify(requestData),
})

const analysis = await response.json()
const content = await this.parseResponse(response)
return {
analysis: analysis.choices?.[0]?.message.content
analysis: content
};

// const apiUrl = "https://api.pearktrue.cn/api/airecognizeimg/"
Expand Down Expand Up @@ -257,4 +257,79 @@ export class GoogleImageAnalysisTool extends AbstractTool {
}
}

// 自动检测并解析响应(兼容流式 + 非流式)
async parseResponse(response) {
if (!response.ok) {
throw new Error(`API请求失败: ${response.status} ${response.statusText}`);
}

const contentType = response.headers.get('content-type') || '';

// 检测流式响应(优先级高,避免先 json() 消费 body)
if (contentType.includes('text/event-stream') || contentType.includes('stream')) {
return await this.handleStreamResponse(response);
}

// 检测 JSON 响应
if (contentType.includes('application/json')) {
const data = await response.json();
return data.choices?.[0]?.message?.content || '';
}

// Content-Type 未明确时,先读 body 一次性判断
const text = await response.text();
// 尝试按 SSE 格式解析
if (text.includes('data: ')) {
return this.parseSSEText(text);
}
// 尝试按 JSON 解析
try {
const data = JSON.parse(text);
return data.choices?.[0]?.message?.content || '';
} catch {
throw new Error('无法解析响应格式');
}
}

// 从已读取的文本中解析 SSE 格式
parseSSEText(text) {
let content = "";
for (const line of text.split("\n")) {
if (!line.startsWith("data: ")) continue;
const dataStr = line.slice(6).trim();
if (dataStr === "[DONE]") break;
try {
const data = JSON.parse(dataStr);
content += data?.choices?.[0]?.delta?.content || "";
} catch { }
}
if (!content) throw new Error("未接收到有效内容");
return content;
}

async handleStreamResponse(response) {
const reader = response.body.getReader();
const decoder = new TextDecoder();
let content = "";

while (true) {
const { value, done } = await reader.read();
if (done) break;

for (const line of decoder.decode(value).split("\n")) {
if (!line.startsWith("data: ")) continue;
const dataStr = line.slice(6).trim();
if (dataStr === "[DONE]") break;

try {
const data = JSON.parse(dataStr);
content += data?.choices?.[0]?.delta?.content || "";
} catch { }
}
}

if (!content) throw new Error("未接收到有效内容");
return content;
}

}
62 changes: 54 additions & 8 deletions functions/functions_tools/GoogleImageEditTool.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ export class GoogleImageEditTool extends AbstractTool {
}

async func(opts, e) {
const STREAM = false;

try {
const config = this.loadConfig();
const { prompt } = opts;
Expand All @@ -59,14 +57,12 @@ export class GoogleImageEditTool extends AbstractTool {
body: JSON.stringify({
model: imageEditApiModel || "gemini-3-pro-image-preview",
messages: [{ role: "user", content }],
stream: STREAM,
stream: false,
}),
});

// 处理响应
const imageUrl = STREAM
? await this.handleStreamResponse(response)
: await this.handleNormalResponse(response);
// 自动检测并处理响应
const imageUrl = await this.parseResponse(response);

const processedUrl = this.extractImageUrl(imageUrl);

Expand Down Expand Up @@ -118,6 +114,57 @@ export class GoogleImageEditTool extends AbstractTool {
return messages;
}

// 自动检测并解析响应(兼容流式 + 非流式)
async parseResponse(response) {
if (!response.ok) {
throw new Error(`API请求失败: ${response.status} ${response.statusText}`);
}

const contentType = response.headers.get('content-type') || '';

// 检测流式响应(优先级高,避免先 json() 消费 body)
if (contentType.includes('text/event-stream') || contentType.includes('stream')) {
return await this.handleStreamResponse(response);
}

// 检测 JSON 响应
if (contentType.includes('application/json')) {
return await this.handleNormalResponse(response);
}

// Content-Type 未明确时,先读 body 一次性判断
const text = await response.text();
// 尝试按 SSE 格式解析
if (text.includes('data: ')) {
return this.parseSSEText(text);
}
// 尝试按 JSON 解析
try {
const data = JSON.parse(text);
return data?.choices?.[0]?.message?.images?.[0]?.image_url?.url ||
data?.choices?.[0]?.message?.images?.[0]?.url ||
data?.choices?.[0]?.message?.content;
} catch {
throw new Error('无法解析响应格式');
}
}

// 从已读取的文本中解析 SSE 格式
parseSSEText(text) {
let content = "";
for (const line of text.split("\n")) {
if (!line.startsWith("data: ")) continue;
const dataStr = line.slice(6).trim();
if (dataStr === "[DONE]") break;
try {
const data = JSON.parse(dataStr);
content += data?.choices?.[0]?.delta?.content || "";
} catch { }
}
if (!content) throw new Error("未接收到有效内容");
return content;
}

async handleStreamResponse(response) {
if (!response.ok || !response.body) {
throw new Error(`API请求失败: ${response.statusText}`);
Expand All @@ -133,7 +180,6 @@ export class GoogleImageEditTool extends AbstractTool {

for (const line of decoder.decode(value).split("\n")) {
if (!line.startsWith("data: ")) continue;

const dataStr = line.slice(6).trim();
if (dataStr === "[DONE]") break;

Expand Down
4 changes: 2 additions & 2 deletions functions/functions_tools/JinyanTool.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ export class JinyanTool extends AbstractTool {

const groupId = e.group_id;

// 权限检查:普通用户不能命令机器人禁言,除非机器人自主决定或是主人
if (!selfDecision && !['owner', 'admin'].includes(senderRole) && !this.e.isMaster) {
// 权限检查:普通用户不能命令机器人禁言,除非机器人自主决定
if (!selfDecision && !['owner', 'admin'].includes(senderRole)) {
return '该群员不是群主或管理员,无权命令我执行禁言操作';
}

Expand Down
6 changes: 4 additions & 2 deletions functions/functions_tools/QQZoneTool.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class QQZoneTool extends AbstractTool {
properties: {
text: {
type: 'string',
description: '你将要发送到qq空间说说的内容(以发送者的角度生成流畅通顺的内容)',
description: '说说正文文本(以发送者的角度生成流畅通顺的内容)。注意:参数名必须为 "text",不要用 content/message 等其他名字。例:{"text": "今天天气真好"}',
},
type: {
type: 'boolean',
Expand All @@ -30,7 +30,9 @@ export class QQZoneTool extends AbstractTool {
}

async func(opts, e) {
const { text, type = false, pos = 1} = opts;
// 兼容模型可能误用的常见别名(content/message/msg),以 text 为准
const text = opts.text ?? opts.content ?? opts.message ?? opts.msg
const { type = false, pos = 1 } = opts
if (!type) {
try {
if (!text) return {
Expand Down
Loading
Loading