剩余参数
剩余参数是 OneBot Commander 的高级功能,允许你捕获模式匹配后剩余的所有消息段。
基本概念
什么是剩余参数
剩余参数使用 ...
语法,可以捕获模式匹配后剩余的所有消息段:
typescript
const commander = new Commander('command [...rest]');
// 匹配: "command arg1 arg2 arg3" -> { rest: ['arg1', 'arg2', 'arg3'] }
语法格式:[...参数名]
或 [...参数名:类型]
基本用法
typescript
// 通用剩余参数
const commander = new Commander('echo [...args]');
const segments = [
{ type: 'text', data: { text: 'echo hello world' } }
];
const result = commander.match(segments);
// result[0] = { args: ['hello', 'world'] }
类型化剩余参数
指定类型的剩余参数
typescript
// 只捕获文本类型的剩余参数
const commander = new Commander('text [...messages:text]');
const segments = [
{ type: 'text', data: { text: 'text' } },
{ type: 'text', data: { text: 'message1' } },
{ type: 'text', data: { text: 'message2' } },
{ type: 'face', data: { id: 1 } }
];
const result = commander.match(segments);
// result[0] = { messages: ['message1', 'message2'] }
// 剩余: [{ type: 'face', data: { id: 1 } }]
混合类型剩余参数
typescript
// 捕获所有类型的剩余参数
const commander = new Commander('forward [...items]');
const segments = [
{ type: 'text', data: { text: 'forward' } },
{ type: 'text', data: { text: 'message' } },
{ type: 'face', data: { id: 1 } },
{ type: 'image', data: { file: 'img.jpg' } }
];
const result = commander.match(segments);
// result[0] = {
// items: [
// { type: 'text', data: { text: 'message' } },
// { type: 'face', data: { id: 1 } },
// { type: 'image', data: { file: 'img.jpg' } }
// ]
// }
高级用法
组合必需参数和剩余参数
typescript
// 必需参数 + 剩余参数
const commander = new Commander('process <action:text> [...data]');
const segments = [
{ type: 'text', data: { text: 'process upload' } },
{ type: 'text', data: { text: 'file1.txt' } },
{ type: 'text', data: { text: 'file2.txt' } }
];
const result = commander.match(segments);
// result[0] = {
// action: 'upload',
// data: [
// { type: 'text', data: { text: 'file1.txt' } },
// { type: 'text', data: { text: 'file2.txt' } }
// ]
// }
多个剩余参数
typescript
// 注意:一个模式中只能有一个剩余参数
const commander = new Commander('batch [...items] [...options]');
// 这是错误的语法,会抛出异常
剩余参数与可选参数
typescript
// 可选参数 + 剩余参数
const commander = new Commander('search [query:text] [...filters]');
// 有查询条件
const segments1 = [
{ type: 'text', data: { text: 'search hello' } },
{ type: 'text', data: { text: 'filter1' } },
{ type: 'text', data: { text: 'filter2' } }
];
// result[0] = { query: 'hello', filters: ['filter1', 'filter2'] }
// 无查询条件
const segments2 = [
{ type: 'text', data: { text: 'search' } },
{ type: 'text', data: { text: 'filter1' } }
];
// result[0] = { filters: ['filter1'] }
实际应用示例
消息转发系统
typescript
const forwardCommander = new Commander('forward [...messages]');
forwardCommander.action((params) => {
const { messages = [] } = params;
// 处理转发的消息
return {
type: 'forward',
count: messages.length,
messages: messages.map(msg => ({
type: msg.type,
data: msg.data
}))
};
});
// 使用示例
const segments = [
{ type: 'text', data: { text: 'forward' } },
{ type: 'text', data: { text: 'Hello' } },
{ type: 'face', data: { id: 1 } },
{ type: 'image', data: { file: 'photo.jpg' } }
];
const result = forwardCommander.match(segments);
// result[0] = {
// type: 'forward',
// count: 3,
// messages: [
// { type: 'text', data: { text: 'Hello' } },
// { type: 'face', data: { id: 1 } },
// { type: 'image', data: { file: 'photo.jpg' } }
// ]
// }
批量处理系统
typescript
const batchCommander = new Commander('batch <operation:text> [...items]');
batchCommander.action(async (params) => {
const { operation, items = [] } = params;
const results = [];
for (const item of items) {
try {
let result;
switch (operation) {
case 'process':
result = await processItem(item);
break;
case 'validate':
result = await validateItem(item);
break;
case 'transform':
result = await transformItem(item);
break;
default:
throw new Error(`不支持的操作: ${operation}`);
}
results.push({ item, result, success: true });
} catch (error) {
results.push({ item, error: error.message, success: false });
}
}
return {
operation,
total: items.length,
success: results.filter(r => r.success).length,
failed: results.filter(r => !r.success).length,
results
};
});
搜索系统
typescript
const searchCommander = new Commander('search <query:text> [...options]');
searchCommander.action(async (params) => {
const { query, options = [] } = params;
// 解析搜索选项
const searchOptions = {};
for (const option of options) {
if (typeof option === 'string' && option.includes(':')) {
const [key, value] = option.split(':');
searchOptions[key] = value;
}
}
// 执行搜索
const results = await performSearch(query, searchOptions);
return {
query,
options: searchOptions,
results,
count: results.length
};
});
数据处理技巧
剩余参数过滤
typescript
const commander = new Commander('filter [...items]');
commander.action((params) => {
const { items = [] } = params;
// 过滤空值
const filtered = items.filter(item => {
if (item.type === 'text') {
return item.data.text && item.data.text.trim() !== '';
}
return true;
});
return {
original: items.length,
filtered: filtered.length,
items: filtered
};
});
剩余参数转换
typescript
const commander = new Commander('convert [...values]');
commander.action((params) => {
const { values = [] } = params;
// 转换不同类型的值
const converted = values.map(item => {
if (item.type === 'text') {
const text = item.data.text;
// 尝试转换为数字
if (!isNaN(text)) {
return Number(text);
}
// 尝试转换为布尔值
if (text.toLowerCase() === 'true') return true;
if (text.toLowerCase() === 'false') return false;
// 保持为字符串
return text;
}
return item;
});
return { converted };
});
剩余参数分组
typescript
const commander = new Commander('group [...items]');
commander.action((params) => {
const { items = [] } = params;
// 按类型分组
const groups = {};
for (const item of items) {
if (!groups[item.type]) {
groups[item.type] = [];
}
groups[item.type].push(item);
}
return {
groups,
summary: Object.entries(groups).map(([type, items]) => ({
type,
count: items.length
}))
};
});
性能优化
延迟处理
typescript
const commander = new Commander('lazy [...items]');
commander.action((params) => {
const { items = [] } = params;
// 返回处理函数,而不是立即处理
return {
items,
process: async () => {
const results = [];
for (const item of items) {
const result = await processItem(item);
results.push(result);
}
return results;
}
};
});
流式处理
typescript
const commander = new Commander('stream [...items]');
commander.action((params) => {
const { items = [] } = params;
// 创建流式处理器
return {
items,
async *process() {
for (const item of items) {
const result = await processItem(item);
yield result;
}
}
};
});
错误处理
剩余参数验证
typescript
const commander = new Commander('validate [...items]');
commander.action((params) => {
const { items = [] } = params;
const validationResults = [];
for (const item of items) {
try {
// 验证每个项目
const isValid = validateItem(item);
validationResults.push({
item,
valid: isValid,
error: null
});
} catch (error) {
validationResults.push({
item,
valid: false,
error: error.message
});
}
}
return {
total: items.length,
valid: validationResults.filter(r => r.valid).length,
invalid: validationResults.filter(r => !r.valid).length,
results: validationResults
};
});
部分失败处理
typescript
const commander = new Commander('safe [...items]');
commander.action(async (params) => {
const { items = [] } = params;
const results = [];
const errors = [];
for (let i = 0; i < items.length; i++) {
try {
const result = await processItem(items[i]);
results.push({ index: i, result });
} catch (error) {
errors.push({ index: i, error: error.message });
}
}
return {
total: items.length,
success: results.length,
failed: errors.length,
results,
errors
};
});
调试技巧
剩余参数日志
typescript
const commander = new Commander('debug [...items]');
commander.action((params) => {
const { items = [] } = params;
console.log('剩余参数数量:', items.length);
console.log('剩余参数类型分布:');
const typeCount = {};
for (const item of items) {
typeCount[item.type] = (typeCount[item.type] || 0) + 1;
}
Object.entries(typeCount).forEach(([type, count]) => {
console.log(` ${type}: ${count}`);
});
return {
count: items.length,
types: typeCount,
items
};
});
剩余参数分析
typescript
function analyzeRestParams(items) {
const analysis = {
total: items.length,
types: {},
textLengths: [],
hasImages: false,
hasFiles: false
};
for (const item of items) {
// 统计类型
analysis.types[item.type] = (analysis.types[item.type] || 0) + 1;
// 分析文本长度
if (item.type === 'text') {
analysis.textLengths.push(item.data.text.length);
}
// 检查特殊类型
if (item.type === 'image') analysis.hasImages = true;
if (item.type === 'file') analysis.hasFiles = true;
}
return analysis;
}
const commander = new Commander('analyze [...items]');
commander.action((params) => {
return analyzeRestParams(params.items || []);
});
最佳实践
1. 合理使用剩余参数
typescript
// ✅ 好的使用方式
const good = new Commander('forward [...messages]');
const good2 = new Commander('batch <operation:text> [...items]');
// ❌ 避免过度使用
const bad = new Commander('[...everything]'); // 过于宽泛
2. 类型安全
typescript
// ✅ 指定类型以提高安全性
const safe = new Commander('text [...messages:text]');
// ❌ 不指定类型可能导致类型错误
const unsafe = new Commander('text [...messages]');
3. 性能考虑
typescript
// ✅ 限制剩余参数数量
const commander = new Commander('process [...items]');
commander.action((params) => {
const { items = [] } = params;
// 限制处理数量
if (items.length > 100) {
throw new Error('项目数量过多,最多支持100个');
}
return processItems(items);
});
常见问题
1. 剩余参数位置
typescript
// 剩余参数必须在最后
const commander = new Commander('command [...rest]<required:text>');
// 这是错误的语法
2. 多个剩余参数
typescript
// 一个模式中只能有一个剩余参数
const commander = new Commander('command [...rest1][...rest2]');
// 这是错误的语法
3. 剩余参数为空
typescript
const commander = new Commander('command [...rest]');
// 没有剩余参数时
const segments = [{ type: 'text', data: { text: 'command' } }];
const result = commander.match(segments);
// result[0] = { rest: [] }
下一步
💡 提示
剩余参数是处理可变长度输入的有力工具,特别适用于批量操作和消息转发等场景。