← back to browse
goat command

e2ee

No description

0
views
0
likes
0
installs
raw source
/**
 * e2ee.js — ported from Mari-v4's modules/commands/e2ee.js
 *
 * Purpose: interactive E2EE (Labyrinth encrypted chat) bridge test + an
 * "info" explainer for how the bot tells a normal inbox DM apart from an
 * E2EE inbox DM apart from an E2EE group.
 *
 * Detection logic (identical to Mari-v4's handleCommand.js / e2ee.js):
 *   - isE2EEThread : threadID is a JID string, i.e. it contains "@".
 *                    Normal (non-E2EE) threads are plain numeric IDs.
 *   - isE2EEDM     : isE2EEThread && !event.isGroup — by the time a
 *                    command runs, event.isGroup has already been
 *                    corrected against the DB (see shared.js buildContext,
 *                    "resolvedIsGroup"), so this is reliable even though
 *                    the raw MQTT guess for a brand-new E2EE JID can't
 *                    always tell a DM from a group on its own.
 *   - normal inbox : senderID === threadID (Messenger's own convention
 *                    for 1-to-1 threads — not a JID, no "@").
 *
 * Gating: this project already has a config-level inbox switch —
 * global.GoatBot.config.antiInbox (toggled via the `security` command,
 * {pn}security antiinbox on/off) — and includes/listen.js already blocks
 * *all* non-group traffic (normal DM + E2EE DM) at the event level when
 * antiInbox is true. That's the equivalent of Mari-v4's `allowInbox`
 * flag, just inverted (antiInbox: true == allowInbox: false) and
 * enforced earlier in the pipeline. So this command does NOT introduce a
 * second config key — it reads the same global.GoatBot.config.antiInbox
 * that security.js already writes, and adds a silent isE2EEDM-specific
 * check as defense-in-depth for anyone who calls the command directly.
 */

