码桶

发现社区成员的开源项目

奶狗 /

ng-webot

公开
index.js8.9 KB
/**
 * 插件:三角洲每日密码(delta-password)
 * --------------------------------------------------
 * 三角洲行动(Delta Force)各地图每日密码房密码查询。
 *
 * 命令:
 *   - 「三角洲」/「三角洲密码」/「今日密码」  → 返回全部地图今日密码汇总(含位置描述)
 *   - 「三角洲图 <地图名>」                  → 发送该地图的位置图片(模糊匹配地图名)
 *
 * 数据源:https://tmini.net/api/sjzmm (返回纯文本,每天更新一次,缓存 30 分钟)
 */
const axios = require('axios');
const path = require('path');
const fs = require('fs');

const API_URL = 'https://tmini.net/api/sjzmm';
const CACHE_TTL = 30 * 60 * 1000; // 30 分钟缓存
const TMP_DIR = path.join(__dirname, '..', '..', 'data', 'tmp');

let cache = null; // { data: { updateDate, maps }, time: timestamp }

// 解析接口返回的纯文本为结构化数据
function parsePasswords(text) {
  const lines = String(text).split(/\r?\n/);
  let updateDate = '';
  const maps = [];
  let cur = null;
  let imgMode = false;

  for (const raw of lines) {
    const line = raw.trim();
    if (!line) continue;
    if (line.startsWith('更新日期')) { updateDate = line.replace(/^更新日期[::]?/, '').replace(/每日密码已更新$/, '').trim(); continue; }
    if (line.startsWith('密码总数')) { imgMode = false; continue; }
    if (line.startsWith('---')) { imgMode = false; continue; }
    if (line.startsWith('地图名称')) {
      cur = { name: line.replace(/^地图名称[::]?/, '').trim(), pwd: '', desc: '', images: [] };
      maps.push(cur);
      imgMode = false;
      continue;
    }
    if (!cur) continue;
    if (line.startsWith('密码')) { cur.pwd = line.replace(/^密码[::]?/, '').trim(); continue; }
    if (line.startsWith('位置描述')) { cur.desc = line.replace(/^位置描述[::]?/, '').trim(); continue; }
    if (line.startsWith('位置图片')) { imgMode = true; continue; }
    if (imgMode) {
      const m = line.match(/https?:\/\/\S+/);
      if (m) cur.images.push(m[0]);
      continue;
    }
  }
  return { updateDate, maps };
}

async function fetchData() {
  if (cache && Date.now() - cache.time < CACHE_TTL) return cache.data;
  const { data } = await axios.get(API_URL, {
    timeout: 12000,
    responseType: 'text',
    headers: { 'User-Agent': 'Mozilla/5.0 (compatible; DeltaBot/1.0)' },
  });
  const parsed = parsePasswords(data);
  if (!parsed.maps.length) throw new Error('接口无数据');
  cache = { data: parsed, time: Date.now() };
  return parsed;
}

// 拼装密码汇总文本
function buildText({ updateDate, maps }) {
  const lines = [];
  lines.push('🎯 三角洲行动 · 今日密码');
  if (updateDate) lines.push('📅 ' + updateDate);
  lines.push('');
  for (const m of maps) {
    lines.push(`【${m.name}】密码:${m.pwd}`);
    if (m.desc) lines.push('  ' + m.desc);
    lines.push('');
  }
  lines.push('———');
  lines.push('发送「三角洲图 地图名」查看位置图(如:三角洲图 零号大坝)');
  return lines.join('\n').trim();
}

// 地图别名,用于自然语言里按关键词匹配具体地图
const MAP_ALIASES = [
  { keys: ['航天基地', '航天'], name: '航天基地' },
  { keys: ['零号大坝', '零号', '大坝'], name: '零号大坝' },
  { keys: ['长弓溪谷', '长弓', '溪谷'], name: '长弓溪谷' },
  { keys: ['AZ3', 'az3', '核电站'], name: 'AZ3核电站' },
  { keys: ['巴克什'], name: '巴克什' },
  { keys: ['彩六', '联动房'], name: '彩六联动房' },
  { keys: ['潮汐', '监狱'], name: '潮汐监狱' },
];

function findMap(text, maps) {
  // 先按别名关键词匹配
  for (const a of MAP_ALIASES) {
    if (a.keys.some((k) => text.includes(k))) {
      const hit = maps.find((m) => m.name === a.name)
        || maps.find((m) => m.name.includes(a.name) || a.name.includes(m.name));
      if (hit) return hit;
    }
  }
  // 再按地图全名匹配
  return maps.find((m) => text.includes(m.name)) || null;
}

