码桶

发现社区成员的开源项目

奶狗 /

ng-webot

公开
main
ng-webot/upload-test/calc-plugin.js
calc-plugin.js3.6 KB
/**
 * 智能计算器插件(测试用)
 * - 对接智能助手:声明 aiTools.calculator,AI 在对话中遇到数学计算会自动调用
 * - 文本指令:「计算 1+2*3」或「calc 1+2*3」
 */
let PRECISION = 2; // 默认小数位,可被插件设置覆盖

// 尝试读取该机器人在「插件设置」里配置的精度(plugin_id 取 meta.id)
async function loadPrecision(botId) {
  try {
    const db = require('../../../lib/db');
    const r = await db.row(
      'SELECT data FROM plugin_settings WHERE plugin_id=? AND bot_id=? LIMIT 1',
      ['calc', botId || 0]
    );
    if (r && r.data) {
      const obj = JSON.parse(r.data);
      if (obj && typeof obj.precision === 'number') PRECISION = obj.precision;
    }
  } catch (_) {
    /* 读取失败则用默认精度,不影响计算 */
  }
}

// 安全计算:仅允许数字与基本运算符,避免 eval 注入
function safeCalc(expr) {
  if (!expr) return { error: '表达式为空' };
  if (!/^[0-9+\-*/%.\s]+$/.test(expr)) return { error: '仅支持数字与 + - * / % ( ) 运算' };
  try {
    // eslint-disable-next-line no-new-func
    const v = Function('"use strict"; return (' + expr + ')')();
    if (typeof v !== 'number' || !isFinite(v)) return { error: '表达式无效或结果非数值' };
    const f = Math.pow(10, Math.min(10, Math.max(0, PRECISION | 0)));
    return { value: Math.round(v * f) / f };
  } catch (e) {
    return { error: '计算错误:' + e.message };
  }
}

module.exports = {
  meta: {
    id: 'calc',
    name: '智能计算器',
    version: '1.0.0',
    author: 'test',
    category: '实用工具',
    description: '对接智能助手的计算器:在对话里让 AI 帮你算账/算数时会自动调用;也支持「计算 1+2*3」文本指令。',
    usage: '「计算 1+2*3」或在对聊中让 AI 计算',
    entry: 'index.js',
    configurable: true,
    settingsSchema: [
      { key: 'precision', label: '小数位数', type: 'number', placeholder: '2', help: '计算结果保留的小数位数(0-10)' }
    ],
    aiTools: [
      {
        type: 'function',
        function: {
          name: 'calculator',
          description: '计算一个数学表达式,支持 + - * / % 和括号。当用户需要算账、算数、比较数值时使用。',
          parameters: {
            type: 'object',
            properties: {
              expression: { type: 'string', description: '要计算的数学表达式,例如 "1+2*3" 或 "(100-30)/7"' }
            },
            required: ['expression']
          }
        }
      }
    ]
  },

  // 供智能助手(function calling)调用
  async handleAiTool(name, args, ctx) {
    if (name !== 'calculator') return '未知工具:' + name;
    if (ctx && ctx.bot && ctx.bot.id) {
      try { await loadPrecision(ctx.bot.id); } catch (_) {}
    }
    const expr = String(args.expression || '').trim();
    const r = safeCalc(expr);
    if (r.error) return '计算失败:' + r.error;
    return `${expr} = ${r.value}`;
  },

  async onMessage(msg, ctx) {
    const text = (msg.content || '').trim();
    if (text.startsWith('计算 ') || /^calc\s/i.test(text)) {
      if (ctx && ctx.bot && ctx.bot.id) {
        try { await loadPrecision(ctx.bot.id); } catch (_) {}
      }
      const expr = text.slice(text.indexOf(' ') + 1).trim();
      const r = safeCalc(expr);
      if (r.error) {
        await ctx.sendText('⚠️ ' + r.error);
        return true;
      }
      await ctx.sendText(`🧮 ${expr} = ${r.value}`);
      return true;
    }
    return false;
  }
};