module.exports = {
	config: {
		name: "e2ee",
		version: "1.0.0",
		author: "rX",
		countDown: 5,
		role: 0,
		description: {
			vi: "Kiểm tra cầu nối E2EE (mã hóa đầu cuối) và xem cách bot phân biệt inbox thường / inbox E2EE.",
			en: "Test the E2EE (end-to-end encrypted) bridge and see how the bot tells normal inbox apart from E2EE inbox."
		},
		category: "system",
		guide: {
			vi:
`   {pn}: chạy bài test tương tác (chỉ hoạt động trong đoạn chat E2EE)
   {pn} info: giải thích cách bot nhận diện inbox thường / E2EE
   {pn} status: xem trạng thái E2EE + Anti-Inbox hiện tại
   Dùng {pn}security e2ee/antiinbox on|off để bật/tắt (chỉ bot admin).`,
			en:
`   {pn}: run the interactive bridge test (only works inside E2EE chats)
   {pn} info: explain how the bot detects normal inbox vs E2EE inbox
   {pn} status: show current E2EE + Anti-Inbox state
   Use {pn}security e2ee/antiinbox on|off to toggle (bot admin only).`
		}
	},

	langs: {
		vi: {
			testMsg: "🔒 Thử nghiệm E2EE\n━━━━━━━━━━━━━━━━\nTin nhắn của bạn đã được mã hóa đầu cuối.\n\nPhản hồi bằng số:\n1️⃣  1 — Thử nghiệm Ping\n2️⃣  2 — Thông tin luồng chat",
			reply1: "✅ Pong! Cầu nối E2EE đang hoạt động hoàn hảo.",
			reply2: "📋 Thông tin luồng chat E2EE\n━━━━━━━━━━━━━━━━\n• Thread: %1\n• Mã hóa: ✅ Có\n• Giao thức: Labyrinth\n• Cầu nối: Hoạt động",
			replyOther: "❓ Tùy chọn không xác định. Vui lòng phản hồi bằng 1 hoặc 2.",
			e2eeDisabled: "⚠️ Tính năng E2EE hiện đang bị tắt trong config.json (e2ee.enable). Dùng {pn}security e2ee on để bật (cần khởi động lại bot).",
			notE2EE: "⚠️ Lệnh này chỉ hoạt động trong các cuộc trò chuyện mã hóa đầu cuối (E2EE).",
			statusText: "🔐 E2EE: %1\n📥 Anti-Inbox: %2",
			groupExplainer: "💡 Cách bot phân biệt các loại inbox:\n1️⃣ Inbox thường (DM): senderID == threadID (không có \"@\").\n2️⃣ E2EE inbox (DM): threadID là JID (chứa \"@\") và event.isGroup = false.\n3️⃣ E2EE nhóm: threadID là JID (chứa \"@\") nhưng event.isGroup = true — KHÔNG bị chặn bởi Anti-Inbox.\n\nKhi Anti-Inbox (antiInbox) đang BẬT, cả (1) và (2) đều bị bot bỏ qua hoàn toàn — chỉ (3) và các nhóm thường vẫn hoạt động."
		},
		en: {
			testMsg: "🔒 E2EE Test\n━━━━━━━━━━━━━━━━\nYour message is end-to-end encrypted.\n\nReply with a number:\n1️⃣  1 — Ping test\n2️⃣  2 — Thread info",
			reply1: "✅ Pong! E2EE bridge is working perfectly.",
			reply2: "📋 E2EE Thread Info\n━━━━━━━━━━━━━━━━\n• Thread: %1\n• Encrypted: ✅ Yes\n• Protocol: Labyrinth\n• Bridge: Active",
			replyOther: "❓ Unknown option. Reply with 1 or 2.",
			e2eeDisabled: "⚠️ E2EE is currently disabled in config.json (e2ee.enable). Use {pn}security e2ee on to enable it (bot restart required).",
			notE2EE: "⚠️ This command only works in E2EE (end-to-end encrypted) chats.",
			statusText: "🔐 E2EE: %1\n📥 Anti-Inbox: %2",
			groupExplainer: "💡 How the bot tells inbox types apart:\n1️⃣ Normal inbox (DM): senderID == threadID (no \"@\").\n2️⃣ E2EE inbox (DM): threadID is a JID (contains \"@\") and event.isGroup = false.\n3️⃣ E2EE group: threadID is a JID (contains \"@\") but event.isGroup = true — NEVER blocked by Anti-Inbox.\n\nWhen Anti-Inbox is ON, both (1) and (2) are silently ignored by the bot — only (3) and normal groups keep working."
		}
	},

	onStart: async function ({ api, args, event, message, getLang }) {
		const { threadID, messageID, senderID } = event;
		const sub = (args[0] || "").toLowerCase();

		if (sub === "info") {
			return message.reply(getLang("groupExplainer"));
		}

		if (sub === "status") {
			const config = global.GoatBot.config;
			const e2eeState = config.e2ee?.enable === true ? "ON ✅" : "OFF ❌";
			const antiInboxState = config.antiInbox === true ? "ON ✅ (inbox blocked)" : "OFF ❌ (inbox allowed)";
			return message.reply(getLang("statusText", e2eeState, antiInboxState));
		}

		// ── Interactive bridge test ──────────────────────────────────────────
		const e2eeEnabled = global.GoatBot.config.e2ee?.enable === true;
		if (!e2eeEnabled) return message.reply(getLang("e2eeDisabled"));

		// A JID threadID (contains "@") means an E2EE (Labyrinth) chat —
		// normal Facebook thread IDs are plain numeric strings.
		const isE2EEThread = typeof threadID === "string" && threadID.includes("@");
		if (!isE2EEThread) return message.reply(getLang("notE2EE"));

		// event.isGroup here is already the DB-resolved value (buildContext
		// in shared.js corrects it against threadData before any command
		// runs), so this reliably tells an E2EE 1-1 DM apart from an E2EE
		// group even though the raw MQTT guess alone can't.
		const isE2EEDM = isE2EEThread && !event.isGroup;
		if (isE2EEDM && global.GoatBot.config.antiInbox === true) {
			return; // Anti-Inbox is ON → silently ignore E2EE inbox, same as normal inbox
		}

		await api.setMessageReaction("🔒", messageID, () => {}, true).catch(() => {});

		try {
			await api.sendTypingIndicator(true, threadID, () => {}).catch(() => {});
			await new Promise(resolve => setTimeout(resolve, 5000));
			await api.sendTypingIndicator(false, threadID, () => {}).catch(() => {});
		} catch (_) {
			await new Promise(resolve => setTimeout(resolve, 5000));
		}

		return api.sendMessage(getLang("testMsg"), threadID, (err, info) => {
			if (info && info.messageID) {
				global.GoatBot.onReply.set(info.messageID, {
					commandName: "e2ee",
					messageID: info.messageID,
					author: senderID,
					threadID
				});
			}
		}, messageID);
	},

	onReply: async function ({ api, event, Reply, getLang }) {
		if (String(event.senderID) !== String(Reply.author)) return;
		Reply.delete && Reply.delete();

		const choice = (event.body || "").trim();
		const { threadID, messageID } = event;

		if (choice === "1") return api.sendMessage(getLang("reply1"), threadID, messageID);
		if (choice === "2") return api.sendMessage(getLang("reply2", threadID), threadID, messageID);
		return api.sendMessage(getLang("replyOther"), threadID, messageID);
	}
};

View raw file