#!/usr/bin/env node /** * سكريبت تشفير ملفات الإضافة المتوافق مع Manifest V3 * bridge.source.js → تشفير متوسط-قوي (أقل من الشديد بقليل) * background.source.js → إعدادات خفيفة جداً (دون تغيير كبير) */ const JavaScriptObfuscator = require('javascript-obfuscator'); const fs = require('fs'); const path = require('path'); const zlib = require('zlib'); const EXT_DIR = __dirname; // ========== إعدادات خفيفة جداً (لـ background) ========== const LIGHT_OPTIONS = { compact: true, selfDefending: false, debugProtection: false, debugProtectionInterval: 0, renameGlobals: false, renameProperties: false, transformObjectKeys: false, controlFlowFlattening: false, deadCodeInjection: false, stringArray: false, unicodeEscapeSequence: false, numbersToExpressions: false, identifierNamesGenerator: 'mangled', target: 'browser', seed: 42, sourceMap: false, }; // ========== إعدادات تشفير متوسط-قوي (لـ bridge - أقل من الشديد بقليل) ========== const MEDIUM_STRONG_OPTIONS = { compact: true, selfDefending: true, // حماية ذاتية (خفيفة، لا تسبب مشاكل في MV3) debugProtection: false, // تم تعطيلها لتجنب أي تباطؤ ملحوظ debugProtectionInterval: 0, renameGlobals: false, renameProperties: false, transformObjectKeys: true, // تعقيد مفاتيح الكائنات controlFlowFlattening: true, // تشويش تدفق التحكم (بقيمة منخفضة) controlFlowFlatteningThreshold: 0.5, // 50% فقط من الكود يتأثر deadCodeInjection: false, // تعطيل حقن الكود الميت (يقلل الحجم) stringArray: true, // تشفير النصوص stringArrayThreshold: 0.5, // 50% فقط من النصوص stringArrayEncoding: [], // بدون تشفير إضافي (أسرع) stringArrayIndexShift: false, // تعطيل إزاحة المؤشر splitStrings: false, // تعطيل تقسيم النصوص unicodeEscapeSequence: false, // تعطيل يونيكود (يقلل الحجم) numbersToExpressions: false, // تعطيل تحويل الأرقام identifierNamesGenerator: 'mangled-shuffled', target: 'browser', seed: 999, sourceMap: false, }; // ========== تشفير ملف مع خيارات ========== function obfuscateFile(sourceFile, outputFile, label, options) { if (!fs.existsSync(sourceFile)) { console.error(`❌ خطأ: لم يتم العثور على الملف المصدري: ${sourceFile}`); process.exit(1); } const src = fs.readFileSync(sourceFile, 'utf8'); console.log(`📄 ${label} — الأصلي: ${(src.length / 1024).toFixed(1)} KB`); const result = JavaScriptObfuscator.obfuscate(src, options).getObfuscatedCode(); fs.writeFileSync(outputFile, result, 'utf8'); console.log(`🔐 ${label} — المشفر: ${(result.length / 1024).toFixed(1)} KB`); return result; } // ========== background بتشفير خفيف جداً ========== obfuscateFile( path.join(EXT_DIR, 'src', 'background.source.js'), path.join(EXT_DIR, 'background.js'), 'background.js', LIGHT_OPTIONS ); // ========== bridge بتشفير متوسط-قوي (أقل من الشديد) ========== obfuscateFile( path.join(EXT_DIR, 'src', 'bridge.source.js'), path.join(EXT_DIR, 'bridge.js'), 'bridge.js', MEDIUM_STRONG_OPTIONS ); console.log('✅ اكتمل تشفير الملفين (bridge بتشفير متوسط-قوي، background خفيف)'); // ========== بناء ZIP (بدون تعديل) ========== function crc32(buf) { const table = (() => { const t = new Uint32Array(256); for (let i = 0; i < 256; i++) { let c = i; for (let j = 0; j < 8; j++) c = (c & 1) ? 0xEDB88320 ^ (c >>> 1) : c >>> 1; t[i] = c; } return t; })(); let crc = 0xFFFFFFFF; for (const b of buf) crc = table[(crc ^ b) & 0xFF] ^ (crc >>> 8); return (crc ^ 0xFFFFFFFF) >>> 0; } const files = ['manifest.json', 'background.js', 'bridge.js', 'icon-128.png']; const parts = []; const centralDir = []; let offset = 0; for (const file of files) { const filePath = path.join(EXT_DIR, file); if (!fs.existsSync(filePath)) { console.error(`❌ خطأ: الملف المطلوب للـ ZIP مفقود: ${filePath}`); process.exit(1); } const data = fs.readFileSync(filePath); const compressed = zlib.deflateRawSync(data, { level: 6 }); const crc = crc32(data); const nameBytes = Buffer.from(file, 'utf8'); const now = new Date(); const dosDate = ((now.getFullYear() - 1980) << 9) | ((now.getMonth() + 1) << 5) | now.getDate(); const dosTime = (now.getHours() << 11) | (now.getMinutes() << 5) | (now.getSeconds() >> 1); const localHeader = Buffer.alloc(30 + nameBytes.length); localHeader.writeUInt32LE(0x04034b50, 0); localHeader.writeUInt16LE(20, 4); localHeader.writeUInt16LE(0, 6); localHeader.writeUInt16LE(8, 8); localHeader.writeUInt16LE(dosTime, 10); localHeader.writeUInt16LE(dosDate, 12); localHeader.writeUInt32LE(crc, 14); localHeader.writeUInt32LE(compressed.length, 18); localHeader.writeUInt32LE(data.length, 22); localHeader.writeUInt16LE(nameBytes.length, 26); localHeader.writeUInt16LE(0, 28); nameBytes.copy(localHeader, 30); const centralEntry = Buffer.alloc(46 + nameBytes.length); centralEntry.writeUInt32LE(0x02014b50, 0); centralEntry.writeUInt16LE(20, 4); centralEntry.writeUInt16LE(20, 6); centralEntry.writeUInt16LE(0, 8); centralEntry.writeUInt16LE(8, 10); centralEntry.writeUInt16LE(dosTime, 12); centralEntry.writeUInt16LE(dosDate, 14); centralEntry.writeUInt32LE(crc, 16); centralEntry.writeUInt32LE(compressed.length, 20); centralEntry.writeUInt32LE(data.length, 24); centralEntry.writeUInt16LE(nameBytes.length, 28); centralEntry.writeUInt16LE(0, 30); centralEntry.writeUInt16LE(0, 32); centralEntry.writeUInt16LE(0, 34); centralEntry.writeUInt16LE(0, 36); centralEntry.writeUInt32LE(0, 38); centralEntry.writeUInt32LE(offset, 42); nameBytes.copy(centralEntry, 46); parts.push(localHeader, compressed); centralDir.push(centralEntry); offset += localHeader.length + compressed.length; } const centralDirBuf = Buffer.concat(centralDir); const eocd = Buffer.alloc(22); eocd.writeUInt32LE(0x06054b50, 0); eocd.writeUInt16LE(0, 4); eocd.writeUInt16LE(0, 6); eocd.writeUInt16LE(files.length, 8); eocd.writeUInt16LE(files.length, 10); eocd.writeUInt32LE(centralDirBuf.length, 12); eocd.writeUInt32LE(offset, 16); eocd.writeUInt16LE(0, 20); const zip = Buffer.concat([...parts, centralDirBuf, eocd]); const zipPath = path.join(EXT_DIR, 'fb-publisher-bridge.zip'); fs.writeFileSync(zipPath, zip); console.log(`📦 ZIP المحدث: ${(zip.length / 1024).toFixed(1)} KB → ${zipPath}`); console.log('🎉 اكتمل التشفير المتوافق وبناء ZIP بنجاح!');