// 下载图片到临时目录并发送,发送后清理
async function sendImages(ctx, urls) {
  if (!fs.existsSync(TMP_DIR)) fs.mkdirSync(TMP_DIR, { recursive: true });
  for (let i = 0; i < urls.length; i++) {
    const url = urls[i];
    let tmpPath = '';
    try {
      const ext = (url.split('?')[0].split('.').pop() || 'jpg').toLowerCase();
      const safeExt = /^(jpg|jpeg|png|gif|webp)$/.test(ext) ? ext : 'jpg';
      tmpPath = path.join(TMP_DIR, 'delta_' + Date.now() + '_' + i + '.' + safeExt);
      const resp = await axios.get(url, {
        timeout: 15000,
        responseType: 'arraybuffer',
        headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' },
      });
      const buf = Buffer.from(resp.data);
      if (buf.length < 100) continue;
      fs.writeFileSync(tmpPath, buf);
      await ctx.sendMedia(tmpPath, 'image');
    } catch (e) {
      console.warn('[delta-password] 图片发送失败:', url, e.message);
    } finally {
      if (tmpPath) { try { fs.unlinkSync(tmpPath); } catch (_) {} }
    }
  }
}

module.exports = {
  meta: {
    id: 'delta-password',
    name: '三角洲每日密码',
    version: '1.0.0',
    author: '奶狗',
    category: '信息获取',
    description: '发送「三角洲」查询三角洲行动各地图今日密码房密码;「三角洲图 地图名」查看位置图。也支持自然语言,如「看一下航天基地密码」。',
    entry: 'delta-password/index.js',
    configurable: false,
    commandPrefix: ['三角洲', '今日密码'],
    // 柔性匹配:含「密码」的自然语言(如「看一下航天基地密码」)也让位给本插件,不走 AI
    commandMatch: '三角洲|今日密码|密码',
  },

  async onMessage(msg, ctx) {
    const text = (msg.content || '').trim();

    // 1) 查看位置图:三角洲图 <地图名> / 图 <地图名> / <地图名>图
    let imgQ = null;
    const m1 = text.match(/^三角洲图\s*(.+)$/);
    if (m1) imgQ = m1[1].trim();
    else if (text.startsWith('图 ')) imgQ = text.slice(2).trim();
    else {
      const m3 = text.match(/^(.+?)图$/);
      if (m3) imgQ = m3[1].trim();
    }
    if (imgQ) {
      if (!imgQ) { await ctx.sendText('用法:三角洲图 地图名(如:三角洲图 零号大坝)'); return true; }
      try {
        const data = await fetchData();
        const hit = data.maps.find((m) => m.name === imgQ)
          || data.maps.find((m) => m.name.includes(imgQ) || imgQ.includes(m.name));
        if (!hit) {
          await ctx.sendText('未找到地图「' + imgQ + '」。可选:' + data.maps.map((m) => m.name).join('、'));
          return true;
        }
        if (!hit.images.length) {
          await ctx.sendText('【' + hit.name + '】密码:' + hit.pwd + '\n暂无位置图片。' + (hit.desc ? '\n' + hit.desc : ''));
          return true;
        }
        await ctx.sendText('【' + hit.name + '】密码:' + hit.pwd + (hit.desc ? '\n' + hit.desc : ''));
        await sendImages(ctx, hit.images);
      } catch (e) {
        console.error('[delta-password] 出错:', e.message);
        await ctx.sendText('三角洲密码获取失败,请稍后再试。');
      }
      return true;
    }

    // 2) 查询全部密码汇总
    if (text === '三角洲' || text === '三角洲密码' || text === '今日密码' || text === '三角洲行动') {
      try {
        const data = await fetchData();
        await ctx.sendText(buildText(data));
      } catch (e) {
        console.error('[delta-password] 出错:', e.message);
        await ctx.sendText('三角洲密码获取失败,请稍后再试。');
      }
      return true;
    }

    // 3) 自然语言密码查询:含「密码」二字(如「看一下航天基地密码」「航天基地的密码是多少」)
    if (text.includes('密码')) {
      try {
        const data = await fetchData();
        const hit = findMap(text, data.maps);
        const wantsImg = text.includes('图');
        if (hit) {
          let out = `【${hit.name}】密码:${hit.pwd}`;
          if (hit.desc) out += '\n' + hit.desc;
          if (wantsImg && hit.images.length) {
            await ctx.sendText(out);
            await sendImages(ctx, hit.images);
            return true;
          }
          if (hit.images.length) out += '\n(发送「三角洲图 ' + hit.name + '」可查看位置图)';
          await ctx.sendText(out);
        } else {
          await ctx.sendText(buildText(data));
        }
      } catch (e) {
        console.error('[delta-password] 出错:', e.message);
        await ctx.sendText('三角洲密码获取失败,请稍后再试。');
      }
      return true;
    }

    return false;
  },

  // 导出解析函数(便于单测/调试;插件系统只读 meta/onMessage)
  parsePasswords,
};