Upload 181 files
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- artifacts/fb-publisher/.replit-artifact/artifact.toml +23 -0
- artifacts/fb-publisher/components.json +20 -0
- artifacts/fb-publisher/dist/public/assets/index-CBpzG2YA.css +0 -0
- artifacts/fb-publisher/dist/public/assets/index-oEk3pbMd.js +0 -0
- artifacts/fb-publisher/dist/public/extension/background.js +1 -0
- artifacts/fb-publisher/dist/public/extension/bridge.js +1 -0
- artifacts/fb-publisher/dist/public/extension/fb-publisher-bridge.zip +3 -0
- artifacts/fb-publisher/dist/public/extension/icon-128.png +0 -0
- artifacts/fb-publisher/dist/public/extension/manifest.json +43 -0
- artifacts/fb-publisher/dist/public/extension/popup.html +162 -0
- artifacts/fb-publisher/dist/public/extension/popup.js +82 -0
- artifacts/fb-publisher/dist/public/extension/src/.gitignore +2 -0
- artifacts/fb-publisher/dist/public/extension/src/background.source.js +678 -0
- artifacts/fb-publisher/dist/public/extension/src/bridge.source.js +74 -0
- artifacts/fb-publisher/dist/public/favicon.png +0 -0
- artifacts/fb-publisher/dist/public/favicon.svg +3 -0
- artifacts/fb-publisher/dist/public/index.html +18 -0
- artifacts/fb-publisher/dist/public/logo.png +0 -0
- artifacts/fb-publisher/dist/public/logo.svg +10 -0
- artifacts/fb-publisher/dist/public/opengraph.jpg +0 -0
- artifacts/fb-publisher/dist/public/robots.txt +2 -0
- artifacts/fb-publisher/index.html +17 -0
- artifacts/fb-publisher/package.json +83 -0
- artifacts/fb-publisher/public/extension/background.js +1 -0
- artifacts/fb-publisher/public/extension/bridge.js +1 -0
- artifacts/fb-publisher/public/extension/fb-publisher-bridge.zip +3 -0
- artifacts/fb-publisher/public/extension/icon-128.png +0 -0
- artifacts/fb-publisher/public/extension/manifest.json +43 -0
- artifacts/fb-publisher/public/extension/popup.html +162 -0
- artifacts/fb-publisher/public/extension/popup.js +82 -0
- artifacts/fb-publisher/public/extension/src/.gitignore +2 -0
- artifacts/fb-publisher/public/extension/src/background.source.js +678 -0
- artifacts/fb-publisher/public/extension/src/bridge.source.js +118 -0
- artifacts/fb-publisher/public/favicon.png +0 -0
- artifacts/fb-publisher/public/favicon.svg +3 -0
- artifacts/fb-publisher/public/logo.png +0 -0
- artifacts/fb-publisher/public/logo.svg +10 -0
- artifacts/fb-publisher/public/opengraph.jpg +0 -0
- artifacts/fb-publisher/public/robots.txt +2 -0
- artifacts/fb-publisher/src/App.tsx +281 -0
- artifacts/fb-publisher/src/components/UpdateBanner.tsx +125 -0
- artifacts/fb-publisher/src/components/ui/accordion.tsx +55 -0
- artifacts/fb-publisher/src/components/ui/alert-dialog.tsx +139 -0
- artifacts/fb-publisher/src/components/ui/alert.tsx +59 -0
- artifacts/fb-publisher/src/components/ui/aspect-ratio.tsx +5 -0
- artifacts/fb-publisher/src/components/ui/avatar.tsx +50 -0
- artifacts/fb-publisher/src/components/ui/badge.tsx +43 -0
- artifacts/fb-publisher/src/components/ui/breadcrumb.tsx +115 -0
- artifacts/fb-publisher/src/components/ui/button-group.tsx +83 -0
- artifacts/fb-publisher/src/components/ui/button.tsx +65 -0
artifacts/fb-publisher/.replit-artifact/artifact.toml
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
kind = "web"
|
| 2 |
+
previewPath = "/__dev-fb"
|
| 3 |
+
title = "FB Publisher (Dev Only)"
|
| 4 |
+
version = "1.0.0"
|
| 5 |
+
id = "artifacts/fb-publisher"
|
| 6 |
+
router = "path"
|
| 7 |
+
|
| 8 |
+
[[integratedSkills]]
|
| 9 |
+
name = "react-vite"
|
| 10 |
+
version = "1.0.0"
|
| 11 |
+
|
| 12 |
+
[[services]]
|
| 13 |
+
name = "web"
|
| 14 |
+
paths = []
|
| 15 |
+
localPort = 18305
|
| 16 |
+
|
| 17 |
+
[services.development]
|
| 18 |
+
run = "pnpm --filter @workspace/fb-publisher run dev"
|
| 19 |
+
|
| 20 |
+
[services.env]
|
| 21 |
+
PORT = "18305"
|
| 22 |
+
BASE_PATH = "/"
|
| 23 |
+
|
artifacts/fb-publisher/components.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"$schema": "https://ui.shadcn.com/schema.json",
|
| 3 |
+
"style": "new-york",
|
| 4 |
+
"rsc": false,
|
| 5 |
+
"tsx": true,
|
| 6 |
+
"tailwind": {
|
| 7 |
+
"config": "",
|
| 8 |
+
"css": "src/index.css",
|
| 9 |
+
"baseColor": "neutral",
|
| 10 |
+
"cssVariables": true,
|
| 11 |
+
"prefix": ""
|
| 12 |
+
},
|
| 13 |
+
"aliases": {
|
| 14 |
+
"components": "@/components",
|
| 15 |
+
"utils": "@/lib/utils",
|
| 16 |
+
"ui": "@/components/ui",
|
| 17 |
+
"lib": "@/lib",
|
| 18 |
+
"hooks": "@/hooks"
|
| 19 |
+
}
|
| 20 |
+
}
|
artifacts/fb-publisher/dist/public/assets/index-CBpzG2YA.css
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
artifacts/fb-publisher/dist/public/assets/index-oEk3pbMd.js
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
artifacts/fb-publisher/dist/public/extension/background.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
const DEFAULT_APP_URL='https://social-media-bridge-s2555.replit.app/app';function bgLogError(_0x24e1e7,_0x445805,_0x623bd3={}){console['error']('❌\x20[BG]['+_0x24e1e7+']',_0x445805,_0x623bd3);}function bgLogInfo(_0x2f6de9,_0x425659,_0x2400a5={}){console['log']('✅\x20[BG]['+_0x2f6de9+']',_0x425659,_0x2400a5);}const USER_CONFIG={'userId':null,'doc_id_groups':'3884641628300421','doc_id_post_text':'3559434960802556','doc_id_post_photo':'25528973250111023','fb_dtsg':null,'lsd':null,'jazoest':'25415','upload_id':'jsc_c_9','targetGroupId':null};let publishCancelled=![],isPublishingNow=![],activePublishImages=[],appTabId=null,currentPublishTabId=null,resolveStorageReady;const storageReady=new Promise(_0x2637a7=>{resolveStorageReady=_0x2637a7;});chrome['storage']['local']['get'](['fb_dtsg','lsd','targetGroupId','publishCancelled','userId'],_0x4b579e=>{if(_0x4b579e['fb_dtsg'])USER_CONFIG['fb_dtsg']=_0x4b579e['fb_dtsg'];if(_0x4b579e['lsd'])USER_CONFIG['lsd']=_0x4b579e['lsd'];if(_0x4b579e['userId'])USER_CONFIG['userId']=_0x4b579e['userId'];if(_0x4b579e['targetGroupId'])USER_CONFIG['targetGroupId']=_0x4b579e['targetGroupId'];_0x4b579e['publishCancelled']&&(publishCancelled=_0x4b579e['publishCancelled'],chrome['storage']['local']['set']({'publishCancelled':![]})),bgLogInfo('init','تم\x20تحميل\x20البيانات\x20المخزنة'),resolveStorageReady();});function randomDelay(){return Math['floor'](Math['random']()*(0x95-0x3e+0x1))+0x3e;}function getFacebookUserId(){return new Promise(_0x2d8a66=>{chrome['cookies']['get']({'url':'https://www.facebook.com','name':'c_user'},_0x532d4e=>{_0x532d4e&&_0x532d4e['value']?(bgLogInfo('cookies','تم\x20العثور\x20على\x20c_user:\x20'+_0x532d4e['value']),_0x2d8a66(_0x532d4e['value'])):(bgLogInfo('cookies','لم\x20يتم\x20العثور\x20على\x20c_user\x20-\x20قد\x20لا\x20يكون\x20المستخدم\x20مسجل\x20الدخول'),_0x2d8a66(null));});});}function parseDtsgFromText(_0x352e66){let _0x6236ee=null,_0x971753=null,_0x375ee2=_0x352e66['match'](/DTSGInitialData[^{]*\{[^}]*"token"\s*:\s*"([^"]+)"/);if(_0x375ee2)_0x6236ee=_0x375ee2[0x1];if(!_0x6236ee){_0x375ee2=_0x352e66['match'](/\["fb_dtsg"\s*,\s*\[\]\s*,\s*\{\s*"token"\s*:\s*"([^"]+)"/);if(_0x375ee2)_0x6236ee=_0x375ee2[0x1];}if(!_0x6236ee){_0x375ee2=_0x352e66['match'](/"fb_dtsg"\s*:\s*"([A-Za-z0-9_\-:\.]{10,120})"/);if(_0x375ee2)_0x6236ee=_0x375ee2[0x1];}if(!_0x6236ee){_0x375ee2=_0x352e66['match'](/fb_dtsg["']\s*[,:]\s*["']([^"']{10,120})/);if(_0x375ee2)_0x6236ee=_0x375ee2[0x1];}if(!_0x6236ee){_0x375ee2=_0x352e66['match'](/DTSGInitializer\s*\(\s*\{[^}]*"token"\s*:\s*"([^"]+)"/);if(_0x375ee2)_0x6236ee=_0x375ee2[0x1];}_0x375ee2=_0x352e66['match'](/\["LSD"\s*,\s*\[\]\s*,\s*\{\s*"token"\s*:\s*"([^"]+)"/);if(_0x375ee2)_0x971753=_0x375ee2[0x1];if(!_0x971753){_0x375ee2=_0x352e66['match'](/"LSD"\s*,\s*\[\s*\]\s*,\s*\{\s*"token"\s*:\s*"([^"]+)"/);if(_0x375ee2)_0x971753=_0x375ee2[0x1];}return{'fb_dtsg':_0x6236ee,'lsd':_0x971753};}async function extractFbDtsg(_0x258cbb){return new Promise(_0x45919e=>{chrome['scripting']['executeScript']({'target':{'tabId':_0x258cbb},'func':()=>{let _0x514d7c=null,_0x381c4f=null;const _0x5aa70f=document['querySelector']('input[name=\x22fb_dtsg\x22]');if(_0x5aa70f&&_0x5aa70f['value'])_0x514d7c=_0x5aa70f['value'];const _0x1c5bf7=document['querySelector']('input[name=\x22lsd\x22]');if(_0x1c5bf7&&_0x1c5bf7['value'])_0x381c4f=_0x1c5bf7['value'];const _0x235787=document['documentElement']['innerHTML'];if(!_0x514d7c){let _0x24158e=_0x235787['match'](/DTSGInitialData[^{]*\{[^}]*"token"\s*:\s*"([^"]+)"/);if(_0x24158e)_0x514d7c=_0x24158e[0x1];}if(!_0x514d7c){let _0x269bc2=_0x235787['match'](/\["fb_dtsg"\s*,\s*\[\]\s*,\s*\{\s*"token"\s*:\s*"([^"]+)"/);if(_0x269bc2)_0x514d7c=_0x269bc2[0x1];}if(!_0x514d7c){let _0xd3123=_0x235787['match'](/"fb_dtsg"\s*:\s*"([A-Za-z0-9_\-:\.]{10,120})"/);if(_0xd3123)_0x514d7c=_0xd3123[0x1];}if(!_0x514d7c){const _0x52ce8a=document['querySelectorAll']('script');for(let _0x5ca0b7 of _0x52ce8a){const _0x219c3b=_0x5ca0b7['textContent'];if(!_0x219c3b)continue;let _0x5b3b59=_0x219c3b['match'](/DTSGInitialData[^}]*token["']\s*:\s*["']([^"']+)["']/);if(_0x5b3b59&&_0x5b3b59[0x1]){_0x514d7c=_0x5b3b59[0x1];break;}_0x5b3b59=_0x219c3b['match'](/fb_dtsg["']\s*:\s*["']([^"']+)["']/);if(_0x5b3b59&&_0x5b3b59[0x1]){_0x514d7c=_0x5b3b59[0x1];break;}_0x5b3b59=_0x219c3b['match'](/["']fb_dtsg["']\s*,\s*["']([^"']+)["']/);if(_0x5b3b59&&_0x5b3b59[0x1]){_0x514d7c=_0x5b3b59[0x1];break;}_0x5b3b59=_0x219c3b['match'](/DTSGInitializer[^)]*"token"\s*:\s*"([^"]+)"/);if(_0x5b3b59&&_0x5b3b59[0x1]){_0x514d7c=_0x5b3b59[0x1];break;}}}if(!_0x381c4f){let _0x34f649=_0x235787['match'](/\["LSD"\s*,\s*\[\]\s*,\s*\{\s*"token"\s*:\s*"([^"]+)"/);if(_0x34f649)_0x381c4f=_0x34f649[0x1];}if(!_0x381c4f){let _0xe7e267=_0x235787['match'](/["']LSD["']\s*,\s*\[\s*\]\s*,\s*\{\s*["']token["']\s*:\s*["']([^"']+)["']/);if(_0xe7e267)_0x381c4f=_0xe7e267[0x1];}return{'fb_dtsg':_0x514d7c,'lsd':_0x381c4f};}},_0x369f7e=>{if(_0x369f7e&&_0x369f7e[0x0]&&_0x369f7e[0x0]['result'])_0x45919e(_0x369f7e[0x0]['result']);else _0x45919e({'fb_dtsg':null,'lsd':null});});});}async function extractFbDtsgViaFetch(){try{bgLogInfo('fetchDtsg','محاولة\x20استخراج\x20DTSG\x20عبر\x20fetch...');const _0x182377=await fetch('https://www.facebook.com/',{'credentials':'include','headers':{'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','Accept-Language':'ar,en-US;q=0.9,en;q=0.8','Cache-Control':'no-cache'}});if(!_0x182377['ok'])return bgLogInfo('fetchDtsg','HTTP\x20error:\x20'+_0x182377['status']),{'fb_dtsg':null,'lsd':null};const _0x2afc96=await _0x182377['text'](),_0x3dbc1f=parseDtsgFromText(_0x2afc96);return bgLogInfo('fetchDtsg',_0x3dbc1f['fb_dtsg']?'✅\x20نجح\x20الاستخراج\x20عبر\x20fetch':'❌\x20لم\x20يُعثر\x20على\x20DTSG\x20في\x20fetch'),_0x3dbc1f;}catch(_0x10d493){return bgLogInfo('fetchDtsg','فشل\x20fetch:\x20'+_0x10d493['message']),{'fb_dtsg':null,'lsd':null};}}async function ensureFacebookAndCaptureKeys(){return new Promise(async _0x3fc6b0=>{bgLogInfo('autoCapture','بدء\x20عملية\x20التحقق\x20من\x20فيسبوك\x20واصطياد\x20المفاتيح\x20تلقائياً');const _0x123e56=await getFacebookUserId();if(!_0x123e56){_0x3fc6b0({'success':![],'error':'not_logged_in','message':'يجب\x20تسجيل\x20الدخول\x20إلى\x20فيسبوك\x20أولاً.\x20هل\x20تريد\x20فتح\x20فيسبوك\x20الآن؟'});return;}USER_CONFIG['userId']=_0x123e56,bgLogInfo('autoCapture','تم\x20التعرف\x20على\x20الحساب:\x20'+_0x123e56);const _0x143a37=await chrome['tabs']['query']({'url':['*://*.facebook.com/*','*://*.web.facebook.com/*']});let _0x3f33d4=null;if(_0x143a37['length']===0x0){bgLogInfo('autoCapture','لا\x20يوجد\x20تبويب\x20فيسبوك\x20مفتوح،\x20جاري\x20فتح\x20تبويب\x20جديد...');const _0xc738e0=await chrome['tabs']['create']({'url':'https://www.facebook.com','active':!![]});_0x3f33d4=_0xc738e0['id'],await new Promise(_0x57712a=>setTimeout(_0x57712a,0x1388));}else _0x3f33d4=_0x143a37[0x0]['id'],bgLogInfo('autoCapture','تم\x20العثور\x20على\x20تبويب\x20فيسبوك\x20مفتوح:\x20'+_0x3f33d4);let _0x226f0f=await extractFbDtsg(_0x3f33d4);if(!_0x226f0f['fb_dtsg']){bgLogInfo('autoCapture','المحاولة\x201\x20فشلت\x20—\x20جاري\x20استخراج\x20DTSG\x20عبر\x20fetch\x20مباشر...');const _0x3026f4=await extractFbDtsgViaFetch();if(_0x3026f4['fb_dtsg'])_0x226f0f=_0x3026f4,bgLogInfo('autoCapture','✅\x20نجح\x20استخراج\x20DTSG\x20عبر\x20fetch');else{bgLogInfo('autoCapture','المحاولة\x202\x20فشلت\x20—\x20جاري\x20تحديث\x20صفحة\x20فيسبوك...'),await chrome['tabs']['reload'](_0x3f33d4),await new Promise(_0x351209=>setTimeout(_0x351209,0x1770));const _0x5adbbb=await extractFbDtsg(_0x3f33d4);if(_0x5adbbb['fb_dtsg'])_0x226f0f=_0x5adbbb,bgLogInfo('autoCapture','✅\x20نجح\x20استخراج\x20DTSG\x20بعد\x20إعادة\x20التحميل');else{const _0xc9cb93=await extractFbDtsgViaFetch();if(!_0xc9cb93['fb_dtsg']){_0x3fc6b0({'success':![],'error':'no_fb_dtsg','message':'لم\x20يتم\x20العثور\x20على\x20مفاتيح\x20فيسبوك.\x20تأكد\x20من\x20أنك\x20مسجل\x20الدخول\x20في\x20فيسبوك\x20بنفس\x20المتصفح\x20وأن\x20الصفحة\x20محملة\x20بالكامل.'});return;}_0x226f0f=_0xc9cb93;}}}USER_CONFIG['fb_dtsg']=_0x226f0f['fb_dtsg'],USER_CONFIG['lsd']=_0x226f0f['lsd'],chrome['storage']['local']['set']({'fb_dtsg':_0x226f0f['fb_dtsg'],'lsd':_0x226f0f['lsd'],'userId':_0x123e56}),bgLogInfo('autoCapture','✅\x20تم\x20اصطياد\x20المفاتيح\x20بنجاح\x20تلقائياً'),_0x3fc6b0({'success':!![],'userId':_0x123e56,'tabId':_0x3f33d4});});}async function uploadImage(_0xac0cfb,_0x1d2148,_0x59a0d2){return new Promise(_0x474830=>{bgLogInfo('upload','رفع\x20الصورة:\x20'+_0x59a0d2),chrome['scripting']['executeScript']({'target':{'tabId':_0xac0cfb},'func':(_0x38b99f,_0x516758,_0x3d7ae7)=>{return new Promise(_0x506a82=>{try{const _0x3301d9=atob(_0x516758['split'](',')[0x1]),_0x4ed531=_0x516758['split'](',')[0x0]['split'](':')[0x1]['split'](';')[0x0],_0x3026b6=new ArrayBuffer(_0x3301d9['length']),_0x241b8e=new Uint8Array(_0x3026b6);for(let _0x5d3531=0x0;_0x5d3531<_0x3301d9['length'];_0x5d3531++)_0x241b8e[_0x5d3531]=_0x3301d9['charCodeAt'](_0x5d3531);const _0xc96fb0=new Blob([_0x3026b6],{'type':_0x4ed531}),_0x462d1c=new FormData();_0x462d1c['append']('fb_dtsg',_0x38b99f['fb_dtsg']),_0x462d1c['append']('profile_id',_0x38b99f['userId']),_0x462d1c['append']('source','8'),_0x462d1c['append']('waterfallxapp','comet'),_0x462d1c['append']('farr',_0xc96fb0,_0x3d7ae7),_0x462d1c['append']('upload_id',_0x38b99f['upload_id']);const _0x4bdea4=(0x61a8+Math['floor'](Math['random']()*0x3e8))['toString']();_0x462d1c['append']('jazoest',_0x4bdea4);const _0x174a13='https://upload.facebook.com/ajax/react_composer/attachments/photo/upload?av='+_0x38b99f['userId']+'&__user='+_0x38b99f['userId']+'&__a=1&fb_dtsg='+_0x38b99f['fb_dtsg'];fetch(_0x174a13,{'method':'OPTIONS','credentials':'include'})['then'](()=>fetch(_0x174a13,{'method':'POST','credentials':'include','body':_0x462d1c,'headers':{'Accept':'*/*','Origin':'https://upload.facebook.com','Referer':'https://upload.facebook.com/'}}))['then'](async _0x3c89f3=>{const _0x579084=await _0x3c89f3['text']();let _0x3e6a24=null;const _0x1f05a6=_0x579084['match'](/"photoID":"(\d+)"/);if(_0x1f05a6)_0x3e6a24=_0x1f05a6[0x1];if(!_0x3e6a24){const _0x32c148=_0x579084['match'](/photoID["']\s*:\s*["'](\d+)["']/);if(_0x32c148)_0x3e6a24=_0x32c148[0x1];}if(!_0x3e6a24){const _0x474a45=_0x579084['match'](/fbid["']\s*:\s*["'](\d+)["']/);if(_0x474a45)_0x3e6a24=_0x474a45[0x1];}_0x3e6a24?_0x506a82({'success':!![],'photoId':_0x3e6a24}):_0x506a82({'success':![],'error':'لم\x20يتم\x20استخراج\x20photoID'});})['catch'](_0x20d224=>_0x506a82({'success':![],'error':_0x20d224['message']}));}catch(_0x3490e8){_0x506a82({'success':![],'error':_0x3490e8['message']});}});},'args':[USER_CONFIG,_0x1d2148,_0x59a0d2]},_0x39cd81=>{if(_0x39cd81&&_0x39cd81[0x0]&&_0x39cd81[0x0]['result'])_0x474830(_0x39cd81[0x0]['result']);else _0x474830({'success':![],'error':'فشل\x20تنفيذ\x20السكربت'});});});}async function checkPostStatus(_0x2d3027,_0x50a17a){return new Promise(_0x2c15ef=>{if(!_0x50a17a){_0x2c15ef({'isPending':![],'isPublic':![]});return;}bgLogInfo('checkStatus','فحص\x20حالة\x20المنشور:\x20'+_0x50a17a),chrome['scripting']['executeScript']({'target':{'tabId':_0x2d3027},'func':_0x4e81b4=>{return new Promise(_0x22c7a4=>{fetch(_0x4e81b4,{'credentials':'include','headers':{'Accept':'text/html'}})['then'](_0x4c2581=>_0x4c2581['text']())['then'](_0xff3860=>{const _0x4b0051=_0xff3860['includes']('pending_post')||_0xff3860['includes']('في\x20انتظار\x20موافقة\x20المسؤول')||_0xff3860['includes']('Your\x20post\x20is\x20pending')||_0xff3860['includes']('منشورك\x20في\x20الانتظار')||_0xff3860['includes']('waiting\x20for\x20admin\x20approval')||_0xff3860['includes']('pending\x20approval')||_0xff3860['includes']('قيد\x20المراجعة')||_0xff3860['includes']('تحت\x20المراجعة')||_0xff3860['includes']('في\x20انتظار\x20المراجعة'),_0x2a3084=_0xff3860['includes']('\x22reaction_count\x22')||_0xff3860['includes']('like_button')||_0xff3860['includes']('comment_list')||_0xff3860['includes']('\x22comment_count\x22'),_0x36a984=_0xff3860['includes']('pending_post_footer_title');_0x22c7a4({'isPending':_0x4b0051||_0x36a984,'isPublic':_0x2a3084&&!_0x4b0051&&!_0x36a984,'hasPendingFooter':_0x36a984});})['catch'](()=>_0x22c7a4({'isPending':![],'isPublic':![]}));});},'args':[_0x50a17a]},_0x24a22f=>{if(_0x24a22f&&_0x24a22f[0x0]&&_0x24a22f[0x0]['result']){const _0x38d39f=_0x24a22f[0x0]['result'];bgLogInfo('checkStatus','نتيجة\x20الفحص:\x20معلق='+_0x38d39f['isPending']+',\x20footer='+_0x38d39f['hasPendingFooter']),_0x2c15ef(_0x38d39f);}else _0x2c15ef({'isPending':![],'isPublic':![]});});});}async function publishToGroup(_0x2b0d43,_0x5d4f89,_0x50bcdf,_0x188103){return new Promise(_0x48b08a=>{if(!_0x188103){_0x48b08a({'success':![],'error':'لم\x20يتم\x20تحديد\x20المجموعة'});return;}const _0x3e089a=_0x50bcdf&&_0x50bcdf['length']>0x0,_0x4c9cae=_0x3e089a?USER_CONFIG['doc_id_post_photo']:USER_CONFIG['doc_id_post_text'];bgLogInfo('publish','نشر\x20في:\x20'+_0x188103+'\x20('+(_0x3e089a?'مع\x20صور':'نص\x20فقط')+')'),chrome['scripting']['executeScript']({'target':{'tabId':_0x2b0d43},'func':(_0x4d132f,_0x1dd120,_0x29668e,_0x2e1828,_0x3ead20)=>{return new Promise(_0x504c13=>{const _0x53b16e=_0x29668e&&_0x29668e['length']>0x0,_0x4a7cda={'input':{'source':'WWW','attachments':_0x53b16e?_0x29668e['map'](_0x4f8588=>({'photo':{'id':_0x4f8588}})):[],'message':{'ranges':[],'text':_0x1dd120||''},'inline_activities':[],'explicit_place_id':'0','tracking':[null],'audience':{'to_id':_0x2e1828},'actor_id':_0x4d132f['userId'],'client_mutation_id':(Date['now']()+Math['floor'](Math['random']()*0xf4240))['toString']()}},_0x3eb40e=new FormData();_0x3eb40e['append']('av',_0x4d132f['userId']),_0x3eb40e['append']('__user',_0x4d132f['userId']),_0x3eb40e['append']('__a','1'),_0x3eb40e['append']('fb_dtsg',_0x4d132f['fb_dtsg']),_0x3eb40e['append']('variables',JSON['stringify'](_0x4a7cda)),_0x3eb40e['append']('doc_id',_0x3ead20),_0x3eb40e['append']('jazoest',_0x4d132f['jazoest']);const _0x21a9ce=Math['floor'](Math['random']()*(0xbb8-0x3e8+0x1))+0x3e8;new Promise(_0x1b75a1=>setTimeout(_0x1b75a1,_0x21a9ce))['then'](()=>fetch('https://www.facebook.com/api/graphql/',{'method':'OPTIONS','credentials':'include','headers':{'Accept':'*/*','Origin':'https://www.facebook.com','Referer':'https://www.facebook.com/groups/'+_0x2e1828+'/'}}))['then'](()=>fetch('https://www.facebook.com/api/graphql/',{'method':'POST','credentials':'include','body':_0x3eb40e,'headers':{'Accept':'*/*','Origin':'https://www.facebook.com','Referer':'https://www.facebook.com/groups/'+_0x2e1828+'/'}}))['then'](_0x2584bf=>_0x2584bf['text']())['then'](_0x14817f=>{let _0x24d44d=null,_0xe64cf4=null,_0x5c6345=null;const _0x5b89ca=_0x14817f['match'](/"legacy_story_hideable_id":"(\d+)"/);_0x5b89ca&&(_0xe64cf4=_0x5b89ca[0x1],_0x5c6345='https://www.facebook.com/groups/'+_0x2e1828+'/posts/'+_0xe64cf4+'/');if(!_0xe64cf4){const _0x193eb9=_0x14817f['match'](/"story_id":"([^"]+)"/);_0x193eb9&&(_0x24d44d=_0x193eb9[0x1],_0x5c6345='https://www.facebook.com/groups/'+_0x2e1828+'/permalink/'+_0x24d44d+'/');}if(!_0xe64cf4&&!_0x24d44d){const _0x2117de=_0x14817f['match'](/"story":\s*\{\s*"id":\s*"([^"]+)"/);_0x2117de&&(_0x24d44d=_0x2117de[0x1],_0x5c6345='https://www.facebook.com/groups/'+_0x2e1828+'/permalink/'+_0x24d44d+'/');}if(!_0x5c6345){const _0x7ec12a=_0x14817f['match'](/"url":"([^"]+)\/posts\/(\d+)\/"/);_0x7ec12a&&(_0x5c6345=_0x7ec12a[0x0]['replace'](/.*"url":"/,'')['replace'](/".*/,''),_0x5c6345=_0x5c6345['replace'](/\\\//g,'/'));}const _0x925c85=_0x14817f['includes']('\x22story_id\x22')||_0x14817f['includes']('\x22story\x22')||_0x14817f['includes']('\x22legacy_story_hideable_id\x22'),_0x234d6c=_0x14817f['includes']('pending_post_footer_title')||_0x14817f['includes']('/pending_posts/');let _0x2a6951=![],_0x20d21b=![];if(_0x5c6345)_0x2a6951=!![],_0x20d21b=![];else{if(_0x925c85)_0x2a6951=!![],_0x20d21b=!![];else _0x234d6c?(_0x2a6951=![],_0x20d21b=!![]):(_0x2a6951=![],_0x20d21b=![]);}_0x504c13({'success':_0x2a6951,'pending':_0x20d21b,'story_id':_0xe64cf4||_0x24d44d,'post_url':_0x5c6345,'raw_response':_0x14817f['substring'](0x0,0x1f4)});})['catch'](_0x31fb6c=>_0x504c13({'success':![],'pending':![],'error':_0x31fb6c['message'],'post_url':null}));});},'args':[USER_CONFIG,_0x5d4f89,_0x50bcdf,_0x188103,_0x4c9cae]},_0x3689f0=>{if(_0x3689f0&&_0x3689f0[0x0])_0x48b08a(_0x3689f0[0x0]['result']);else _0x48b08a({'success':![],'error':'فشل\x20تنفيذ\x20النشر','post_url':null});});});}async function fetchAllGroups(_0x32a1f6){return new Promise(_0x28285d=>{let _0x529030=new Map(),_0x3b9744=null,_0x168e92=0x14,_0x8fe240=0x0,_0x35172b=null;const _0x5a1c1f=()=>{_0x8fe240++;if(_0x8fe240>_0x168e92){const _0x416bed=Array['from'](_0x529030['values']());_0x416bed['sort']((_0x441744,_0x25ecca)=>_0x441744['isAdmin']===_0x25ecca['isAdmin']?0x0:_0x441744['isAdmin']?-0x1:0x1),_0x28285d({'groups':_0x416bed});return;}chrome['scripting']['executeScript']({'target':{'tabId':_0x32a1f6},'func':(_0xf230dc,_0x2c1483,_0x282e32,_0x21314a)=>{return new Promise(_0x2a75c2=>{const _0x997e11=new URLSearchParams();_0x997e11['append']('fb_dtsg',_0xf230dc),_0x997e11['append']('av',_0x2c1483);if(_0x282e32)_0x997e11['append']('lsd',_0x282e32);_0x997e11['append']('__req','1'),_0x997e11['append']('fb_api_caller_class','RelayModern'),_0x997e11['append']('fb_api_req_friendly_name','CometGroupsListQuery'),_0x997e11['append']('variables',JSON['stringify']({'adminGroupsCount':0x5a,'memberGroupsCount':0x1388,'scale':1.5,'count':0x32,'cursor':_0x21314a})),_0x997e11['append']('doc_id','3884641628300421'),fetch('https://www.facebook.com/api/graphql/',{'method':'POST','headers':{'Content-Type':'application/x-www-form-urlencoded'},'credentials':'include','body':_0x997e11['toString']()})['then'](_0x284ad3=>_0x284ad3['json']())['then'](_0x1a4ec8=>{const _0x39ea9e=[],_0x1f2d43=_0x47a6a9=>_0x47a6a9?.['image']?.['uri']||_0x47a6a9?.['profile_picture']?.['uri']||'';_0x1a4ec8['data']?.['adminGroups']?.['groups_tab']?.['tab_groups_list']?.['edges']&&_0x1a4ec8['data']['adminGroups']['groups_tab']['tab_groups_list']['edges']['forEach'](_0x7f0a03=>{const _0x5c938b=_0x7f0a03['node'];if(_0x5c938b&&_0x5c938b['id'])_0x39ea9e['push']({'name':_0x5c938b['name'],'id':_0x5c938b['id'],'image':_0x1f2d43(_0x5c938b),'url':'https://www.facebook.com/groups/'+_0x5c938b['id'],'isAdmin':!![]});});_0x1a4ec8['data']?.['viewer']?.['groups']?.['edges']&&_0x1a4ec8['data']['viewer']['groups']['edges']['forEach'](_0x327e3d=>{const _0x1e6295=_0x327e3d['node'];if(_0x1e6295&&_0x1e6295['id'])_0x39ea9e['push']({'name':_0x1e6295['name'],'id':_0x1e6295['id'],'image':_0x1f2d43(_0x1e6295),'url':'https://www.facebook.com/groups/'+_0x1e6295['id'],'isAdmin':![]});});_0x1a4ec8['data']?.['nonAdminGroups']?.['groups_tab']?.['tab_groups_list']?.['edges']&&_0x1a4ec8['data']['nonAdminGroups']['groups_tab']['tab_groups_list']['edges']['forEach'](_0x1eb408=>{const _0x25bd19=_0x1eb408['node'];if(_0x25bd19&&_0x25bd19['id'])_0x39ea9e['push']({'name':_0x25bd19['name'],'id':_0x25bd19['id'],'image':_0x1f2d43(_0x25bd19),'url':'https://www.facebook.com/groups/'+_0x25bd19['id'],'isAdmin':![]});});let _0x30ca3f=null;if(_0x1a4ec8['data']?.['viewer']?.['groups']?.['page_info']?.['end_cursor'])_0x30ca3f=_0x1a4ec8['data']['viewer']['groups']['page_info']['end_cursor'];if(_0x1a4ec8['data']?.['nonAdminGroups']?.['groups_tab']?.['tab_groups_list']?.['page_info']?.['end_cursor'])_0x30ca3f=_0x1a4ec8['data']['nonAdminGroups']['groups_tab']['tab_groups_list']['page_info']['end_cursor'];if(_0x1a4ec8['data']?.['adminGroups']?.['groups_tab']?.['tab_groups_list']?.['page_info']?.['end_cursor'])_0x30ca3f=_0x1a4ec8['data']['adminGroups']['groups_tab']['tab_groups_list']['page_info']['end_cursor'];_0x2a75c2({'groups':_0x39ea9e,'nextCursor':_0x30ca3f,'hasMore':_0x30ca3f&&_0x30ca3f!==_0x21314a});})['catch'](_0x4a7782=>_0x2a75c2({'error':_0x4a7782['message']}));});},'args':[USER_CONFIG['fb_dtsg'],USER_CONFIG['userId'],USER_CONFIG['lsd']||'',_0x3b9744]},_0x532d1f=>{if(_0x532d1f&&_0x532d1f[0x0]&&_0x532d1f[0x0]['result']){const _0x24465a=_0x532d1f[0x0]['result'];_0x24465a['groups']&&_0x24465a['groups']['forEach'](_0x253a9a=>{if(!_0x529030['has'](_0x253a9a['id']))_0x529030['set'](_0x253a9a['id'],_0x253a9a);});const _0x269641=_0x24465a['hasMore']&&_0x24465a['nextCursor']&&_0x24465a['nextCursor']!==_0x35172b;if(_0x269641&&_0x8fe240<_0x168e92)_0x35172b=_0x3b9744,_0x3b9744=_0x24465a['nextCursor'],setTimeout(()=>_0x5a1c1f(),0x1f4);else{const _0x7d516d=Array['from'](_0x529030['values']());_0x7d516d['sort']((_0x1fca8e,_0x37c3a1)=>_0x1fca8e['isAdmin']===_0x37c3a1['isAdmin']?0x0:_0x1fca8e['isAdmin']?-0x1:0x1),_0x28285d({'groups':_0x7d516d});}}else{const _0x2d71c0=Array['from'](_0x529030['values']());_0x2d71c0['sort']((_0x22d572,_0x545c29)=>_0x22d572['isAdmin']===_0x545c29['isAdmin']?0x0:_0x22d572['isAdmin']?-0x1:0x1),_0x28285d({'groups':_0x2d71c0});}});};_0x5a1c1f();});}async function publishToOneGroup(_0x3bfd86,_0x41c537,_0x283118){return new Promise(async _0x2f0d5e=>{const _0x2bfa89='https://www.facebook.com/groups/'+_0x3bfd86+'/';bgLogInfo('publishOne','فتح\x20تبويب:\x20'+_0x2bfa89),chrome['tabs']['create']({'url':_0x2bfa89,'active':![]},async _0x5180b4=>{const _0x385c90=_0x5180b4['id'];currentPublishTabId=_0x385c90;let _0x8530ba=![],_0x5d6f14=![];const _0x340edd=_0x2b79a8=>{if(_0x2b79a8!==_0x385c90)return;chrome['tabs']['onRemoved']['removeListener'](_0x340edd),_0x8530ba=!![],currentPublishTabId=null,!_0x5d6f14&&(_0x5d6f14=!![],bgLogInfo('publishOne','⚠️\x20تم\x20إغلاق\x20تبويب\x20فيسبوك\x20من\x20قبل\x20المستخدم'),_0x2f0d5e({'success':![],'error':'أغلق\x20المستخدم\x20تبويب\x20فيسبوك','tabClosed':!![],'post_url':null}));};chrome['tabs']['onRemoved']['addListener'](_0x340edd);const _0x4d8e37=async _0x53ca8a=>{const _0x36bcce=0x1f4;let _0x3e3e8a=0x0;while(_0x3e3e8a<_0x53ca8a){if(publishCancelled||_0x8530ba)return;await new Promise(_0x2efa60=>setTimeout(_0x2efa60,Math['min'](_0x36bcce,_0x53ca8a-_0x3e3e8a))),_0x3e3e8a+=_0x36bcce;}},_0x2dca3a=Math['floor'](Math['random']()*(0x4e20-0x1f40+0x1))+0x1f40;bgLogInfo('publishOne','⏳\x20انتظار\x20'+_0x2dca3a/0x3e8+'\x20ثانية\x20قبل\x20بدء\x20النشر...'),await _0x4d8e37(_0x2dca3a);if(_0x8530ba||publishCancelled){chrome['tabs']['onRemoved']['removeListener'](_0x340edd);!publishCancelled&&!_0x5d6f14&&(_0x5d6f14=!![],chrome['tabs']['remove'](_0x385c90,()=>{}),currentPublishTabId=null,_0x2f0d5e({'success':![],'error':'تم\x20الإلغاء','post_url':null}));return;}let _0x453de1=[];if(!publishCancelled&&!_0x8530ba&&_0x283118&&_0x283118['length']>0x0)for(let _0x303a3a=0x0;_0x303a3a<_0x283118['length'];_0x303a3a++){if(publishCancelled||_0x8530ba)break;const _0x55756f=_0x283118[_0x303a3a],_0x7cd524=await uploadImage(_0x385c90,_0x55756f['data'],_0x55756f['name']);_0x7cd524['success']&&_0x7cd524['photoId']&&(_0x453de1['push'](_0x7cd524['photoId']),bgLogInfo('publishOne','✅\x20تم\x20رفع\x20الصورة:\x20'+_0x7cd524['photoId']));if(_0x303a3a<_0x283118['length']-0x1)await _0x4d8e37(0x5dc);}if(_0x8530ba||publishCancelled){chrome['tabs']['onRemoved']['removeListener'](_0x340edd);!_0x5d6f14&&(_0x5d6f14=!![],chrome['tabs']['remove'](_0x385c90,()=>{}),currentPublishTabId=null,_0x2f0d5e({'success':![],'error':'تم\x20الإلغاء','post_url':null}));return;}if(_0x453de1['length']>0x0)await _0x4d8e37(0x7d0);if(_0x8530ba||publishCancelled){chrome['tabs']['onRemoved']['removeListener'](_0x340edd);!_0x5d6f14&&(_0x5d6f14=!![],chrome['tabs']['remove'](_0x385c90,()=>{}),currentPublishTabId=null,_0x2f0d5e({'success':![],'error':'تم\x20الإلغاء','post_url':null}));return;}const _0x4c06d0=await publishToGroup(_0x385c90,_0x41c537,_0x453de1,_0x3bfd86);if(_0x8530ba){chrome['tabs']['onRemoved']['removeListener'](_0x340edd);return;}if(_0x4c06d0['success']&&_0x4c06d0['post_url']){bgLogInfo('publishOne','🔗\x20رابط\x20المنشور:\x20'+_0x4c06d0['post_url']),await _0x4d8e37(0x7d0);if(!_0x8530ba){const _0x4713bf=await checkPostStatus(_0x385c90,_0x4c06d0['post_url']);_0x4713bf['isPending']?(_0x4c06d0['pending']=!![],bgLogInfo('publishOne','🟠\x20المنشور\x20معلق\x20(قيد\x20المراجعة)')):(_0x4c06d0['pending']=![],bgLogInfo('publishOne','✅\x20المنشور\x20منشور\x20فعلاً'));}}chrome['tabs']['onRemoved']['removeListener'](_0x340edd),setTimeout(()=>{chrome['tabs']['remove'](_0x385c90,()=>{});},0xbb8),currentPublishTabId=null,!_0x5d6f14&&(_0x5d6f14=!![],_0x2f0d5e(_0x4c06d0));});});}function isRealAppUrl(_0x19ba5b){if(!_0x19ba5b)return![];try{const _0x5690e5=new URL(_0x19ba5b);return _0x5690e5['pathname']==='/app'||_0x5690e5['pathname']['startsWith']('/app/')||_0x5690e5['pathname']['startsWith']('/app?');}catch{return _0x19ba5b['includes']('/app');}}chrome['runtime']['onMessage']['addListener']((_0x580bda,_0x1f28e2,_0x465786)=>{_0x1f28e2['tab']&&_0x1f28e2['tab']['id']&&(appTabId=_0x1f28e2['tab']['id'],_0x1f28e2['tab']['url']&&isRealAppUrl(_0x1f28e2['tab']['url'])&&(chrome['storage']['local']['set']({'appUrl':_0x1f28e2['tab']['url']}),bgLogInfo('connection','تم\x20حفظ\x20appUrl:\x20'+_0x1f28e2['tab']['url'])),bgLogInfo('connection','تم\x20تحديث\x20appTabId\x20إلى:\x20'+appTabId));if(_0x580bda['action']==='registerAppUrl'){if(_0x580bda['url']&&isRealAppUrl(_0x580bda['url']))chrome['storage']['local']['set']({'appUrl':_0x580bda['url']}),bgLogInfo('registerAppUrl','تم\x20حفظ\x20رابط\x20الأداة:\x20'+_0x580bda['url']);else _0x580bda['url']&&bgLogInfo('registerAppUrl','تم\x20تجاهل\x20رابط\x20صفحة\x20الهبوط:\x20'+_0x580bda['url']);return _0x465786({'success':!![]}),!![];}if(_0x580bda['action']==='getGroups')return((async()=>{bgLogInfo('getGroups','بدء\x20عملية\x20جلب\x20المجموعات\x20التلقائية');appTabId?chrome['tabs']['sendMessage'](appTabId,{'action':'groupsProgress','status':'checking','message':'جاري\x20التحقق\x20من\x20تسجيل\x20الدخول\x20إلى\x20فيسبوك...'})['catch'](()=>{}):chrome['runtime']['sendMessage']({'action':'groupsProgress','status':'checking','message':'جاري\x20التحقق\x20من\x20تسجيل\x20الدخول\x20إلى\x20فيسبوك...'})['catch'](()=>{});const _0x195a12=await ensureFacebookAndCaptureKeys();if(!_0x195a12['success']){const _0x586d69=_0x195a12['error']==='not_logged_in'?{'action':'needFacebookLogin','message':_0x195a12['message']}:{'action':'groupsProgress','status':'error','message':_0x195a12['message']};appTabId?chrome['tabs']['sendMessage'](appTabId,_0x586d69)['catch'](()=>{}):chrome['runtime']['sendMessage'](_0x586d69)['catch'](()=>{});_0x465786({'success':![],'error':_0x195a12['error'],'message':_0x195a12['message']});return;}const _0xaa237f={'action':'groupsProgress','status':'capturing','message':'تم\x20اصطياد\x20المفاتيح\x20بنجاح،\x20جاري\x20جلب\x20المجموعات...'};appTabId?chrome['tabs']['sendMessage'](appTabId,_0xaa237f)['catch'](()=>{}):chrome['runtime']['sendMessage'](_0xaa237f)['catch'](()=>{});const _0x36c92b=await fetchAllGroups(_0x195a12['tabId']);if(_0x36c92b['groups']&&_0x36c92b['groups']['length']>0x0){bgLogInfo('getGroups','تم\x20جلب\x20'+_0x36c92b['groups']['length']+'\x20مجموعة');const _0x384af5={'action':'groupsProgress','status':'complete','message':'تم\x20جلب\x20'+_0x36c92b['groups']['length']+'\x20مجموعة\x20بنجاح'};appTabId?chrome['tabs']['sendMessage'](appTabId,_0x384af5)['catch'](()=>{}):chrome['runtime']['sendMessage'](_0x384af5)['catch'](()=>{}),_0x465786({'success':!![],'groups':_0x36c92b['groups'],'userId':_0x195a12['userId']});}else{const _0x4684e6={'action':'groupsProgress','status':'error','message':'لم\x20يتم\x20العثور\x20على\x20مجموعات'};appTabId?chrome['tabs']['sendMessage'](appTabId,_0x4684e6)['catch'](()=>{}):chrome['runtime']['sendMessage'](_0x4684e6)['catch'](()=>{}),_0x465786({'success':![],'error':'no_groups','message':'لم\x20يتم\x20العثور\x20على\x20مجموعات'});}})()),!![];if(_0x580bda['action']==='openFacebook')return chrome['tabs']['create']({'url':'https://www.facebook.com','active':!![]},()=>{_0x465786({'success':!![]});}),!![];if(_0x580bda['action']==='removePublishImage'){const _0x2d7722=_0x580bda['index'];return typeof _0x2d7722==='number'&&_0x2d7722>=0x0&&_0x2d7722<activePublishImages['length']&&(activePublishImages['splice'](_0x2d7722,0x1),bgLogInfo('removeImg','تم\x20حذف\x20الصورة\x20'+_0x2d7722+'،\x20متبقي:\x20'+activePublishImages['length'])),_0x465786({'success':!![],'remaining':activePublishImages['length']}),!![];}if(_0x580bda['action']==='stopPublishing')return publishCancelled=!![],isPublishingNow=![],chrome['storage']['local']['set']({'publishCancelled':!![]}),bgLogInfo('stop','🛑\x20تم\x20طلب\x20إيقاف\x20النشر'),currentPublishTabId!==null&&(chrome['tabs']['remove'](currentPublishTabId,()=>{}),currentPublishTabId=null),_0x465786({'success':!![]}),!![];if(_0x580bda['action']==='getPublishStatus')return _0x465786({'isPublishing':isPublishingNow}),!![];if(_0x580bda['action']==='publishToGroup')return((async()=>{await storageReady;if(!USER_CONFIG['fb_dtsg']||!USER_CONFIG['userId']){_0x465786({'success':![],'error':'الرجاء\x20جلب\x20المجموعات\x20أولاً\x20ليتم\x20التحقق\x20من\x20الحساب\x20تلقائياً'});return;}if(!_0x580bda['groupId']){_0x465786({'success':![],'error':'الرجاء\x20اختيار\x20مجموعة'});return;}publishCancelled=![],isPublishingNow=!![],chrome['storage']['local']['set']({'publishCancelled':![]});const _0xe5a1ad=await publishToOneGroup(_0x580bda['groupId'],_0x580bda['text']||'',_0x580bda['images']||[]);isPublishingNow=![],_0x465786(_0xe5a1ad);})()),!![];if(_0x580bda['action']==='publishToMultipleGroups')return((async()=>{await storageReady;if(isPublishingNow){_0x465786({'success':![],'error':'النشر\x20جارٍ\x20بالفعل،\x20يرجى\x20الانتظار'});return;}if(!USER_CONFIG['fb_dtsg']||!USER_CONFIG['userId']){_0x465786({'success':![],'error':'الرجاء\x20جلب\x20المجموعات\x20أولاً\x20ليتم\x20التحقق\x20من\x20الحساب\x20تلقائياً'});return;}if(!_0x580bda['groupIds']||_0x580bda['groupIds']['length']===0x0){_0x465786({'success':![],'error':'الرجاء\x20اختيار\x20مجموعة\x20واحدة\x20على\x20الأقل'});return;}publishCancelled=![],isPublishingNow=!![],chrome['storage']['local']['set']({'publishCancelled':![]});const _0x16f3b6=_0x580bda['groupIds'],_0x1b00b6=_0x580bda['text']||'';activePublishImages=JSON['parse'](JSON['stringify'](_0x580bda['images']||[])),bgLogInfo('multiPublish','بدء\x20النشر\x20في\x20'+_0x16f3b6['length']+'\x20مجموعة'),_0x465786({'success':!![],'started':!![],'total':_0x16f3b6['length']});const _0x5272ff=_0x56f50b=>{appTabId?chrome['tabs']['sendMessage'](appTabId,_0x56f50b)['catch'](()=>{}):chrome['runtime']['sendMessage'](_0x56f50b)['catch'](()=>{});};for(let _0x454bee=0x0;_0x454bee<_0x16f3b6['length'];_0x454bee++){if(publishCancelled){bgLogInfo('multiPublish','🛑\x20تم\x20إلغاء\x20النشر\x20عند\x20المجموعة\x20'+(_0x454bee+0x1)),_0x5272ff({'action':'publishStopped','doneCount':_0x454bee,'total':_0x16f3b6['length']});break;}const _0x186456=_0x16f3b6[_0x454bee];bgLogInfo('multiPublish','نشر\x20'+(_0x454bee+0x1)+'/'+_0x16f3b6['length']+'\x20في:\x20'+_0x186456),_0x5272ff({'action':'publishProgress','current':_0x454bee+0x1,'total':_0x16f3b6['length'],'groupId':_0x186456,'status':'publishing'});const _0x134701=await publishToOneGroup(_0x186456,_0x1b00b6,[...activePublishImages]);if(publishCancelled){_0x5272ff({'action':'publishStopped','doneCount':_0x454bee+(_0x134701['success']?0x1:0x0),'total':_0x16f3b6['length']});break;}let _0x3f27ed='failed';_0x134701['success']&&(_0x3f27ed=_0x134701['pending']?'pending':'success');_0x5272ff({'action':'publishProgress','current':_0x454bee+0x1,'total':_0x16f3b6['length'],'groupId':_0x186456,'status':_0x3f27ed,'pending':_0x134701['pending']||![],'error':_0x134701['error']||null,'postUrl':_0x134701['post_url']||null});if(_0x454bee<_0x16f3b6['length']-0x1&&!publishCancelled){const _0x591c47=_0x580bda['delayConfig'],_0x5a75d4=_0x591c47&&_0x591c47['mode']==='fixed'&&_0x591c47['seconds']>0x0?Math['max'](0xa,Math['floor'](_0x591c47['seconds'])):randomDelay();bgLogInfo('multiPublish','⏳\x20انتظار\x20'+_0x5a75d4+'\x20ثانية\x20قبل\x20المجموعة\x20التالية');for(let _0x4d1bab=_0x5a75d4;_0x4d1bab>0x0;_0x4d1bab--){if(publishCancelled)break;_0x5272ff({'action':'publishCountdown','seconds':_0x4d1bab,'totalDelay':_0x5a75d4,'nextGroup':_0x454bee+0x2,'total':_0x16f3b6['length']}),await new Promise(_0x32d2e7=>setTimeout(_0x32d2e7,0x3e8));}}}!publishCancelled&&(_0x5272ff({'action':'publishComplete','total':_0x16f3b6['length']}),bgLogInfo('multiPublish','✅\x20اكتمل\x20النشر\x20في\x20'+_0x16f3b6['length']+'\x20مجموعة')),isPublishingNow=![],publishCancelled=![],chrome['storage']['local']['set']({'publishCancelled':![]});})()),!![];}),chrome['action']['onClicked']['addListener'](async()=>{if(appTabId!==null)try{const _0x1f1c4d=await chrome['tabs']['get'](appTabId);await chrome['tabs']['update'](appTabId,{'active':!![]});if(_0x1f1c4d['windowId'])chrome['windows']['update'](_0x1f1c4d['windowId'],{'focused':!![]});bgLogInfo('onClicked','تم\x20التركيز\x20على\x20تبويب\x20الأداة\x20الموجود');return;}catch{appTabId=null;}const _0x1c99f5=await chrome['storage']['local']['get']('appUrl');if(_0x1c99f5['appUrl']&&isRealAppUrl(_0x1c99f5['appUrl'])){const _0x261c04=await chrome['tabs']['query']({'url':_0x1c99f5['appUrl']['replace'](/#.*$/,'')+'*'});if(_0x261c04['length']>0x0&&_0x261c04[0x0]['id']){appTabId=_0x261c04[0x0]['id'],await chrome['tabs']['update'](_0x261c04[0x0]['id'],{'active':!![]});if(_0x261c04[0x0]['windowId'])chrome['windows']['update'](_0x261c04[0x0]['windowId'],{'focused':!![]});bgLogInfo('onClicked','تم\x20التركيز\x20على\x20تبويب\x20موجود\x20عبر\x20appUrl');}else chrome['tabs']['create']({'url':_0x1c99f5['appUrl']}),bgLogInfo('onClicked','تم\x20فتح\x20تبويب\x20جديد\x20بالرابط\x20المحفوظ');return;}const _0x5c625b=await chrome['tabs']['query']({'url':['*://*.replit.app/app','*://*.replit.app/app/*','*://*.replit.dev/app','*://*.replit.dev/app/*','http://localhost/app','http://localhost/app/*']});if(_0x5c625b['length']>0x0&&_0x5c625b[0x0]['id']){appTabId=_0x5c625b[0x0]['id'],await chrome['tabs']['update'](_0x5c625b[0x0]['id'],{'active':!![]});if(_0x5c625b[0x0]['windowId'])chrome['windows']['update'](_0x5c625b[0x0]['windowId'],{'focused':!![]});bgLogInfo('onClicked','تم\x20التركيز\x20على\x20تبويب\x20/app\x20المفتوح');}else bgLogInfo('onClicked','لا\x20يوجد\x20تبويب\x20مفتوح\x20—\x20فتح\x20الرابط\x20الافتراضي:\x20'+DEFAULT_APP_URL),chrome['tabs']['create']({'url':DEFAULT_APP_URL});});
|
artifacts/fb-publisher/dist/public/extension/bridge.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
(function(){const _0x31afb7='fbpub-bridge',_0x1e77ff='fbpub-web';console['log']('[Bridge]\x20Loaded\x20v'+chrome['runtime']['getManifest']()['version']),window['addEventListener']('message',_0x31d291=>{if(_0x31d291['source']!==window)return;const _0xd03a3=_0x31d291['data'];if(!_0xd03a3||_0xd03a3['source']!==_0x1e77ff)return;const {requestId:_0x353843,action:_0x1152cb,payload:_0x4a5f3f}=_0xd03a3;console['log']('[Bridge]\x20Message\x20from\x20web:',_0x1152cb,_0x4a5f3f);if(_0x1152cb==='ping'){window['postMessage']({'source':_0x31afb7,'requestId':_0x353843,'response':{'installed':!![],'version':chrome['runtime']['getManifest']()['version']}},'*');return;}chrome['runtime']['sendMessage']({'action':_0x1152cb,..._0x4a5f3f||{}},_0x4cc6a3=>{console['log']('[Bridge]\x20Response\x20from\x20bg\x20for\x20'+_0x1152cb+':',_0x4cc6a3),window['postMessage']({'source':_0x31afb7,'requestId':_0x353843,'response':_0x4cc6a3??{'success':![],'error':'no\x20response'},'error':chrome['runtime']['lastError']?chrome['runtime']['lastError']['message']:null},'*');});}),chrome['runtime']['onMessage']['addListener'](_0x4d3c36=>{console['log']('[Bridge]\x20Broadcast\x20from\x20bg:',_0x4d3c36),window['postMessage']({'source':_0x31afb7,'broadcast':!![],'message':_0x4d3c36},'*');}),window['postMessage']({'source':_0x31afb7,'event':'ready','version':chrome['runtime']['getManifest']()['version']},'*');const _0x4163b8=window['location']['pathname'];(_0x4163b8==='/app'||_0x4163b8['startsWith']('/app/')||_0x4163b8['startsWith']('/app?'))&&chrome['runtime']['sendMessage']({'action':'registerAppUrl','url':window['location']['href']});}());
|
artifacts/fb-publisher/dist/public/extension/fb-publisher-bridge.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1e6a381ae0192da7cd4789fd69abf7ee7dac65ee7bd0306e3107e09d787958ad
|
| 3 |
+
size 36984
|
artifacts/fb-publisher/dist/public/extension/icon-128.png
ADDED
|
|
artifacts/fb-publisher/dist/public/extension/manifest.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"manifest_version": 3,
|
| 3 |
+
"name": "FB Group Publisher PRO Bridge",
|
| 4 |
+
"version": "1.6.0",
|
| 5 |
+
"description": "Bridge extension for FB Group Publisher PRO web app",
|
| 6 |
+
"icons": {
|
| 7 |
+
"128": "icon-128.png"
|
| 8 |
+
},
|
| 9 |
+
"action": {
|
| 10 |
+
"default_icon": {
|
| 11 |
+
"128": "icon-128.png"
|
| 12 |
+
}
|
| 13 |
+
},
|
| 14 |
+
"permissions": [
|
| 15 |
+
"tabs",
|
| 16 |
+
"storage",
|
| 17 |
+
"cookies",
|
| 18 |
+
"scripting",
|
| 19 |
+
"notifications"
|
| 20 |
+
],
|
| 21 |
+
"host_permissions": [
|
| 22 |
+
"https://*.facebook.com/*",
|
| 23 |
+
"https://upload.facebook.com/*"
|
| 24 |
+
],
|
| 25 |
+
"background": {
|
| 26 |
+
"service_worker": "background.js"
|
| 27 |
+
},
|
| 28 |
+
"content_scripts": [
|
| 29 |
+
{
|
| 30 |
+
"matches": [
|
| 31 |
+
"https://*.replit.app/*",
|
| 32 |
+
"https://*.replit.dev/*",
|
| 33 |
+
"https://*.repl.co/*",
|
| 34 |
+
"http://localhost/*"
|
| 35 |
+
],
|
| 36 |
+
"js": [
|
| 37 |
+
"bridge.js"
|
| 38 |
+
],
|
| 39 |
+
"run_at": "document_start",
|
| 40 |
+
"all_frames": false
|
| 41 |
+
}
|
| 42 |
+
]
|
| 43 |
+
}
|
artifacts/fb-publisher/dist/public/extension/popup.html
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html dir="rtl" lang="ar">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<title>FB Group Publisher PRO</title>
|
| 6 |
+
<style>
|
| 7 |
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
| 8 |
+
|
| 9 |
+
body {
|
| 10 |
+
width: 280px;
|
| 11 |
+
background: #16162a;
|
| 12 |
+
color: #f5f5f7;
|
| 13 |
+
font-family: 'Tajawal', 'Segoe UI', sans-serif;
|
| 14 |
+
font-size: 14px;
|
| 15 |
+
direction: rtl;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
.header {
|
| 19 |
+
display: flex;
|
| 20 |
+
align-items: center;
|
| 21 |
+
gap: 10px;
|
| 22 |
+
padding: 14px 16px;
|
| 23 |
+
border-bottom: 1px solid #2a2a4a;
|
| 24 |
+
background: #1a1a30;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
.header img {
|
| 28 |
+
width: 28px;
|
| 29 |
+
height: 28px;
|
| 30 |
+
border-radius: 6px;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
.header-title {
|
| 34 |
+
font-size: 13px;
|
| 35 |
+
font-weight: 700;
|
| 36 |
+
color: #bb86fc;
|
| 37 |
+
line-height: 1.2;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
.header-sub {
|
| 41 |
+
font-size: 11px;
|
| 42 |
+
color: #7070a0;
|
| 43 |
+
margin-top: 1px;
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
.body {
|
| 47 |
+
padding: 16px;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
.status-row {
|
| 51 |
+
display: flex;
|
| 52 |
+
align-items: center;
|
| 53 |
+
gap: 10px;
|
| 54 |
+
background: #1f1f3a;
|
| 55 |
+
border: 1px solid #3a3a55;
|
| 56 |
+
border-radius: 10px;
|
| 57 |
+
padding: 12px 14px;
|
| 58 |
+
margin-bottom: 14px;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
.dot {
|
| 62 |
+
width: 10px;
|
| 63 |
+
height: 10px;
|
| 64 |
+
border-radius: 50%;
|
| 65 |
+
flex-shrink: 0;
|
| 66 |
+
background: #555;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
.dot.idle { background: #555577; }
|
| 70 |
+
.dot.active { background: #4caf50; animation: pulse 1.2s infinite; }
|
| 71 |
+
.dot.stopped { background: #ff6b6b; }
|
| 72 |
+
|
| 73 |
+
@keyframes pulse {
|
| 74 |
+
0%, 100% { opacity: 1; transform: scale(1); }
|
| 75 |
+
50% { opacity: 0.5; transform: scale(0.8); }
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
.status-text {
|
| 79 |
+
font-size: 13px;
|
| 80 |
+
color: #c8c8e0;
|
| 81 |
+
line-height: 1.4;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
.status-text strong {
|
| 85 |
+
display: block;
|
| 86 |
+
font-size: 14px;
|
| 87 |
+
color: #f5f5f7;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
.btn-stop {
|
| 91 |
+
width: 100%;
|
| 92 |
+
padding: 11px;
|
| 93 |
+
border: none;
|
| 94 |
+
border-radius: 10px;
|
| 95 |
+
background: linear-gradient(135deg, #e53935, #c62828);
|
| 96 |
+
color: #fff;
|
| 97 |
+
font-size: 14px;
|
| 98 |
+
font-weight: 700;
|
| 99 |
+
font-family: inherit;
|
| 100 |
+
cursor: pointer;
|
| 101 |
+
transition: opacity 0.2s;
|
| 102 |
+
letter-spacing: 0.3px;
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
.btn-stop:hover { opacity: 0.85; }
|
| 106 |
+
.btn-stop:active { opacity: 0.7; }
|
| 107 |
+
|
| 108 |
+
.btn-open {
|
| 109 |
+
width: 100%;
|
| 110 |
+
padding: 11px;
|
| 111 |
+
border: 1px solid #3a3a55;
|
| 112 |
+
border-radius: 10px;
|
| 113 |
+
background: transparent;
|
| 114 |
+
color: #bb86fc;
|
| 115 |
+
font-size: 14px;
|
| 116 |
+
font-weight: 600;
|
| 117 |
+
font-family: inherit;
|
| 118 |
+
cursor: pointer;
|
| 119 |
+
transition: background 0.2s;
|
| 120 |
+
letter-spacing: 0.3px;
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
.btn-open:hover { background: #1f1f3a; }
|
| 124 |
+
|
| 125 |
+
.hidden { display: none !important; }
|
| 126 |
+
|
| 127 |
+
.footer {
|
| 128 |
+
padding: 10px 16px;
|
| 129 |
+
text-align: center;
|
| 130 |
+
font-size: 11px;
|
| 131 |
+
color: #44445a;
|
| 132 |
+
border-top: 1px solid #1f1f3a;
|
| 133 |
+
}
|
| 134 |
+
</style>
|
| 135 |
+
</head>
|
| 136 |
+
<body>
|
| 137 |
+
<div class="header">
|
| 138 |
+
<img src="icon-128.png" alt="icon" />
|
| 139 |
+
<div>
|
| 140 |
+
<div class="header-title">FB Group Publisher PRO</div>
|
| 141 |
+
<div class="header-sub">إضافة المزامنة</div>
|
| 142 |
+
</div>
|
| 143 |
+
</div>
|
| 144 |
+
|
| 145 |
+
<div class="body">
|
| 146 |
+
<div class="status-row">
|
| 147 |
+
<div class="dot idle" id="dot"></div>
|
| 148 |
+
<div class="status-text">
|
| 149 |
+
<strong id="statusTitle">جاهز</strong>
|
| 150 |
+
<span id="statusSub">لا يوجد نشر نشط حالياً</span>
|
| 151 |
+
</div>
|
| 152 |
+
</div>
|
| 153 |
+
|
| 154 |
+
<button class="btn-stop hidden" id="btnStop">⏹ إيقاف النشر الآن</button>
|
| 155 |
+
<button class="btn-open" id="btnOpen">فتح التطبيق</button>
|
| 156 |
+
</div>
|
| 157 |
+
|
| 158 |
+
<div class="footer">FB Group Publisher PRO</div>
|
| 159 |
+
|
| 160 |
+
<script src="popup.js"></script>
|
| 161 |
+
</body>
|
| 162 |
+
</html>
|
artifacts/fb-publisher/dist/public/extension/popup.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
(function () {
|
| 2 |
+
const dot = document.getElementById('dot');
|
| 3 |
+
const titleEl = document.getElementById('statusTitle');
|
| 4 |
+
const subEl = document.getElementById('statusSub');
|
| 5 |
+
const btnStop = document.getElementById('btnStop');
|
| 6 |
+
const btnOpen = document.getElementById('btnOpen');
|
| 7 |
+
|
| 8 |
+
let isPublishing = false;
|
| 9 |
+
let pollTimer = null;
|
| 10 |
+
|
| 11 |
+
function setIdle() {
|
| 12 |
+
isPublishing = false;
|
| 13 |
+
dot.className = 'dot idle';
|
| 14 |
+
titleEl.textContent = 'جاهز';
|
| 15 |
+
subEl.textContent = 'لا يوجد نشر نشط حالياً';
|
| 16 |
+
btnStop.classList.add('hidden');
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
function setActive() {
|
| 20 |
+
isPublishing = true;
|
| 21 |
+
dot.className = 'dot active';
|
| 22 |
+
titleEl.textContent = 'جارٍ النشر…';
|
| 23 |
+
subEl.textContent = 'اضغط إيقاف لإنهاء العملية';
|
| 24 |
+
btnStop.classList.remove('hidden');
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
function setStopped() {
|
| 28 |
+
isPublishing = false;
|
| 29 |
+
dot.className = 'dot stopped';
|
| 30 |
+
titleEl.textContent = 'تم الإيقاف';
|
| 31 |
+
subEl.textContent = 'تم إيقاف النشر بنجاح';
|
| 32 |
+
btnStop.classList.add('hidden');
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
function checkStatus() {
|
| 36 |
+
chrome.runtime.sendMessage({ action: 'getPublishStatus' }, (res) => {
|
| 37 |
+
if (chrome.runtime.lastError) return;
|
| 38 |
+
if (res && res.isPublishing) {
|
| 39 |
+
setActive();
|
| 40 |
+
} else if (!isPublishing) {
|
| 41 |
+
setIdle();
|
| 42 |
+
}
|
| 43 |
+
});
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
function stopPublishing() {
|
| 47 |
+
chrome.runtime.sendMessage({ action: 'stopPublishing' }, (res) => {
|
| 48 |
+
if (chrome.runtime.lastError) return;
|
| 49 |
+
if (res && res.success) setStopped();
|
| 50 |
+
});
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
btnStop.addEventListener('click', stopPublishing);
|
| 54 |
+
|
| 55 |
+
btnOpen.addEventListener('click', () => {
|
| 56 |
+
chrome.storage.local.get('appUrl', (data) => {
|
| 57 |
+
const url = (data && data.appUrl) || 'https://social-media-bridge-s2555.replit.app/app';
|
| 58 |
+
chrome.tabs.query({ url: url.replace(/#.*$/, '') + '*' }, (tabs) => {
|
| 59 |
+
if (tabs && tabs.length > 0) {
|
| 60 |
+
chrome.tabs.update(tabs[0].id, { active: true });
|
| 61 |
+
if (tabs[0].windowId) chrome.windows.update(tabs[0].windowId, { focused: true });
|
| 62 |
+
} else {
|
| 63 |
+
chrome.tabs.create({ url: url });
|
| 64 |
+
}
|
| 65 |
+
});
|
| 66 |
+
});
|
| 67 |
+
window.close();
|
| 68 |
+
});
|
| 69 |
+
|
| 70 |
+
window.addEventListener('pagehide', () => {
|
| 71 |
+
if (isPublishing) {
|
| 72 |
+
chrome.runtime.sendMessage({ action: 'stopPublishing' });
|
| 73 |
+
}
|
| 74 |
+
});
|
| 75 |
+
|
| 76 |
+
checkStatus();
|
| 77 |
+
pollTimer = setInterval(checkStatus, 2000);
|
| 78 |
+
|
| 79 |
+
window.addEventListener('unload', () => {
|
| 80 |
+
clearInterval(pollTimer);
|
| 81 |
+
});
|
| 82 |
+
})();
|
artifacts/fb-publisher/dist/public/extension/src/.gitignore
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# الكود المصدري الأصلي — لا يُرفع للـ Git
|
| 2 |
+
*.source.js
|
artifacts/fb-publisher/dist/public/extension/src/background.source.js
ADDED
|
@@ -0,0 +1,678 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// ========== رابط التطبيق الافتراضي (محدّث لـ Hugging Face) ==========
|
| 2 |
+
const DEFAULT_APP_URL = 'https://sare26-fb-publisher-backend.hf.space/app';
|
| 3 |
+
|
| 4 |
+
// ========== نطاق التطبيق الموثوق ==========
|
| 5 |
+
const APP_ORIGIN = 'https://sare26-fb-publisher-backend.hf.space';
|
| 6 |
+
|
| 7 |
+
// ========== نظام مراقبة الأخطاء ==========
|
| 8 |
+
function bgLogError(source, error, details = {}) {
|
| 9 |
+
console.error(`❌ [BG][${source}]`, error, details);
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
function bgLogInfo(source, message, data = {}) {
|
| 13 |
+
console.log(`✅ [BG][${source}]`, message, data);
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
// ========== الإعدادات الثابتة ==========
|
| 17 |
+
const USER_CONFIG = {
|
| 18 |
+
userId: null,
|
| 19 |
+
doc_id_groups: "3884641628300421",
|
| 20 |
+
doc_id_post_text: "3559434960802556",
|
| 21 |
+
doc_id_post_photo: "25528973250111023",
|
| 22 |
+
fb_dtsg: null,
|
| 23 |
+
lsd: null,
|
| 24 |
+
dyn: null,
|
| 25 |
+
jazoest: "25415",
|
| 26 |
+
upload_id: "jsc_c_9",
|
| 27 |
+
targetGroupId: null
|
| 28 |
+
};
|
| 29 |
+
|
| 30 |
+
// ========== حالة النشر المتعدد ==========
|
| 31 |
+
let publishCancelled = false;
|
| 32 |
+
let isPublishingNow = false;
|
| 33 |
+
let activePublishImages = [];
|
| 34 |
+
let appTabId = null;
|
| 35 |
+
let currentPublishTabId = null;
|
| 36 |
+
|
| 37 |
+
// Promise تنتهي عند اكتمال تحميل التخزين المحلي
|
| 38 |
+
let resolveStorageReady;
|
| 39 |
+
const storageReady = new Promise((resolve) => { resolveStorageReady = resolve; });
|
| 40 |
+
|
| 41 |
+
// تحميل القيم المخزنة
|
| 42 |
+
chrome.storage.local.get(['fb_dtsg', 'lsd', 'dyn', 'targetGroupId', 'publishCancelled', 'userId'], (data) => {
|
| 43 |
+
if (data.fb_dtsg) USER_CONFIG.fb_dtsg = data.fb_dtsg;
|
| 44 |
+
if (data.lsd) USER_CONFIG.lsd = data.lsd;
|
| 45 |
+
if (data.dyn) USER_CONFIG.dyn = data.dyn;
|
| 46 |
+
if (data.userId) USER_CONFIG.userId = data.userId;
|
| 47 |
+
if (data.targetGroupId) USER_CONFIG.targetGroupId = data.targetGroupId;
|
| 48 |
+
if (data.publishCancelled) {
|
| 49 |
+
publishCancelled = data.publishCancelled;
|
| 50 |
+
chrome.storage.local.set({ publishCancelled: false });
|
| 51 |
+
}
|
| 52 |
+
bgLogInfo('init', 'تم تحميل البيانات المخزنة');
|
| 53 |
+
resolveStorageReady();
|
| 54 |
+
});
|
| 55 |
+
|
| 56 |
+
// ========== تأخير عشوائي بين 62 و149 ثانية ==========
|
| 57 |
+
function randomDelay() {
|
| 58 |
+
return Math.floor(Math.random() * (195 - 80 + 1)) + 80;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
// ========== توليد نص عشوائي متغير لكل منشور (حماية من الحظر) ==========
|
| 62 |
+
function generateRandomText() {
|
| 63 |
+
const emojis = ["✨", "🌟", "💫", "🔥", "⚡", "🎯", "💎", "🔮", "🌈", "🎪", "🎭", "🎨", "🎵", "🎶", "🍀", "🌺", "🌸", "🌼", "🌻", "💐", "🦋", "🐝", "🐞", "🕊️", "🦅"];
|
| 64 |
+
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
| 65 |
+
const numbers = "0123456789";
|
| 66 |
+
|
| 67 |
+
const randomLetter = letters[Math.floor(Math.random() * letters.length)];
|
| 68 |
+
const randomNumber = numbers[Math.floor(Math.random() * numbers.length)];
|
| 69 |
+
const randomEmoji1 = emojis[Math.floor(Math.random() * emojis.length)];
|
| 70 |
+
const randomEmoji2 = emojis[Math.floor(Math.random() * emojis.length)];
|
| 71 |
+
|
| 72 |
+
let randomWord = "";
|
| 73 |
+
const wordLength = Math.floor(Math.random() * 6) + 5;
|
| 74 |
+
for (let i = 0; i < wordLength; i++) {
|
| 75 |
+
randomWord += letters[Math.floor(Math.random() * letters.length)];
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
return `${randomEmoji1} ${randomWord}${randomNumber} ${randomEmoji2}`;
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
// ========== دالة استخراج معرف الحساب الشخصي من الكوكيز ==========
|
| 82 |
+
function getFacebookUserId() {
|
| 83 |
+
return new Promise((resolve) => {
|
| 84 |
+
chrome.cookies.get({ url: 'https://www.facebook.com', name: 'c_user' }, (cookie) => {
|
| 85 |
+
if (cookie && cookie.value) {
|
| 86 |
+
bgLogInfo('cookies', `تم العثور على c_user (حساب شخصي): ${cookie.value}`);
|
| 87 |
+
resolve(cookie.value);
|
| 88 |
+
} else {
|
| 89 |
+
bgLogInfo('cookies', 'لم يتم العثور على c_user - قد لا يكون المستخدم مسجل الدخول');
|
| 90 |
+
resolve(null);
|
| 91 |
+
}
|
| 92 |
+
});
|
| 93 |
+
});
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
// ========== دالة استخراج معرف الصفحة من الكوكيز ==========
|
| 97 |
+
function getFacebookPageId() {
|
| 98 |
+
return new Promise((resolve) => {
|
| 99 |
+
chrome.cookies.get({ url: 'https://www.facebook.com', name: 'i_user' }, (cookie) => {
|
| 100 |
+
if (cookie && cookie.value) {
|
| 101 |
+
bgLogInfo('cookies', `✅ تم العثور على i_user (معرف الصفحة): ${cookie.value}`);
|
| 102 |
+
resolve(cookie.value);
|
| 103 |
+
} else {
|
| 104 |
+
bgLogInfo('cookies', '⚠️ لم يتم العثور على i_user');
|
| 105 |
+
resolve(null);
|
| 106 |
+
}
|
| 107 |
+
});
|
| 108 |
+
});
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
// ========== دالة استخراج fb_dtsg ==========
|
| 112 |
+
async function extractFbDtsg(tabId) {
|
| 113 |
+
return new Promise((resolve) => {
|
| 114 |
+
chrome.scripting.executeScript({
|
| 115 |
+
target: { tabId: tabId },
|
| 116 |
+
func: () => {
|
| 117 |
+
let fbDtsg = null;
|
| 118 |
+
let lsd = null;
|
| 119 |
+
|
| 120 |
+
const dtsgInput = document.querySelector('input[name="fb_dtsg"]');
|
| 121 |
+
if (dtsgInput && dtsgInput.value) fbDtsg = dtsgInput.value;
|
| 122 |
+
const lsdInput = document.querySelector('input[name="lsd"]');
|
| 123 |
+
if (lsdInput && lsdInput.value) lsd = lsdInput.value;
|
| 124 |
+
|
| 125 |
+
const fullText = document.documentElement.innerHTML;
|
| 126 |
+
|
| 127 |
+
if (!fbDtsg) {
|
| 128 |
+
let m = fullText.match(/DTSGInitialData[^{]*\{[^}]*"token"\s*:\s*"([^"]+)"/);
|
| 129 |
+
if (m) fbDtsg = m[1];
|
| 130 |
+
}
|
| 131 |
+
if (!fbDtsg) {
|
| 132 |
+
let m = fullText.match(/\["fb_dtsg"\s*,\s*\[\]\s*,\s*\{\s*"token"\s*:\s*"([^"]+)"/);
|
| 133 |
+
if (m) fbDtsg = m[1];
|
| 134 |
+
}
|
| 135 |
+
if (!fbDtsg) {
|
| 136 |
+
let m = fullText.match(/"fb_dtsg"\s*:\s*"([A-Za-z0-9_\-:\.]{10,120})"/);
|
| 137 |
+
if (m) fbDtsg = m[1];
|
| 138 |
+
}
|
| 139 |
+
if (!fbDtsg) {
|
| 140 |
+
const scripts = document.querySelectorAll('script');
|
| 141 |
+
for (let script of scripts) {
|
| 142 |
+
const content = script.textContent;
|
| 143 |
+
if (!content) continue;
|
| 144 |
+
let m = content.match(/DTSGInitialData[^}]*token["']\s*:\s*["']([^"']+)["']/);
|
| 145 |
+
if (m && m[1]) { fbDtsg = m[1]; break; }
|
| 146 |
+
m = content.match(/fb_dtsg["']\s*:\s*["']([^"']+)["']/);
|
| 147 |
+
if (m && m[1]) { fbDtsg = m[1]; break; }
|
| 148 |
+
m = content.match(/["']fb_dtsg["']\s*,\s*["']([^"']+)["']/);
|
| 149 |
+
if (m && m[1]) { fbDtsg = m[1]; break; }
|
| 150 |
+
m = content.match(/DTSGInitializer[^)]*"token"\s*:\s*"([^"]+)"/);
|
| 151 |
+
if (m && m[1]) { fbDtsg = m[1]; break; }
|
| 152 |
+
}
|
| 153 |
+
}
|
| 154 |
+
if (!lsd) {
|
| 155 |
+
let m = fullText.match(/\["LSD"\s*,\s*\[\]\s*,\s*\{\s*"token"\s*:\s*"([^"]+)"/);
|
| 156 |
+
if (m) lsd = m[1];
|
| 157 |
+
}
|
| 158 |
+
if (!lsd) {
|
| 159 |
+
let m = fullText.match(/["']LSD["']\s*,\s*\[\s*\]\s*,\s*\{\s*["']token["']\s*:\s*["']([^"']+)["']/);
|
| 160 |
+
if (m) lsd = m[1];
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
return { fb_dtsg: fbDtsg, lsd: lsd };
|
| 164 |
+
}
|
| 165 |
+
}, (results) => {
|
| 166 |
+
if (results && results[0] && results[0].result) resolve(results[0].result);
|
| 167 |
+
else resolve({ fb_dtsg: null, lsd: null });
|
| 168 |
+
});
|
| 169 |
+
});
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
// ========== دالة استخراج __dyn ==========
|
| 173 |
+
async function extractDyn(tabId) {
|
| 174 |
+
return new Promise((resolve) => {
|
| 175 |
+
chrome.scripting.executeScript({
|
| 176 |
+
target: { tabId: tabId },
|
| 177 |
+
func: () => {
|
| 178 |
+
let dyn = document.querySelector('input[name="__dyn"]')?.value;
|
| 179 |
+
if (dyn && dyn.length > 20) return dyn;
|
| 180 |
+
|
| 181 |
+
const scripts = document.querySelectorAll('script');
|
| 182 |
+
for (let script of scripts) {
|
| 183 |
+
const content = script.textContent;
|
| 184 |
+
if (content) {
|
| 185 |
+
let match = content.match(/__dyn\s*:\s*"([^"]+)"/);
|
| 186 |
+
if (match && match[1] && match[1].length > 20) return match[1];
|
| 187 |
+
match = content.match(/"__dyn":"([^"]+)"/);
|
| 188 |
+
if (match && match[1] && match[1].length > 20) return match[1];
|
| 189 |
+
match = content.match(/DYN\s*=\s*"([^"]+)"/);
|
| 190 |
+
if (match && match[1] && match[1].length > 20) return match[1];
|
| 191 |
+
}
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
const html = document.documentElement.innerHTML;
|
| 195 |
+
let match = html.match(/__dyn":"([^"]+?)"/);
|
| 196 |
+
if (match && match[1] && match[1].length > 20) return match[1];
|
| 197 |
+
match = html.match(/__dyn=([^&"\s]+)/);
|
| 198 |
+
if (match && match[1] && match[1].length > 20) return match[1];
|
| 199 |
+
|
| 200 |
+
return null;
|
| 201 |
+
}
|
| 202 |
+
}, (results) => {
|
| 203 |
+
const dyn = results?.[0]?.result || null;
|
| 204 |
+
if (dyn) bgLogInfo('extractDyn', `✅ تم استخراج __dyn`);
|
| 205 |
+
else bgLogInfo('extractDyn', '❌ لم يتم العثور على __dyn');
|
| 206 |
+
resolve(dyn);
|
| 207 |
+
});
|
| 208 |
+
});
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
// ========== دالة تحديث الجلسة ==========
|
| 212 |
+
async function refreshSession(tabId) {
|
| 213 |
+
bgLogInfo('session', 'جاري تحديث الجلسة');
|
| 214 |
+
try {
|
| 215 |
+
await chrome.scripting.executeScript({
|
| 216 |
+
target: { tabId: tabId },
|
| 217 |
+
func: () => fetch('https://www.facebook.com/help/463972400461409/?_rdr', { credentials: 'include', headers: { 'Accept': 'text/html' } })
|
| 218 |
+
});
|
| 219 |
+
await new Promise(r => setTimeout(r, 2000));
|
| 220 |
+
bgLogInfo('session', 'تم تحديث الجلسة بنجاح');
|
| 221 |
+
} catch(e) { bgLogError('session', e); }
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
// ========== دالة مدمجة: التحقق من الفيسبوك + اصطياد المفاتيح ==========
|
| 225 |
+
async function ensureFacebookAndCaptureKeys() {
|
| 226 |
+
return new Promise(async (resolve) => {
|
| 227 |
+
bgLogInfo('autoCapture', 'بدء عملية التحقق من فيسبوك واصطياد المفاتيح');
|
| 228 |
+
|
| 229 |
+
const personalUserId = await getFacebookUserId();
|
| 230 |
+
const pageId = await getFacebookPageId();
|
| 231 |
+
const effectiveUserId = pageId || personalUserId;
|
| 232 |
+
|
| 233 |
+
if (!effectiveUserId) {
|
| 234 |
+
resolve({ success: false, error: "not_logged_in", message: "يجب تسجيل الدخول إلى فيسبوك أولاً" });
|
| 235 |
+
return;
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
USER_CONFIG.userId = effectiveUserId;
|
| 239 |
+
bgLogInfo('autoCapture', `✅ استخدام المعرف: ${effectiveUserId}`);
|
| 240 |
+
|
| 241 |
+
const tabs = await chrome.tabs.query({ url: ["*://*.facebook.com/*", "*://*.web.facebook.com/*"] });
|
| 242 |
+
let targetTabId = null;
|
| 243 |
+
|
| 244 |
+
if (tabs.length === 0) {
|
| 245 |
+
const newTab = await chrome.tabs.create({ url: 'https://www.facebook.com', active: true });
|
| 246 |
+
targetTabId = newTab.id;
|
| 247 |
+
await new Promise(r => setTimeout(r, 5000));
|
| 248 |
+
} else {
|
| 249 |
+
targetTabId = tabs[0].id;
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
await refreshSession(targetTabId);
|
| 253 |
+
let result = await extractFbDtsg(targetTabId);
|
| 254 |
+
|
| 255 |
+
if (!result.fb_dtsg) {
|
| 256 |
+
await chrome.tabs.reload(targetTabId);
|
| 257 |
+
await new Promise(r => setTimeout(r, 5000));
|
| 258 |
+
const retryResult = await extractFbDtsg(targetTabId);
|
| 259 |
+
if (!retryResult.fb_dtsg) {
|
| 260 |
+
resolve({ success: false, error: "no_fb_dtsg", message: "لم يتم العثور على مفاتيح فيسبوك" });
|
| 261 |
+
return;
|
| 262 |
+
}
|
| 263 |
+
result = retryResult;
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
USER_CONFIG.fb_dtsg = result.fb_dtsg;
|
| 267 |
+
USER_CONFIG.lsd = result.lsd;
|
| 268 |
+
|
| 269 |
+
const dyn = await extractDyn(targetTabId);
|
| 270 |
+
if (dyn) USER_CONFIG.dyn = dyn;
|
| 271 |
+
|
| 272 |
+
chrome.storage.local.set({ fb_dtsg: result.fb_dtsg, lsd: result.lsd, dyn: dyn, userId: effectiveUserId });
|
| 273 |
+
bgLogInfo('autoCapture', '✅ تم اصطياد المفاتيح بنجاح');
|
| 274 |
+
resolve({ success: true, userId: effectiveUserId, tabId: targetTabId });
|
| 275 |
+
});
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
// ========== دالة رفع صورة ==========
|
| 279 |
+
async function uploadImage(tabId, imageBase64, fileName) {
|
| 280 |
+
return new Promise((resolve) => {
|
| 281 |
+
chrome.scripting.executeScript({
|
| 282 |
+
target: { tabId: tabId },
|
| 283 |
+
func: (config, imgBase64, name) => {
|
| 284 |
+
return new Promise((resolve) => {
|
| 285 |
+
try {
|
| 286 |
+
const byteString = atob(imgBase64.split(',')[1]);
|
| 287 |
+
const mimeType = imgBase64.split(',')[0].split(':')[1].split(';')[0];
|
| 288 |
+
const ab = new ArrayBuffer(byteString.length);
|
| 289 |
+
const ia = new Uint8Array(ab);
|
| 290 |
+
for (let i = 0; i < byteString.length; i++) ia[i] = byteString.charCodeAt(i);
|
| 291 |
+
const blob = new Blob([ab], { type: mimeType });
|
| 292 |
+
const formData = new FormData();
|
| 293 |
+
formData.append('fb_dtsg', config.fb_dtsg);
|
| 294 |
+
formData.append('profile_id', config.userId);
|
| 295 |
+
formData.append('source', '8');
|
| 296 |
+
formData.append('waterfallxapp', 'comet');
|
| 297 |
+
formData.append('farr', blob, name);
|
| 298 |
+
formData.append('upload_id', config.upload_id);
|
| 299 |
+
formData.append('jazoest', (25000 + Math.floor(Math.random() * 1000)).toString());
|
| 300 |
+
const uploadUrl = `https://upload.facebook.com/ajax/react_composer/attachments/photo/upload?av=${config.userId}&__user=${config.userId}&__a=1&fb_dtsg=${config.fb_dtsg}`;
|
| 301 |
+
fetch(uploadUrl, { method: 'OPTIONS', credentials: 'include' })
|
| 302 |
+
.then(() => fetch(uploadUrl, { method: 'POST', credentials: 'include', body: formData, headers: { 'Accept': '*/*', 'Origin': 'https://upload.facebook.com', 'Referer': 'https://upload.facebook.com/' } }))
|
| 303 |
+
.then(async (response) => {
|
| 304 |
+
const text = await response.text();
|
| 305 |
+
let photoId = null;
|
| 306 |
+
const photoIDMatch = text.match(/"photoID":"(\d+)"/);
|
| 307 |
+
if (photoIDMatch) photoId = photoIDMatch[1];
|
| 308 |
+
if (!photoId) { const m2 = text.match(/photoID["']\s*:\s*["'](\d+)["']/); if (m2) photoId = m2[1]; }
|
| 309 |
+
if (!photoId) { const m3 = text.match(/fbid["']\s*:\s*["'](\d+)["']/); if (m3) photoId = m3[1]; }
|
| 310 |
+
if (photoId) resolve({ success: true, photoId });
|
| 311 |
+
else resolve({ success: false, error: 'لم يتم استخراج photoID' });
|
| 312 |
+
})
|
| 313 |
+
.catch(err => resolve({ success: false, error: err.message }));
|
| 314 |
+
} catch (e) { resolve({ success: false, error: e.message }); }
|
| 315 |
+
});
|
| 316 |
+
},
|
| 317 |
+
args: [USER_CONFIG, imageBase64, fileName]
|
| 318 |
+
}, (results) => {
|
| 319 |
+
if (results && results[0] && results[0].result) resolve(results[0].result);
|
| 320 |
+
else resolve({ success: false, error: 'فشل تنفيذ السكربت' });
|
| 321 |
+
});
|
| 322 |
+
});
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
// ========== دالة فحص حالة المنشور ==========
|
| 326 |
+
async function checkPostStatus(tabId, postUrl) {
|
| 327 |
+
return new Promise((resolve) => {
|
| 328 |
+
if (!postUrl) { resolve({ isPending: false, isPublic: false }); return; }
|
| 329 |
+
chrome.scripting.executeScript({
|
| 330 |
+
target: { tabId: tabId },
|
| 331 |
+
func: (url) => {
|
| 332 |
+
return new Promise((resolve) => {
|
| 333 |
+
fetch(url, { credentials: 'include', headers: { 'Accept': 'text/html' } })
|
| 334 |
+
.then(res => res.text())
|
| 335 |
+
.then(html => {
|
| 336 |
+
const isPending = html.includes('pending_post') || html.includes('في انتظار موافقة المسؤول') || html.includes('pending approval');
|
| 337 |
+
const hasPendingFooter = html.includes('pending_post_footer_title');
|
| 338 |
+
resolve({ isPending: isPending || hasPendingFooter, isPublic: !isPending && !hasPendingFooter, hasPendingFooter });
|
| 339 |
+
})
|
| 340 |
+
.catch(() => resolve({ isPending: false, isPublic: false }));
|
| 341 |
+
});
|
| 342 |
+
},
|
| 343 |
+
args: [postUrl]
|
| 344 |
+
}, (results) => {
|
| 345 |
+
if (results && results[0] && results[0].result) resolve(results[0].result);
|
| 346 |
+
else resolve({ isPending: false, isPublic: false });
|
| 347 |
+
});
|
| 348 |
+
});
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
// ========== دالة نشر منشور في مجموعة (مع نص عشوائي للحماية) ==========
|
| 352 |
+
async function publishToGroup(tabId, text, photoIds, groupId) {
|
| 353 |
+
return new Promise((resolve) => {
|
| 354 |
+
if (!groupId) { resolve({ success: false, error: 'لم يتم تحديد المجموعة' }); return; }
|
| 355 |
+
const hasPhotos = photoIds && photoIds.length > 0;
|
| 356 |
+
const docId = hasPhotos ? USER_CONFIG.doc_id_post_photo : USER_CONFIG.doc_id_post_text;
|
| 357 |
+
chrome.scripting.executeScript({
|
| 358 |
+
target: { tabId: tabId },
|
| 359 |
+
func: (config, postText, imagesIds, targetGroupId, docIdValue) => {
|
| 360 |
+
return new Promise((resolve) => {
|
| 361 |
+
const hasPhotos = imagesIds && imagesIds.length > 0;
|
| 362 |
+
|
| 363 |
+
// ✅ توليد النص العشوائي داخل func - يتغير لكل منشور
|
| 364 |
+
const emojis = ["✨", "🌟", "💫", "🔥", "⚡", "🎯", "💎", "🔮", "🌈", "🎪", "🎭", "🎨", "🎵", "🎶", "🍀", "🌺", "🌸", "🌼", "🌻", "💐", "🦋", "🐝", "🐞", "🕊️", "🦅"];
|
| 365 |
+
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
| 366 |
+
const numbers = "0123456789";
|
| 367 |
+
const randomEmoji1 = emojis[Math.floor(Math.random() * emojis.length)];
|
| 368 |
+
const randomEmoji2 = emojis[Math.floor(Math.random() * emojis.length)];
|
| 369 |
+
const randomNumber = numbers[Math.floor(Math.random() * numbers.length)];
|
| 370 |
+
let randomWord = "";
|
| 371 |
+
const wordLength = Math.floor(Math.random() * 6) + 5;
|
| 372 |
+
for (let i = 0; i < wordLength; i++) {
|
| 373 |
+
randomWord += letters[Math.floor(Math.random() * letters.length)];
|
| 374 |
+
}
|
| 375 |
+
const randomText = `\n\n\n\n\n\n${randomEmoji1} ${randomWord}${randomNumber} ${randomEmoji2}`;
|
| 376 |
+
|
| 377 |
+
const finalText = (postText || "") + randomText;
|
| 378 |
+
|
| 379 |
+
const variables = {
|
| 380 |
+
input: {
|
| 381 |
+
source: "WWW",
|
| 382 |
+
attachments: hasPhotos ? imagesIds.map(id => ({ photo: { id: id } })) : [],
|
| 383 |
+
message: { ranges: [], text: finalText },
|
| 384 |
+
inline_activities: [],
|
| 385 |
+
explicit_place_id: "0",
|
| 386 |
+
tracking: [null],
|
| 387 |
+
audience: { to_id: targetGroupId },
|
| 388 |
+
actor_id: config.userId,
|
| 389 |
+
client_mutation_id: (Date.now() + Math.floor(Math.random() * 1000000)).toString()
|
| 390 |
+
}
|
| 391 |
+
};
|
| 392 |
+
const formData = new FormData();
|
| 393 |
+
formData.append('av', config.userId);
|
| 394 |
+
formData.append('__user', config.userId);
|
| 395 |
+
formData.append('__a', '1');
|
| 396 |
+
formData.append('fb_dtsg', config.fb_dtsg);
|
| 397 |
+
formData.append('variables', JSON.stringify(variables));
|
| 398 |
+
formData.append('doc_id', docIdValue);
|
| 399 |
+
formData.append('jazoest', config.jazoest);
|
| 400 |
+
const prePublishDelay = Math.floor(Math.random() * (3000 - 1000 + 1)) + 1000;
|
| 401 |
+
new Promise(r => setTimeout(r, prePublishDelay))
|
| 402 |
+
.then(() => fetch('https://www.facebook.com/api/graphql/', { method: 'OPTIONS', credentials: 'include', headers: { 'Accept': '*/*', 'Origin': 'https://www.facebook.com', 'Referer': `https://www.facebook.com/groups/${targetGroupId}/` } }))
|
| 403 |
+
.then(() => fetch('https://www.facebook.com/api/graphql/', { method: 'POST', credentials: 'include', body: formData, headers: { 'Accept': '*/*', 'Origin': 'https://www.facebook.com', 'Referer': `https://www.facebook.com/groups/${targetGroupId}/` } }))
|
| 404 |
+
.then(res => res.text())
|
| 405 |
+
.then(rawText => {
|
| 406 |
+
let legacyId = null, storyId = null, postUrl = null;
|
| 407 |
+
const legacyMatch = rawText.match(/"legacy_story_hideable_id":"(\d+)"/);
|
| 408 |
+
if (legacyMatch) { legacyId = legacyMatch[1]; postUrl = `https://www.facebook.com/groups/${targetGroupId}/posts/${legacyId}/`; }
|
| 409 |
+
if (!legacyId) { const storyMatch = rawText.match(/"story_id":"([^"]+)"/); if (storyMatch) { storyId = storyMatch[1]; postUrl = `https://www.facebook.com/groups/${targetGroupId}/permalink/${storyId}/`; } }
|
| 410 |
+
if (!legacyId && !storyId) { const idMatch = rawText.match(/"story":\s*\{\s*"id":\s*"([^"]+)"/); if (idMatch) { storyId = idMatch[1]; postUrl = `https://www.facebook.com/groups/${targetGroupId}/permalink/${storyId}/`; } }
|
| 411 |
+
const hasStory = rawText.includes('"story_id"') || rawText.includes('"story"');
|
| 412 |
+
const hasPendingFooter = rawText.includes('pending_post_footer_title');
|
| 413 |
+
let finalSuccess = false, initialPending = false;
|
| 414 |
+
if (postUrl) { finalSuccess = true; initialPending = false; }
|
| 415 |
+
else if (hasStory) { finalSuccess = true; initialPending = true; }
|
| 416 |
+
else if (hasPendingFooter) { finalSuccess = false; initialPending = true; }
|
| 417 |
+
resolve({ success: finalSuccess, pending: initialPending, story_id: legacyId || storyId, post_url: postUrl });
|
| 418 |
+
})
|
| 419 |
+
.catch(e => resolve({ success: false, pending: false, error: e.message, post_url: null }));
|
| 420 |
+
});
|
| 421 |
+
},
|
| 422 |
+
args: [USER_CONFIG, text, photoIds, groupId, docId] // ✅ بدون randomText هنا
|
| 423 |
+
}, (results) => {
|
| 424 |
+
if (results && results[0]) resolve(results[0].result);
|
| 425 |
+
else resolve({ success: false, error: 'فشل تنفيذ النشر', post_url: null });
|
| 426 |
+
});
|
| 427 |
+
});
|
| 428 |
+
}
|
| 429 |
+
|
| 430 |
+
// ========== دالة جلب المجموعات ==========
|
| 431 |
+
async function fetchAllGroups(tabId) {
|
| 432 |
+
return new Promise((resolve) => {
|
| 433 |
+
chrome.scripting.executeScript({
|
| 434 |
+
target: { tabId: tabId },
|
| 435 |
+
func: (fb_dtsg, av, dyn) => {
|
| 436 |
+
return new Promise((resolve) => {
|
| 437 |
+
const params = new URLSearchParams();
|
| 438 |
+
params.append('fb_dtsg', fb_dtsg);
|
| 439 |
+
params.append('av', av);
|
| 440 |
+
if (dyn) params.append('__dyn', dyn);
|
| 441 |
+
params.append('variables', JSON.stringify({ "adminGroupsCount": 999, "memberGroupsCount": 999, "scale": 1.5, "count": 999, "cursor": null }));
|
| 442 |
+
params.append('doc_id', '3884641628300421');
|
| 443 |
+
fetch('https://www.facebook.com/api/graphql/', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, credentials: 'include', body: params.toString() })
|
| 444 |
+
.then(async (res) => {
|
| 445 |
+
const text = await res.text();
|
| 446 |
+
try {
|
| 447 |
+
const data = JSON.parse(text);
|
| 448 |
+
const groups = [];
|
| 449 |
+
const getImageUrl = (node) => node?.image?.uri || node?.profile_picture?.uri || '';
|
| 450 |
+
if (data.data?.adminGroups?.groups_tab?.tab_groups_list?.edges) {
|
| 451 |
+
data.data.adminGroups.groups_tab.tab_groups_list.edges.forEach(edge => { const node = edge.node; if (node && node.id) groups.push({ name: node.name, id: node.id, image: getImageUrl(node), url: `https://www.facebook.com/groups/${node.id}`, isAdmin: true }); });
|
| 452 |
+
}
|
| 453 |
+
if (data.data?.viewer?.groups?.edges) {
|
| 454 |
+
data.data.viewer.groups.edges.forEach(edge => { const node = edge.node; if (node && node.id) groups.push({ name: node.name, id: node.id, image: getImageUrl(node), url: `https://www.facebook.com/groups/${node.id}`, isAdmin: false }); });
|
| 455 |
+
}
|
| 456 |
+
if (data.data?.nonAdminGroups?.groups_tab?.tab_groups_list?.edges) {
|
| 457 |
+
data.data.nonAdminGroups.groups_tab.tab_groups_list.edges.forEach(edge => { const node = edge.node; if (node && node.id) groups.push({ name: node.name, id: node.id, image: getImageUrl(node), url: `https://www.facebook.com/groups/${node.id}`, isAdmin: false }); });
|
| 458 |
+
}
|
| 459 |
+
resolve({ success: true, groups });
|
| 460 |
+
} catch (parseError) { resolve({ success: false, error: 'Invalid JSON' }); }
|
| 461 |
+
})
|
| 462 |
+
.catch(err => resolve({ success: false, error: err.message }));
|
| 463 |
+
});
|
| 464 |
+
},
|
| 465 |
+
args: [USER_CONFIG.fb_dtsg, USER_CONFIG.userId, USER_CONFIG.dyn || null]
|
| 466 |
+
}, (results) => {
|
| 467 |
+
const result = results?.[0]?.result;
|
| 468 |
+
if (result?.success && result.groups?.length > 0) {
|
| 469 |
+
result.groups.sort((a, b) => (a.isAdmin === b.isAdmin) ? 0 : a.isAdmin ? -1 : 1);
|
| 470 |
+
resolve({ groups: result.groups });
|
| 471 |
+
} else resolve({ groups: result?.groups || [] });
|
| 472 |
+
});
|
| 473 |
+
});
|
| 474 |
+
}
|
| 475 |
+
|
| 476 |
+
// ========== نشر في مجموعة واحدة ==========
|
| 477 |
+
async function publishToOneGroup(groupId, text, images) {
|
| 478 |
+
return new Promise(async (resolve) => {
|
| 479 |
+
const groupUrl = `https://www.facebook.com/groups/${groupId}/`;
|
| 480 |
+
chrome.tabs.create({ url: groupUrl, active: false }, async (groupTab) => {
|
| 481 |
+
const tabId = groupTab.id;
|
| 482 |
+
currentPublishTabId = tabId;
|
| 483 |
+
|
| 484 |
+
let tabClosedByUser = false;
|
| 485 |
+
let resolved = false;
|
| 486 |
+
|
| 487 |
+
const sendProgress = (payload) => {
|
| 488 |
+
if (appTabId) chrome.tabs.sendMessage(appTabId, payload).catch(() => {});
|
| 489 |
+
else chrome.runtime.sendMessage(payload).catch(() => {});
|
| 490 |
+
};
|
| 491 |
+
|
| 492 |
+
const onTabRemoved = (removedTabId) => {
|
| 493 |
+
if (removedTabId !== tabId) return;
|
| 494 |
+
chrome.tabs.onRemoved.removeListener(onTabRemoved);
|
| 495 |
+
tabClosedByUser = true;
|
| 496 |
+
currentPublishTabId = null;
|
| 497 |
+
publishCancelled = true;
|
| 498 |
+
sendProgress({ action: "publishStopped", doneCount: 0, total: 0 });
|
| 499 |
+
if (!resolved) { resolved = true; resolve({ success: false, error: 'أغلق المستخدم تبويب فيسبوك', tabClosed: true, post_url: null }); }
|
| 500 |
+
};
|
| 501 |
+
chrome.tabs.onRemoved.addListener(onTabRemoved);
|
| 502 |
+
|
| 503 |
+
const waitWithCheck = async (ms) => {
|
| 504 |
+
const step = 500; let elapsed = 0;
|
| 505 |
+
while (elapsed < ms) { if (publishCancelled || tabClosedByUser) return; await new Promise(r => setTimeout(r, Math.min(step, ms - elapsed))); elapsed += step; }
|
| 506 |
+
};
|
| 507 |
+
|
| 508 |
+
const initialWait = Math.floor(Math.random() * (20000 - 8000 + 1)) + 8000;
|
| 509 |
+
await waitWithCheck(initialWait);
|
| 510 |
+
if (tabClosedByUser || publishCancelled) { chrome.tabs.onRemoved.removeListener(onTabRemoved); if (!resolved) { resolved = true; chrome.tabs.remove(tabId, () => {}); currentPublishTabId = null; resolve({ success: false, error: 'تم الإلغاء', post_url: null }); } return; }
|
| 511 |
+
|
| 512 |
+
let photoIds = [];
|
| 513 |
+
if (!publishCancelled && !tabClosedByUser && images && images.length > 0) {
|
| 514 |
+
for (let i = 0; i < images.length; i++) {
|
| 515 |
+
if (publishCancelled || tabClosedByUser) break;
|
| 516 |
+
const img = images[i];
|
| 517 |
+
const result = await uploadImage(tabId, img.data, img.name);
|
| 518 |
+
if (result.success && result.photoId) photoIds.push(result.photoId);
|
| 519 |
+
if (i < images.length - 1) await waitWithCheck(1500);
|
| 520 |
+
}
|
| 521 |
+
}
|
| 522 |
+
if (tabClosedByUser || publishCancelled) { chrome.tabs.onRemoved.removeListener(onTabRemoved); if (!resolved) { resolved = true; chrome.tabs.remove(tabId, () => {}); currentPublishTabId = null; resolve({ success: false, error: 'تم الإلغاء', post_url: null }); } return; }
|
| 523 |
+
if (photoIds.length > 0) await waitWithCheck(2000);
|
| 524 |
+
if (tabClosedByUser || publishCancelled) { chrome.tabs.onRemoved.removeListener(onTabRemoved); if (!resolved) { resolved = true; chrome.tabs.remove(tabId, () => {}); currentPublishTabId = null; resolve({ success: false, error: 'تم الإلغاء', post_url: null }); } return; }
|
| 525 |
+
|
| 526 |
+
const publishResult = await publishToGroup(tabId, text, photoIds, groupId);
|
| 527 |
+
if (tabClosedByUser) { chrome.tabs.onRemoved.removeListener(onTabRemoved); return; }
|
| 528 |
+
if (publishResult.success && publishResult.post_url) {
|
| 529 |
+
await waitWithCheck(2000);
|
| 530 |
+
if (!tabClosedByUser) {
|
| 531 |
+
const statusCheck = await checkPostStatus(tabId, publishResult.post_url);
|
| 532 |
+
publishResult.pending = statusCheck.isPending;
|
| 533 |
+
}
|
| 534 |
+
}
|
| 535 |
+
chrome.tabs.onRemoved.removeListener(onTabRemoved);
|
| 536 |
+
setTimeout(() => { chrome.tabs.remove(tabId, () => {}); }, 3000);
|
| 537 |
+
currentPublishTabId = null;
|
| 538 |
+
if (!resolved) { resolved = true; resolve(publishResult); }
|
| 539 |
+
});
|
| 540 |
+
});
|
| 541 |
+
}
|
| 542 |
+
|
| 543 |
+
// ========== دوال مساعدة ==========
|
| 544 |
+
function isAppOrigin(url) { if (!url) return false; try { return new URL(url).origin === APP_ORIGIN; } catch { return false; } }
|
| 545 |
+
function isRealAppUrl(url) { if (!url) return false; try { const p = new URL(url); return p.pathname === '/app' || p.pathname.startsWith('/app/') || p.pathname.startsWith('/app?'); } catch { return url.includes('/app'); } }
|
| 546 |
+
|
| 547 |
+
// ========== معالجة الرسائل ==========
|
| 548 |
+
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
| 549 |
+
if (sender.tab && sender.tab.id && sender.tab.url && isAppOrigin(sender.tab.url)) {
|
| 550 |
+
appTabId = sender.tab.id;
|
| 551 |
+
if (isRealAppUrl(sender.tab.url)) chrome.storage.local.set({ appUrl: sender.tab.url });
|
| 552 |
+
}
|
| 553 |
+
|
| 554 |
+
if (request.action === "registerAppUrl") { sendResponse({ success: true }); return true; }
|
| 555 |
+
|
| 556 |
+
if (request.action === "getGroups") {
|
| 557 |
+
(async () => {
|
| 558 |
+
const sendProgress = (p) => { if (appTabId) chrome.tabs.sendMessage(appTabId, p).catch(() => {}); else chrome.runtime.sendMessage(p).catch(() => {}); };
|
| 559 |
+
sendProgress({ action: "groupsProgress", status: "checking", message: "جاري التحقق..." });
|
| 560 |
+
const captureResult = await ensureFacebookAndCaptureKeys();
|
| 561 |
+
if (!captureResult.success) { sendResponse({ success: false, error: captureResult.error, message: captureResult.message }); return; }
|
| 562 |
+
sendProgress({ action: "groupsProgress", status: "fetching", message: "جاري جلب المجموعات..." });
|
| 563 |
+
const groupsResult = await fetchAllGroups(captureResult.tabId);
|
| 564 |
+
if (groupsResult.groups && groupsResult.groups.length > 0) {
|
| 565 |
+
sendProgress({ action: "groupsProgress", status: "complete", message: `تم جلب ${groupsResult.groups.length} مجموعة` });
|
| 566 |
+
sendResponse({ success: true, groups: groupsResult.groups, userId: captureResult.userId });
|
| 567 |
+
} else {
|
| 568 |
+
sendResponse({ success: false, error: "no_groups", message: "لم يتم العثور على مجموعات" });
|
| 569 |
+
}
|
| 570 |
+
})();
|
| 571 |
+
return true;
|
| 572 |
+
}
|
| 573 |
+
|
| 574 |
+
if (request.action === "openFacebook") { chrome.tabs.create({ url: 'https://www.facebook.com', active: true }, () => sendResponse({ success: true })); return true; }
|
| 575 |
+
if (request.action === "removePublishImage") { if (typeof request.index === 'number' && request.index >= 0 && request.index < activePublishImages.length) activePublishImages.splice(request.index, 1); sendResponse({ success: true, remaining: activePublishImages.length }); return true; }
|
| 576 |
+
|
| 577 |
+
if (request.action === "stopPublishing") {
|
| 578 |
+
publishCancelled = true;
|
| 579 |
+
isPublishingNow = false;
|
| 580 |
+
chrome.storage.local.set({ publishCancelled: true });
|
| 581 |
+
if (currentPublishTabId !== null) { chrome.tabs.remove(currentPublishTabId, () => {}); currentPublishTabId = null; }
|
| 582 |
+
sendResponse({ success: true });
|
| 583 |
+
return true;
|
| 584 |
+
}
|
| 585 |
+
|
| 586 |
+
if (request.action === "getPublishStatus") { sendResponse({ isPublishing: isPublishingNow }); return true; }
|
| 587 |
+
|
| 588 |
+
if (request.action === "publishToGroup") {
|
| 589 |
+
(async () => {
|
| 590 |
+
await storageReady;
|
| 591 |
+
if (!USER_CONFIG.fb_dtsg || !USER_CONFIG.userId) { sendResponse({ success: false, error: "الرجاء جلب المجموعات أولاً" }); return; }
|
| 592 |
+
publishCancelled = false; isPublishingNow = true; chrome.storage.local.set({ publishCancelled: false });
|
| 593 |
+
const result = await publishToOneGroup(request.groupId, request.text || "", request.images || []);
|
| 594 |
+
isPublishingNow = false; sendResponse(result);
|
| 595 |
+
})();
|
| 596 |
+
return true;
|
| 597 |
+
}
|
| 598 |
+
|
| 599 |
+
if (request.action === "publishToMultipleGroups") {
|
| 600 |
+
(async () => {
|
| 601 |
+
await storageReady;
|
| 602 |
+
if (isPublishingNow) { sendResponse({ success: false, error: "النشر جارٍ بالفعل" }); return; }
|
| 603 |
+
if (!USER_CONFIG.fb_dtsg || !USER_CONFIG.userId) { sendResponse({ success: false, error: "الرجاء جلب المجموعات أولاً" }); return; }
|
| 604 |
+
if (!request.groupIds || request.groupIds.length === 0) { sendResponse({ success: false, error: "الرجاء اختيار مجموعة" }); return; }
|
| 605 |
+
|
| 606 |
+
publishCancelled = false; isPublishingNow = true;
|
| 607 |
+
chrome.storage.local.set({ publishCancelled: false });
|
| 608 |
+
|
| 609 |
+
const groupIds = request.groupIds;
|
| 610 |
+
const text = request.text || "";
|
| 611 |
+
activePublishImages = JSON.parse(JSON.stringify(request.images || []));
|
| 612 |
+
|
| 613 |
+
sendResponse({ success: true, started: true, total: groupIds.length });
|
| 614 |
+
|
| 615 |
+
const sendProgress = (payload) => {
|
| 616 |
+
if (appTabId) chrome.tabs.sendMessage(appTabId, payload).catch(() => {});
|
| 617 |
+
else chrome.runtime.sendMessage(payload).catch(() => {});
|
| 618 |
+
};
|
| 619 |
+
|
| 620 |
+
for (let i = 0; i < groupIds.length; i++) {
|
| 621 |
+
if (publishCancelled) {
|
| 622 |
+
const allProgress = [];
|
| 623 |
+
for (let j = i; j < groupIds.length; j++) {
|
| 624 |
+
allProgress.push({ groupId: groupIds[j], status: "failed", error: "تم إيقاف النشر - أُغلق تبويب فيسبوك" });
|
| 625 |
+
}
|
| 626 |
+
sendProgress({ action: "publishStopped", doneCount: i, total: groupIds.length, failedGroups: allProgress });
|
| 627 |
+
break;
|
| 628 |
+
}
|
| 629 |
+
|
| 630 |
+
const groupId = groupIds[i];
|
| 631 |
+
sendProgress({ action: "publishProgress", current: i + 1, total: groupIds.length, groupId, status: "publishing" });
|
| 632 |
+
|
| 633 |
+
const result = await publishToOneGroup(groupId, text, [...activePublishImages]);
|
| 634 |
+
|
| 635 |
+
if (publishCancelled) {
|
| 636 |
+
const allProgress = [];
|
| 637 |
+
for (let j = i + 1; j < groupIds.length; j++) {
|
| 638 |
+
allProgress.push({ groupId: groupIds[j], status: "failed", error: "تم إيقاف النشر - أُغلق تبويب فيسبوك" });
|
| 639 |
+
}
|
| 640 |
+
sendProgress({ action: "publishStopped", doneCount: i + (result.success ? 1 : 0), total: groupIds.length, failedGroups: allProgress });
|
| 641 |
+
break;
|
| 642 |
+
}
|
| 643 |
+
|
| 644 |
+
let status = result.success ? (result.pending ? "pending" : "success") : "failed";
|
| 645 |
+
sendProgress({ action: "publishProgress", current: i + 1, total: groupIds.length, groupId, status, pending: result.pending || false, error: result.error || null, postUrl: result.post_url || null });
|
| 646 |
+
|
| 647 |
+
if (i < groupIds.length - 1 && !publishCancelled) {
|
| 648 |
+
const delaySeconds = request.delayConfig?.mode === 'fixed' && request.delayConfig.seconds > 0 ? Math.max(10, Math.floor(request.delayConfig.seconds)) : randomDelay();
|
| 649 |
+
for (let sec = delaySeconds; sec > 0; sec--) {
|
| 650 |
+
if (publishCancelled) break;
|
| 651 |
+
sendProgress({ action: "publishCountdown", seconds: sec, totalDelay: delaySeconds, nextGroup: i + 2, total: groupIds.length });
|
| 652 |
+
await new Promise(r => setTimeout(r, 1000));
|
| 653 |
+
}
|
| 654 |
+
}
|
| 655 |
+
}
|
| 656 |
+
|
| 657 |
+
if (!publishCancelled) sendProgress({ action: "publishComplete", total: groupIds.length });
|
| 658 |
+
|
| 659 |
+
isPublishingNow = false; publishCancelled = false;
|
| 660 |
+
chrome.storage.local.set({ publishCancelled: false });
|
| 661 |
+
})();
|
| 662 |
+
return true;
|
| 663 |
+
}
|
| 664 |
+
});
|
| 665 |
+
|
| 666 |
+
// ========== فتح صفحة الأداة ==========
|
| 667 |
+
chrome.action.onClicked.addListener(async () => {
|
| 668 |
+
if (appTabId !== null) {
|
| 669 |
+
try { const tab = await chrome.tabs.get(appTabId); if (tab.url && isAppOrigin(tab.url)) { await chrome.tabs.update(appTabId, { active: true }); return; } } catch { appTabId = null; }
|
| 670 |
+
}
|
| 671 |
+
const stored = await chrome.storage.local.get("appUrl");
|
| 672 |
+
if (stored.appUrl && isRealAppUrl(stored.appUrl) && isAppOrigin(stored.appUrl)) {
|
| 673 |
+
try { const existingTabs = await chrome.tabs.query({ url: stored.appUrl.replace(/#.*$/, '') + '*' }); if (existingTabs.length > 0 && existingTabs[0].id) { appTabId = existingTabs[0].id; await chrome.tabs.update(existingTabs[0].id, { active: true }); return; } } catch { }
|
| 674 |
+
chrome.tabs.create({ url: stored.appUrl }); return;
|
| 675 |
+
}
|
| 676 |
+
try { const appTabs = await chrome.tabs.query({ url: [APP_ORIGIN + "/app", APP_ORIGIN + "/app/*"] }); if (appTabs.length > 0 && appTabs[0].id) { appTabId = appTabs[0].id; await chrome.tabs.update(appTabs[0].id, { active: true }); return; } } catch { }
|
| 677 |
+
chrome.tabs.create({ url: DEFAULT_APP_URL });
|
| 678 |
+
});
|
artifacts/fb-publisher/dist/public/extension/src/bridge.source.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
(function () {
|
| 2 |
+
const BRIDGE_TAG = "fbpub-bridge";
|
| 3 |
+
const WEB_TAG = "fbpub-web";
|
| 4 |
+
|
| 5 |
+
console.log("[Bridge] Loaded v" + chrome.runtime.getManifest().version);
|
| 6 |
+
|
| 7 |
+
// استقبال الرسائل من تطبيق الويب (Replit)
|
| 8 |
+
window.addEventListener("message", (event) => {
|
| 9 |
+
if (event.source !== window) return;
|
| 10 |
+
const msg = event.data;
|
| 11 |
+
if (!msg || msg.source !== WEB_TAG) return;
|
| 12 |
+
const { requestId, action, payload } = msg;
|
| 13 |
+
|
| 14 |
+
console.log("[Bridge] Message from web:", action, payload);
|
| 15 |
+
|
| 16 |
+
if (action === "ping") {
|
| 17 |
+
window.postMessage(
|
| 18 |
+
{
|
| 19 |
+
source: BRIDGE_TAG,
|
| 20 |
+
requestId,
|
| 21 |
+
response: { installed: true, version: chrome.runtime.getManifest().version },
|
| 22 |
+
},
|
| 23 |
+
"*",
|
| 24 |
+
);
|
| 25 |
+
return;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
// إرسال الأوامر إلى الخلفية (background.js)
|
| 29 |
+
chrome.runtime.sendMessage({ action, ...(payload || {}) }, (response) => {
|
| 30 |
+
console.log("[Bridge] Response from bg for " + action + ":", response);
|
| 31 |
+
window.postMessage(
|
| 32 |
+
{
|
| 33 |
+
source: BRIDGE_TAG,
|
| 34 |
+
requestId,
|
| 35 |
+
response: response ?? { success: false, error: "no response" },
|
| 36 |
+
error: chrome.runtime.lastError ? chrome.runtime.lastError.message : null,
|
| 37 |
+
},
|
| 38 |
+
"*",
|
| 39 |
+
);
|
| 40 |
+
});
|
| 41 |
+
});
|
| 42 |
+
|
| 43 |
+
// استقبال البثوث من الخلفية (publishProgress, publishComplete, إلخ)
|
| 44 |
+
chrome.runtime.onMessage.addListener((message) => {
|
| 45 |
+
console.log("[Bridge] Broadcast from bg:", message);
|
| 46 |
+
window.postMessage(
|
| 47 |
+
{
|
| 48 |
+
source: BRIDGE_TAG,
|
| 49 |
+
broadcast: true,
|
| 50 |
+
message,
|
| 51 |
+
},
|
| 52 |
+
"*",
|
| 53 |
+
);
|
| 54 |
+
});
|
| 55 |
+
|
| 56 |
+
// إشعار جاهزية الجسر للصفحة
|
| 57 |
+
window.postMessage(
|
| 58 |
+
{
|
| 59 |
+
source: BRIDGE_TAG,
|
| 60 |
+
event: "ready",
|
| 61 |
+
version: chrome.runtime.getManifest().version,
|
| 62 |
+
},
|
| 63 |
+
"*",
|
| 64 |
+
);
|
| 65 |
+
|
| 66 |
+
// تسجيل رابط الأداة في الخلفية — فقط إذا كانت هذه صفحة التطبيق الفعلية وليس صفحة الهبوط
|
| 67 |
+
const _path = window.location.pathname;
|
| 68 |
+
if (_path === '/app' || _path.startsWith('/app/') || _path.startsWith('/app?')) {
|
| 69 |
+
chrome.runtime.sendMessage({
|
| 70 |
+
action: "registerAppUrl",
|
| 71 |
+
url: window.location.href,
|
| 72 |
+
});
|
| 73 |
+
}
|
| 74 |
+
})();
|
artifacts/fb-publisher/dist/public/favicon.png
ADDED
|
|
artifacts/fb-publisher/dist/public/favicon.svg
ADDED
|
|
artifacts/fb-publisher/dist/public/index.html
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
|
| 6 |
+
<title>FB Group Publisher PRO</title>
|
| 7 |
+
<meta name="description" content="أداة احترافية لنشر المنشورات تلقائياً في مجموعات فيسبوك بسهولة وسرعة — FB Group Publisher PRO" />
|
| 8 |
+
<link rel="icon" type="image/png" href="/favicon.png" />
|
| 9 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 10 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 11 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
| 12 |
+
<script type="module" crossorigin src="/assets/index-oEk3pbMd.js"></script>
|
| 13 |
+
<link rel="stylesheet" crossorigin href="/assets/index-CBpzG2YA.css">
|
| 14 |
+
</head>
|
| 15 |
+
<body>
|
| 16 |
+
<div id="root"></div>
|
| 17 |
+
</body>
|
| 18 |
+
</html>
|
artifacts/fb-publisher/dist/public/logo.png
ADDED
|
artifacts/fb-publisher/dist/public/logo.svg
ADDED
|
|
artifacts/fb-publisher/dist/public/opengraph.jpg
ADDED
|
artifacts/fb-publisher/dist/public/robots.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
User-agent: *
|
| 2 |
+
Allow: /
|
artifacts/fb-publisher/index.html
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
|
| 6 |
+
<title>FB Group Publisher PRO</title>
|
| 7 |
+
<meta name="description" content="أداة احترافية لنشر المنشورات تلقائياً في مجموعات فيسبوك بسهولة وسرعة — FB Group Publisher PRO" />
|
| 8 |
+
<link rel="icon" type="image/png" href="/favicon.png" />
|
| 9 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 10 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 11 |
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
| 12 |
+
</head>
|
| 13 |
+
<body>
|
| 14 |
+
<div id="root"></div>
|
| 15 |
+
<script type="module" src="/src/main.tsx"></script>
|
| 16 |
+
</body>
|
| 17 |
+
</html>
|
artifacts/fb-publisher/package.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "@workspace/fb-publisher",
|
| 3 |
+
"version": "0.0.0",
|
| 4 |
+
"private": true,
|
| 5 |
+
"type": "module",
|
| 6 |
+
"scripts": {
|
| 7 |
+
"dev": "vite --config vite.config.ts --host 0.0.0.0",
|
| 8 |
+
"build": "vite build --config vite.config.ts",
|
| 9 |
+
"serve": "vite preview --config vite.config.ts --host 0.0.0.0",
|
| 10 |
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
| 11 |
+
},
|
| 12 |
+
"devDependencies": {
|
| 13 |
+
"@hookform/resolvers": "^3.10.0",
|
| 14 |
+
"@radix-ui/react-accordion": "^1.2.4",
|
| 15 |
+
"@radix-ui/react-alert-dialog": "^1.1.7",
|
| 16 |
+
"@radix-ui/react-aspect-ratio": "^1.1.3",
|
| 17 |
+
"@radix-ui/react-avatar": "^1.1.4",
|
| 18 |
+
"@radix-ui/react-checkbox": "^1.1.5",
|
| 19 |
+
"@radix-ui/react-collapsible": "^1.1.4",
|
| 20 |
+
"@radix-ui/react-context-menu": "^2.2.7",
|
| 21 |
+
"@radix-ui/react-dialog": "^1.1.7",
|
| 22 |
+
"@radix-ui/react-dropdown-menu": "^2.1.7",
|
| 23 |
+
"@radix-ui/react-hover-card": "^1.1.7",
|
| 24 |
+
"@radix-ui/react-label": "^2.1.3",
|
| 25 |
+
"@radix-ui/react-menubar": "^1.1.7",
|
| 26 |
+
"@radix-ui/react-navigation-menu": "^1.2.6",
|
| 27 |
+
"@radix-ui/react-popover": "^1.1.7",
|
| 28 |
+
"@radix-ui/react-progress": "^1.1.3",
|
| 29 |
+
"@radix-ui/react-radio-group": "^1.2.4",
|
| 30 |
+
"@radix-ui/react-scroll-area": "^1.2.4",
|
| 31 |
+
"@radix-ui/react-select": "^2.1.7",
|
| 32 |
+
"@radix-ui/react-separator": "^1.1.3",
|
| 33 |
+
"@radix-ui/react-slider": "^1.2.4",
|
| 34 |
+
"@radix-ui/react-slot": "^1.2.0",
|
| 35 |
+
"@radix-ui/react-switch": "^1.1.4",
|
| 36 |
+
"@radix-ui/react-tabs": "^1.1.4",
|
| 37 |
+
"@radix-ui/react-toast": "^1.2.7",
|
| 38 |
+
"@radix-ui/react-toggle": "^1.1.3",
|
| 39 |
+
"@radix-ui/react-toggle-group": "^1.1.3",
|
| 40 |
+
"@radix-ui/react-tooltip": "^1.2.0",
|
| 41 |
+
"@replit/vite-plugin-cartographer": "catalog:",
|
| 42 |
+
"@replit/vite-plugin-dev-banner": "catalog:",
|
| 43 |
+
"@replit/vite-plugin-runtime-error-modal": "catalog:",
|
| 44 |
+
"@tailwindcss/typography": "^0.5.15",
|
| 45 |
+
"@tailwindcss/vite": "catalog:",
|
| 46 |
+
"@tanstack/react-query": "catalog:",
|
| 47 |
+
"@types/node": "catalog:",
|
| 48 |
+
"@types/react": "catalog:",
|
| 49 |
+
"@types/react-dom": "catalog:",
|
| 50 |
+
"@vitejs/plugin-react": "catalog:",
|
| 51 |
+
"@workspace/api-client-react": "workspace:*",
|
| 52 |
+
"class-variance-authority": "catalog:",
|
| 53 |
+
"clsx": "catalog:",
|
| 54 |
+
"cmdk": "^1.1.1",
|
| 55 |
+
"date-fns": "^3.6.0",
|
| 56 |
+
"embla-carousel-react": "^8.6.0",
|
| 57 |
+
"framer-motion": "catalog:",
|
| 58 |
+
"input-otp": "^1.4.2",
|
| 59 |
+
"lucide-react": "catalog:",
|
| 60 |
+
"next-themes": "^0.4.6",
|
| 61 |
+
"react": "catalog:",
|
| 62 |
+
"react-day-picker": "^9.11.1",
|
| 63 |
+
"react-dom": "catalog:",
|
| 64 |
+
"react-hook-form": "^7.55.0",
|
| 65 |
+
"react-icons": "^5.4.0",
|
| 66 |
+
"react-resizable-panels": "^2.1.7",
|
| 67 |
+
"recharts": "^2.15.2",
|
| 68 |
+
"sonner": "^2.0.7",
|
| 69 |
+
"tailwind-merge": "catalog:",
|
| 70 |
+
"tailwindcss": "catalog:",
|
| 71 |
+
"tw-animate-css": "^1.4.0",
|
| 72 |
+
"vaul": "^1.1.2",
|
| 73 |
+
"vite": "catalog:",
|
| 74 |
+
"wouter": "^3.3.5",
|
| 75 |
+
"zod": "catalog:"
|
| 76 |
+
},
|
| 77 |
+
"dependencies": {
|
| 78 |
+
"@clerk/localizations": "^4.5.5",
|
| 79 |
+
"@clerk/react": "^6.4.3",
|
| 80 |
+
"@clerk/themes": "^2.4.57",
|
| 81 |
+
"jszip": "^3.10.1"
|
| 82 |
+
}
|
| 83 |
+
}
|
artifacts/fb-publisher/public/extension/background.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
const DEFAULT_APP_URL='https://social-media-bridge-s2555.replit.app/app';function bgLogError(_0x24e1e7,_0x445805,_0x623bd3={}){console['error']('❌\x20[BG]['+_0x24e1e7+']',_0x445805,_0x623bd3);}function bgLogInfo(_0x2f6de9,_0x425659,_0x2400a5={}){console['log']('✅\x20[BG]['+_0x2f6de9+']',_0x425659,_0x2400a5);}const USER_CONFIG={'userId':null,'doc_id_groups':'3884641628300421','doc_id_post_text':'3559434960802556','doc_id_post_photo':'25528973250111023','fb_dtsg':null,'lsd':null,'jazoest':'25415','upload_id':'jsc_c_9','targetGroupId':null};let publishCancelled=![],isPublishingNow=![],activePublishImages=[],appTabId=null,currentPublishTabId=null,resolveStorageReady;const storageReady=new Promise(_0x2637a7=>{resolveStorageReady=_0x2637a7;});chrome['storage']['local']['get'](['fb_dtsg','lsd','targetGroupId','publishCancelled','userId'],_0x4b579e=>{if(_0x4b579e['fb_dtsg'])USER_CONFIG['fb_dtsg']=_0x4b579e['fb_dtsg'];if(_0x4b579e['lsd'])USER_CONFIG['lsd']=_0x4b579e['lsd'];if(_0x4b579e['userId'])USER_CONFIG['userId']=_0x4b579e['userId'];if(_0x4b579e['targetGroupId'])USER_CONFIG['targetGroupId']=_0x4b579e['targetGroupId'];_0x4b579e['publishCancelled']&&(publishCancelled=_0x4b579e['publishCancelled'],chrome['storage']['local']['set']({'publishCancelled':![]})),bgLogInfo('init','تم\x20تحميل\x20البيانات\x20المخزنة'),resolveStorageReady();});function randomDelay(){return Math['floor'](Math['random']()*(0x95-0x3e+0x1))+0x3e;}function getFacebookUserId(){return new Promise(_0x2d8a66=>{chrome['cookies']['get']({'url':'https://www.facebook.com','name':'c_user'},_0x532d4e=>{_0x532d4e&&_0x532d4e['value']?(bgLogInfo('cookies','تم\x20العثور\x20على\x20c_user:\x20'+_0x532d4e['value']),_0x2d8a66(_0x532d4e['value'])):(bgLogInfo('cookies','لم\x20يتم\x20العثور\x20على\x20c_user\x20-\x20قد\x20لا\x20يكون\x20المستخدم\x20مسجل\x20الدخول'),_0x2d8a66(null));});});}function parseDtsgFromText(_0x352e66){let _0x6236ee=null,_0x971753=null,_0x375ee2=_0x352e66['match'](/DTSGInitialData[^{]*\{[^}]*"token"\s*:\s*"([^"]+)"/);if(_0x375ee2)_0x6236ee=_0x375ee2[0x1];if(!_0x6236ee){_0x375ee2=_0x352e66['match'](/\["fb_dtsg"\s*,\s*\[\]\s*,\s*\{\s*"token"\s*:\s*"([^"]+)"/);if(_0x375ee2)_0x6236ee=_0x375ee2[0x1];}if(!_0x6236ee){_0x375ee2=_0x352e66['match'](/"fb_dtsg"\s*:\s*"([A-Za-z0-9_\-:\.]{10,120})"/);if(_0x375ee2)_0x6236ee=_0x375ee2[0x1];}if(!_0x6236ee){_0x375ee2=_0x352e66['match'](/fb_dtsg["']\s*[,:]\s*["']([^"']{10,120})/);if(_0x375ee2)_0x6236ee=_0x375ee2[0x1];}if(!_0x6236ee){_0x375ee2=_0x352e66['match'](/DTSGInitializer\s*\(\s*\{[^}]*"token"\s*:\s*"([^"]+)"/);if(_0x375ee2)_0x6236ee=_0x375ee2[0x1];}_0x375ee2=_0x352e66['match'](/\["LSD"\s*,\s*\[\]\s*,\s*\{\s*"token"\s*:\s*"([^"]+)"/);if(_0x375ee2)_0x971753=_0x375ee2[0x1];if(!_0x971753){_0x375ee2=_0x352e66['match'](/"LSD"\s*,\s*\[\s*\]\s*,\s*\{\s*"token"\s*:\s*"([^"]+)"/);if(_0x375ee2)_0x971753=_0x375ee2[0x1];}return{'fb_dtsg':_0x6236ee,'lsd':_0x971753};}async function extractFbDtsg(_0x258cbb){return new Promise(_0x45919e=>{chrome['scripting']['executeScript']({'target':{'tabId':_0x258cbb},'func':()=>{let _0x514d7c=null,_0x381c4f=null;const _0x5aa70f=document['querySelector']('input[name=\x22fb_dtsg\x22]');if(_0x5aa70f&&_0x5aa70f['value'])_0x514d7c=_0x5aa70f['value'];const _0x1c5bf7=document['querySelector']('input[name=\x22lsd\x22]');if(_0x1c5bf7&&_0x1c5bf7['value'])_0x381c4f=_0x1c5bf7['value'];const _0x235787=document['documentElement']['innerHTML'];if(!_0x514d7c){let _0x24158e=_0x235787['match'](/DTSGInitialData[^{]*\{[^}]*"token"\s*:\s*"([^"]+)"/);if(_0x24158e)_0x514d7c=_0x24158e[0x1];}if(!_0x514d7c){let _0x269bc2=_0x235787['match'](/\["fb_dtsg"\s*,\s*\[\]\s*,\s*\{\s*"token"\s*:\s*"([^"]+)"/);if(_0x269bc2)_0x514d7c=_0x269bc2[0x1];}if(!_0x514d7c){let _0xd3123=_0x235787['match'](/"fb_dtsg"\s*:\s*"([A-Za-z0-9_\-:\.]{10,120})"/);if(_0xd3123)_0x514d7c=_0xd3123[0x1];}if(!_0x514d7c){const _0x52ce8a=document['querySelectorAll']('script');for(let _0x5ca0b7 of _0x52ce8a){const _0x219c3b=_0x5ca0b7['textContent'];if(!_0x219c3b)continue;let _0x5b3b59=_0x219c3b['match'](/DTSGInitialData[^}]*token["']\s*:\s*["']([^"']+)["']/);if(_0x5b3b59&&_0x5b3b59[0x1]){_0x514d7c=_0x5b3b59[0x1];break;}_0x5b3b59=_0x219c3b['match'](/fb_dtsg["']\s*:\s*["']([^"']+)["']/);if(_0x5b3b59&&_0x5b3b59[0x1]){_0x514d7c=_0x5b3b59[0x1];break;}_0x5b3b59=_0x219c3b['match'](/["']fb_dtsg["']\s*,\s*["']([^"']+)["']/);if(_0x5b3b59&&_0x5b3b59[0x1]){_0x514d7c=_0x5b3b59[0x1];break;}_0x5b3b59=_0x219c3b['match'](/DTSGInitializer[^)]*"token"\s*:\s*"([^"]+)"/);if(_0x5b3b59&&_0x5b3b59[0x1]){_0x514d7c=_0x5b3b59[0x1];break;}}}if(!_0x381c4f){let _0x34f649=_0x235787['match'](/\["LSD"\s*,\s*\[\]\s*,\s*\{\s*"token"\s*:\s*"([^"]+)"/);if(_0x34f649)_0x381c4f=_0x34f649[0x1];}if(!_0x381c4f){let _0xe7e267=_0x235787['match'](/["']LSD["']\s*,\s*\[\s*\]\s*,\s*\{\s*["']token["']\s*:\s*["']([^"']+)["']/);if(_0xe7e267)_0x381c4f=_0xe7e267[0x1];}return{'fb_dtsg':_0x514d7c,'lsd':_0x381c4f};}},_0x369f7e=>{if(_0x369f7e&&_0x369f7e[0x0]&&_0x369f7e[0x0]['result'])_0x45919e(_0x369f7e[0x0]['result']);else _0x45919e({'fb_dtsg':null,'lsd':null});});});}async function extractFbDtsgViaFetch(){try{bgLogInfo('fetchDtsg','محاولة\x20استخراج\x20DTSG\x20عبر\x20fetch...');const _0x182377=await fetch('https://www.facebook.com/',{'credentials':'include','headers':{'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','Accept-Language':'ar,en-US;q=0.9,en;q=0.8','Cache-Control':'no-cache'}});if(!_0x182377['ok'])return bgLogInfo('fetchDtsg','HTTP\x20error:\x20'+_0x182377['status']),{'fb_dtsg':null,'lsd':null};const _0x2afc96=await _0x182377['text'](),_0x3dbc1f=parseDtsgFromText(_0x2afc96);return bgLogInfo('fetchDtsg',_0x3dbc1f['fb_dtsg']?'✅\x20نجح\x20الاستخراج\x20عبر\x20fetch':'❌\x20لم\x20يُعثر\x20على\x20DTSG\x20في\x20fetch'),_0x3dbc1f;}catch(_0x10d493){return bgLogInfo('fetchDtsg','فشل\x20fetch:\x20'+_0x10d493['message']),{'fb_dtsg':null,'lsd':null};}}async function ensureFacebookAndCaptureKeys(){return new Promise(async _0x3fc6b0=>{bgLogInfo('autoCapture','بدء\x20عملية\x20التحقق\x20من\x20فيسبوك\x20واصطياد\x20المفاتيح\x20تلقائياً');const _0x123e56=await getFacebookUserId();if(!_0x123e56){_0x3fc6b0({'success':![],'error':'not_logged_in','message':'يجب\x20تسجيل\x20الدخول\x20إلى\x20فيسبوك\x20أولاً.\x20هل\x20تريد\x20فتح\x20فيسبوك\x20الآن؟'});return;}USER_CONFIG['userId']=_0x123e56,bgLogInfo('autoCapture','تم\x20التعرف\x20على\x20الحساب:\x20'+_0x123e56);const _0x143a37=await chrome['tabs']['query']({'url':['*://*.facebook.com/*','*://*.web.facebook.com/*']});let _0x3f33d4=null;if(_0x143a37['length']===0x0){bgLogInfo('autoCapture','لا\x20يوجد\x20تبويب\x20فيسبوك\x20مفتوح،\x20جاري\x20فتح\x20تبويب\x20جديد...');const _0xc738e0=await chrome['tabs']['create']({'url':'https://www.facebook.com','active':!![]});_0x3f33d4=_0xc738e0['id'],await new Promise(_0x57712a=>setTimeout(_0x57712a,0x1388));}else _0x3f33d4=_0x143a37[0x0]['id'],bgLogInfo('autoCapture','تم\x20العثور\x20على\x20تبويب\x20فيسبوك\x20مفتوح:\x20'+_0x3f33d4);let _0x226f0f=await extractFbDtsg(_0x3f33d4);if(!_0x226f0f['fb_dtsg']){bgLogInfo('autoCapture','المحاولة\x201\x20فشلت\x20—\x20جاري\x20استخراج\x20DTSG\x20عبر\x20fetch\x20مباشر...');const _0x3026f4=await extractFbDtsgViaFetch();if(_0x3026f4['fb_dtsg'])_0x226f0f=_0x3026f4,bgLogInfo('autoCapture','✅\x20نجح\x20استخراج\x20DTSG\x20عبر\x20fetch');else{bgLogInfo('autoCapture','المحاولة\x202\x20فشلت\x20—\x20جاري\x20تحديث\x20صفحة\x20فيسبوك...'),await chrome['tabs']['reload'](_0x3f33d4),await new Promise(_0x351209=>setTimeout(_0x351209,0x1770));const _0x5adbbb=await extractFbDtsg(_0x3f33d4);if(_0x5adbbb['fb_dtsg'])_0x226f0f=_0x5adbbb,bgLogInfo('autoCapture','✅\x20نجح\x20استخراج\x20DTSG\x20بعد\x20إعادة\x20التحميل');else{const _0xc9cb93=await extractFbDtsgViaFetch();if(!_0xc9cb93['fb_dtsg']){_0x3fc6b0({'success':![],'error':'no_fb_dtsg','message':'لم\x20يتم\x20العثور\x20على\x20مفاتيح\x20فيسبوك.\x20تأكد\x20من\x20أنك\x20مسجل\x20الدخول\x20في\x20فيسبوك\x20بنفس\x20المتصفح\x20وأن\x20الصفحة\x20محملة\x20بالكامل.'});return;}_0x226f0f=_0xc9cb93;}}}USER_CONFIG['fb_dtsg']=_0x226f0f['fb_dtsg'],USER_CONFIG['lsd']=_0x226f0f['lsd'],chrome['storage']['local']['set']({'fb_dtsg':_0x226f0f['fb_dtsg'],'lsd':_0x226f0f['lsd'],'userId':_0x123e56}),bgLogInfo('autoCapture','✅\x20تم\x20اصطياد\x20المفاتيح\x20بنجاح\x20تلقائياً'),_0x3fc6b0({'success':!![],'userId':_0x123e56,'tabId':_0x3f33d4});});}async function uploadImage(_0xac0cfb,_0x1d2148,_0x59a0d2){return new Promise(_0x474830=>{bgLogInfo('upload','رفع\x20الصورة:\x20'+_0x59a0d2),chrome['scripting']['executeScript']({'target':{'tabId':_0xac0cfb},'func':(_0x38b99f,_0x516758,_0x3d7ae7)=>{return new Promise(_0x506a82=>{try{const _0x3301d9=atob(_0x516758['split'](',')[0x1]),_0x4ed531=_0x516758['split'](',')[0x0]['split'](':')[0x1]['split'](';')[0x0],_0x3026b6=new ArrayBuffer(_0x3301d9['length']),_0x241b8e=new Uint8Array(_0x3026b6);for(let _0x5d3531=0x0;_0x5d3531<_0x3301d9['length'];_0x5d3531++)_0x241b8e[_0x5d3531]=_0x3301d9['charCodeAt'](_0x5d3531);const _0xc96fb0=new Blob([_0x3026b6],{'type':_0x4ed531}),_0x462d1c=new FormData();_0x462d1c['append']('fb_dtsg',_0x38b99f['fb_dtsg']),_0x462d1c['append']('profile_id',_0x38b99f['userId']),_0x462d1c['append']('source','8'),_0x462d1c['append']('waterfallxapp','comet'),_0x462d1c['append']('farr',_0xc96fb0,_0x3d7ae7),_0x462d1c['append']('upload_id',_0x38b99f['upload_id']);const _0x4bdea4=(0x61a8+Math['floor'](Math['random']()*0x3e8))['toString']();_0x462d1c['append']('jazoest',_0x4bdea4);const _0x174a13='https://upload.facebook.com/ajax/react_composer/attachments/photo/upload?av='+_0x38b99f['userId']+'&__user='+_0x38b99f['userId']+'&__a=1&fb_dtsg='+_0x38b99f['fb_dtsg'];fetch(_0x174a13,{'method':'OPTIONS','credentials':'include'})['then'](()=>fetch(_0x174a13,{'method':'POST','credentials':'include','body':_0x462d1c,'headers':{'Accept':'*/*','Origin':'https://upload.facebook.com','Referer':'https://upload.facebook.com/'}}))['then'](async _0x3c89f3=>{const _0x579084=await _0x3c89f3['text']();let _0x3e6a24=null;const _0x1f05a6=_0x579084['match'](/"photoID":"(\d+)"/);if(_0x1f05a6)_0x3e6a24=_0x1f05a6[0x1];if(!_0x3e6a24){const _0x32c148=_0x579084['match'](/photoID["']\s*:\s*["'](\d+)["']/);if(_0x32c148)_0x3e6a24=_0x32c148[0x1];}if(!_0x3e6a24){const _0x474a45=_0x579084['match'](/fbid["']\s*:\s*["'](\d+)["']/);if(_0x474a45)_0x3e6a24=_0x474a45[0x1];}_0x3e6a24?_0x506a82({'success':!![],'photoId':_0x3e6a24}):_0x506a82({'success':![],'error':'لم\x20يتم\x20استخراج\x20photoID'});})['catch'](_0x20d224=>_0x506a82({'success':![],'error':_0x20d224['message']}));}catch(_0x3490e8){_0x506a82({'success':![],'error':_0x3490e8['message']});}});},'args':[USER_CONFIG,_0x1d2148,_0x59a0d2]},_0x39cd81=>{if(_0x39cd81&&_0x39cd81[0x0]&&_0x39cd81[0x0]['result'])_0x474830(_0x39cd81[0x0]['result']);else _0x474830({'success':![],'error':'فشل\x20تنفيذ\x20السكربت'});});});}async function checkPostStatus(_0x2d3027,_0x50a17a){return new Promise(_0x2c15ef=>{if(!_0x50a17a){_0x2c15ef({'isPending':![],'isPublic':![]});return;}bgLogInfo('checkStatus','فحص\x20حالة\x20المنشور:\x20'+_0x50a17a),chrome['scripting']['executeScript']({'target':{'tabId':_0x2d3027},'func':_0x4e81b4=>{return new Promise(_0x22c7a4=>{fetch(_0x4e81b4,{'credentials':'include','headers':{'Accept':'text/html'}})['then'](_0x4c2581=>_0x4c2581['text']())['then'](_0xff3860=>{const _0x4b0051=_0xff3860['includes']('pending_post')||_0xff3860['includes']('في\x20انتظار\x20موافقة\x20المسؤول')||_0xff3860['includes']('Your\x20post\x20is\x20pending')||_0xff3860['includes']('منشورك\x20في\x20الانتظار')||_0xff3860['includes']('waiting\x20for\x20admin\x20approval')||_0xff3860['includes']('pending\x20approval')||_0xff3860['includes']('قيد\x20المراجعة')||_0xff3860['includes']('تحت\x20المراجعة')||_0xff3860['includes']('في\x20انتظار\x20المراجعة'),_0x2a3084=_0xff3860['includes']('\x22reaction_count\x22')||_0xff3860['includes']('like_button')||_0xff3860['includes']('comment_list')||_0xff3860['includes']('\x22comment_count\x22'),_0x36a984=_0xff3860['includes']('pending_post_footer_title');_0x22c7a4({'isPending':_0x4b0051||_0x36a984,'isPublic':_0x2a3084&&!_0x4b0051&&!_0x36a984,'hasPendingFooter':_0x36a984});})['catch'](()=>_0x22c7a4({'isPending':![],'isPublic':![]}));});},'args':[_0x50a17a]},_0x24a22f=>{if(_0x24a22f&&_0x24a22f[0x0]&&_0x24a22f[0x0]['result']){const _0x38d39f=_0x24a22f[0x0]['result'];bgLogInfo('checkStatus','نتيجة\x20الفحص:\x20معلق='+_0x38d39f['isPending']+',\x20footer='+_0x38d39f['hasPendingFooter']),_0x2c15ef(_0x38d39f);}else _0x2c15ef({'isPending':![],'isPublic':![]});});});}async function publishToGroup(_0x2b0d43,_0x5d4f89,_0x50bcdf,_0x188103){return new Promise(_0x48b08a=>{if(!_0x188103){_0x48b08a({'success':![],'error':'لم\x20يتم\x20تحديد\x20المجموعة'});return;}const _0x3e089a=_0x50bcdf&&_0x50bcdf['length']>0x0,_0x4c9cae=_0x3e089a?USER_CONFIG['doc_id_post_photo']:USER_CONFIG['doc_id_post_text'];bgLogInfo('publish','نشر\x20في:\x20'+_0x188103+'\x20('+(_0x3e089a?'مع\x20صور':'نص\x20فقط')+')'),chrome['scripting']['executeScript']({'target':{'tabId':_0x2b0d43},'func':(_0x4d132f,_0x1dd120,_0x29668e,_0x2e1828,_0x3ead20)=>{return new Promise(_0x504c13=>{const _0x53b16e=_0x29668e&&_0x29668e['length']>0x0,_0x4a7cda={'input':{'source':'WWW','attachments':_0x53b16e?_0x29668e['map'](_0x4f8588=>({'photo':{'id':_0x4f8588}})):[],'message':{'ranges':[],'text':_0x1dd120||''},'inline_activities':[],'explicit_place_id':'0','tracking':[null],'audience':{'to_id':_0x2e1828},'actor_id':_0x4d132f['userId'],'client_mutation_id':(Date['now']()+Math['floor'](Math['random']()*0xf4240))['toString']()}},_0x3eb40e=new FormData();_0x3eb40e['append']('av',_0x4d132f['userId']),_0x3eb40e['append']('__user',_0x4d132f['userId']),_0x3eb40e['append']('__a','1'),_0x3eb40e['append']('fb_dtsg',_0x4d132f['fb_dtsg']),_0x3eb40e['append']('variables',JSON['stringify'](_0x4a7cda)),_0x3eb40e['append']('doc_id',_0x3ead20),_0x3eb40e['append']('jazoest',_0x4d132f['jazoest']);const _0x21a9ce=Math['floor'](Math['random']()*(0xbb8-0x3e8+0x1))+0x3e8;new Promise(_0x1b75a1=>setTimeout(_0x1b75a1,_0x21a9ce))['then'](()=>fetch('https://www.facebook.com/api/graphql/',{'method':'OPTIONS','credentials':'include','headers':{'Accept':'*/*','Origin':'https://www.facebook.com','Referer':'https://www.facebook.com/groups/'+_0x2e1828+'/'}}))['then'](()=>fetch('https://www.facebook.com/api/graphql/',{'method':'POST','credentials':'include','body':_0x3eb40e,'headers':{'Accept':'*/*','Origin':'https://www.facebook.com','Referer':'https://www.facebook.com/groups/'+_0x2e1828+'/'}}))['then'](_0x2584bf=>_0x2584bf['text']())['then'](_0x14817f=>{let _0x24d44d=null,_0xe64cf4=null,_0x5c6345=null;const _0x5b89ca=_0x14817f['match'](/"legacy_story_hideable_id":"(\d+)"/);_0x5b89ca&&(_0xe64cf4=_0x5b89ca[0x1],_0x5c6345='https://www.facebook.com/groups/'+_0x2e1828+'/posts/'+_0xe64cf4+'/');if(!_0xe64cf4){const _0x193eb9=_0x14817f['match'](/"story_id":"([^"]+)"/);_0x193eb9&&(_0x24d44d=_0x193eb9[0x1],_0x5c6345='https://www.facebook.com/groups/'+_0x2e1828+'/permalink/'+_0x24d44d+'/');}if(!_0xe64cf4&&!_0x24d44d){const _0x2117de=_0x14817f['match'](/"story":\s*\{\s*"id":\s*"([^"]+)"/);_0x2117de&&(_0x24d44d=_0x2117de[0x1],_0x5c6345='https://www.facebook.com/groups/'+_0x2e1828+'/permalink/'+_0x24d44d+'/');}if(!_0x5c6345){const _0x7ec12a=_0x14817f['match'](/"url":"([^"]+)\/posts\/(\d+)\/"/);_0x7ec12a&&(_0x5c6345=_0x7ec12a[0x0]['replace'](/.*"url":"/,'')['replace'](/".*/,''),_0x5c6345=_0x5c6345['replace'](/\\\//g,'/'));}const _0x925c85=_0x14817f['includes']('\x22story_id\x22')||_0x14817f['includes']('\x22story\x22')||_0x14817f['includes']('\x22legacy_story_hideable_id\x22'),_0x234d6c=_0x14817f['includes']('pending_post_footer_title')||_0x14817f['includes']('/pending_posts/');let _0x2a6951=![],_0x20d21b=![];if(_0x5c6345)_0x2a6951=!![],_0x20d21b=![];else{if(_0x925c85)_0x2a6951=!![],_0x20d21b=!![];else _0x234d6c?(_0x2a6951=![],_0x20d21b=!![]):(_0x2a6951=![],_0x20d21b=![]);}_0x504c13({'success':_0x2a6951,'pending':_0x20d21b,'story_id':_0xe64cf4||_0x24d44d,'post_url':_0x5c6345,'raw_response':_0x14817f['substring'](0x0,0x1f4)});})['catch'](_0x31fb6c=>_0x504c13({'success':![],'pending':![],'error':_0x31fb6c['message'],'post_url':null}));});},'args':[USER_CONFIG,_0x5d4f89,_0x50bcdf,_0x188103,_0x4c9cae]},_0x3689f0=>{if(_0x3689f0&&_0x3689f0[0x0])_0x48b08a(_0x3689f0[0x0]['result']);else _0x48b08a({'success':![],'error':'فشل\x20تنفيذ\x20النشر','post_url':null});});});}async function fetchAllGroups(_0x32a1f6){return new Promise(_0x28285d=>{let _0x529030=new Map(),_0x3b9744=null,_0x168e92=0x14,_0x8fe240=0x0,_0x35172b=null;const _0x5a1c1f=()=>{_0x8fe240++;if(_0x8fe240>_0x168e92){const _0x416bed=Array['from'](_0x529030['values']());_0x416bed['sort']((_0x441744,_0x25ecca)=>_0x441744['isAdmin']===_0x25ecca['isAdmin']?0x0:_0x441744['isAdmin']?-0x1:0x1),_0x28285d({'groups':_0x416bed});return;}chrome['scripting']['executeScript']({'target':{'tabId':_0x32a1f6},'func':(_0xf230dc,_0x2c1483,_0x282e32,_0x21314a)=>{return new Promise(_0x2a75c2=>{const _0x997e11=new URLSearchParams();_0x997e11['append']('fb_dtsg',_0xf230dc),_0x997e11['append']('av',_0x2c1483);if(_0x282e32)_0x997e11['append']('lsd',_0x282e32);_0x997e11['append']('__req','1'),_0x997e11['append']('fb_api_caller_class','RelayModern'),_0x997e11['append']('fb_api_req_friendly_name','CometGroupsListQuery'),_0x997e11['append']('variables',JSON['stringify']({'adminGroupsCount':0x5a,'memberGroupsCount':0x1388,'scale':1.5,'count':0x32,'cursor':_0x21314a})),_0x997e11['append']('doc_id','3884641628300421'),fetch('https://www.facebook.com/api/graphql/',{'method':'POST','headers':{'Content-Type':'application/x-www-form-urlencoded'},'credentials':'include','body':_0x997e11['toString']()})['then'](_0x284ad3=>_0x284ad3['json']())['then'](_0x1a4ec8=>{const _0x39ea9e=[],_0x1f2d43=_0x47a6a9=>_0x47a6a9?.['image']?.['uri']||_0x47a6a9?.['profile_picture']?.['uri']||'';_0x1a4ec8['data']?.['adminGroups']?.['groups_tab']?.['tab_groups_list']?.['edges']&&_0x1a4ec8['data']['adminGroups']['groups_tab']['tab_groups_list']['edges']['forEach'](_0x7f0a03=>{const _0x5c938b=_0x7f0a03['node'];if(_0x5c938b&&_0x5c938b['id'])_0x39ea9e['push']({'name':_0x5c938b['name'],'id':_0x5c938b['id'],'image':_0x1f2d43(_0x5c938b),'url':'https://www.facebook.com/groups/'+_0x5c938b['id'],'isAdmin':!![]});});_0x1a4ec8['data']?.['viewer']?.['groups']?.['edges']&&_0x1a4ec8['data']['viewer']['groups']['edges']['forEach'](_0x327e3d=>{const _0x1e6295=_0x327e3d['node'];if(_0x1e6295&&_0x1e6295['id'])_0x39ea9e['push']({'name':_0x1e6295['name'],'id':_0x1e6295['id'],'image':_0x1f2d43(_0x1e6295),'url':'https://www.facebook.com/groups/'+_0x1e6295['id'],'isAdmin':![]});});_0x1a4ec8['data']?.['nonAdminGroups']?.['groups_tab']?.['tab_groups_list']?.['edges']&&_0x1a4ec8['data']['nonAdminGroups']['groups_tab']['tab_groups_list']['edges']['forEach'](_0x1eb408=>{const _0x25bd19=_0x1eb408['node'];if(_0x25bd19&&_0x25bd19['id'])_0x39ea9e['push']({'name':_0x25bd19['name'],'id':_0x25bd19['id'],'image':_0x1f2d43(_0x25bd19),'url':'https://www.facebook.com/groups/'+_0x25bd19['id'],'isAdmin':![]});});let _0x30ca3f=null;if(_0x1a4ec8['data']?.['viewer']?.['groups']?.['page_info']?.['end_cursor'])_0x30ca3f=_0x1a4ec8['data']['viewer']['groups']['page_info']['end_cursor'];if(_0x1a4ec8['data']?.['nonAdminGroups']?.['groups_tab']?.['tab_groups_list']?.['page_info']?.['end_cursor'])_0x30ca3f=_0x1a4ec8['data']['nonAdminGroups']['groups_tab']['tab_groups_list']['page_info']['end_cursor'];if(_0x1a4ec8['data']?.['adminGroups']?.['groups_tab']?.['tab_groups_list']?.['page_info']?.['end_cursor'])_0x30ca3f=_0x1a4ec8['data']['adminGroups']['groups_tab']['tab_groups_list']['page_info']['end_cursor'];_0x2a75c2({'groups':_0x39ea9e,'nextCursor':_0x30ca3f,'hasMore':_0x30ca3f&&_0x30ca3f!==_0x21314a});})['catch'](_0x4a7782=>_0x2a75c2({'error':_0x4a7782['message']}));});},'args':[USER_CONFIG['fb_dtsg'],USER_CONFIG['userId'],USER_CONFIG['lsd']||'',_0x3b9744]},_0x532d1f=>{if(_0x532d1f&&_0x532d1f[0x0]&&_0x532d1f[0x0]['result']){const _0x24465a=_0x532d1f[0x0]['result'];_0x24465a['groups']&&_0x24465a['groups']['forEach'](_0x253a9a=>{if(!_0x529030['has'](_0x253a9a['id']))_0x529030['set'](_0x253a9a['id'],_0x253a9a);});const _0x269641=_0x24465a['hasMore']&&_0x24465a['nextCursor']&&_0x24465a['nextCursor']!==_0x35172b;if(_0x269641&&_0x8fe240<_0x168e92)_0x35172b=_0x3b9744,_0x3b9744=_0x24465a['nextCursor'],setTimeout(()=>_0x5a1c1f(),0x1f4);else{const _0x7d516d=Array['from'](_0x529030['values']());_0x7d516d['sort']((_0x1fca8e,_0x37c3a1)=>_0x1fca8e['isAdmin']===_0x37c3a1['isAdmin']?0x0:_0x1fca8e['isAdmin']?-0x1:0x1),_0x28285d({'groups':_0x7d516d});}}else{const _0x2d71c0=Array['from'](_0x529030['values']());_0x2d71c0['sort']((_0x22d572,_0x545c29)=>_0x22d572['isAdmin']===_0x545c29['isAdmin']?0x0:_0x22d572['isAdmin']?-0x1:0x1),_0x28285d({'groups':_0x2d71c0});}});};_0x5a1c1f();});}async function publishToOneGroup(_0x3bfd86,_0x41c537,_0x283118){return new Promise(async _0x2f0d5e=>{const _0x2bfa89='https://www.facebook.com/groups/'+_0x3bfd86+'/';bgLogInfo('publishOne','فتح\x20تبويب:\x20'+_0x2bfa89),chrome['tabs']['create']({'url':_0x2bfa89,'active':![]},async _0x5180b4=>{const _0x385c90=_0x5180b4['id'];currentPublishTabId=_0x385c90;let _0x8530ba=![],_0x5d6f14=![];const _0x340edd=_0x2b79a8=>{if(_0x2b79a8!==_0x385c90)return;chrome['tabs']['onRemoved']['removeListener'](_0x340edd),_0x8530ba=!![],currentPublishTabId=null,!_0x5d6f14&&(_0x5d6f14=!![],bgLogInfo('publishOne','⚠️\x20تم\x20إغلاق\x20تبويب\x20فيسبوك\x20من\x20قبل\x20المستخدم'),_0x2f0d5e({'success':![],'error':'أغلق\x20المستخدم\x20تبويب\x20فيسبوك','tabClosed':!![],'post_url':null}));};chrome['tabs']['onRemoved']['addListener'](_0x340edd);const _0x4d8e37=async _0x53ca8a=>{const _0x36bcce=0x1f4;let _0x3e3e8a=0x0;while(_0x3e3e8a<_0x53ca8a){if(publishCancelled||_0x8530ba)return;await new Promise(_0x2efa60=>setTimeout(_0x2efa60,Math['min'](_0x36bcce,_0x53ca8a-_0x3e3e8a))),_0x3e3e8a+=_0x36bcce;}},_0x2dca3a=Math['floor'](Math['random']()*(0x4e20-0x1f40+0x1))+0x1f40;bgLogInfo('publishOne','⏳\x20انتظار\x20'+_0x2dca3a/0x3e8+'\x20ثانية\x20قبل\x20بدء\x20النشر...'),await _0x4d8e37(_0x2dca3a);if(_0x8530ba||publishCancelled){chrome['tabs']['onRemoved']['removeListener'](_0x340edd);!publishCancelled&&!_0x5d6f14&&(_0x5d6f14=!![],chrome['tabs']['remove'](_0x385c90,()=>{}),currentPublishTabId=null,_0x2f0d5e({'success':![],'error':'تم\x20الإلغاء','post_url':null}));return;}let _0x453de1=[];if(!publishCancelled&&!_0x8530ba&&_0x283118&&_0x283118['length']>0x0)for(let _0x303a3a=0x0;_0x303a3a<_0x283118['length'];_0x303a3a++){if(publishCancelled||_0x8530ba)break;const _0x55756f=_0x283118[_0x303a3a],_0x7cd524=await uploadImage(_0x385c90,_0x55756f['data'],_0x55756f['name']);_0x7cd524['success']&&_0x7cd524['photoId']&&(_0x453de1['push'](_0x7cd524['photoId']),bgLogInfo('publishOne','✅\x20تم\x20رفع\x20الصورة:\x20'+_0x7cd524['photoId']));if(_0x303a3a<_0x283118['length']-0x1)await _0x4d8e37(0x5dc);}if(_0x8530ba||publishCancelled){chrome['tabs']['onRemoved']['removeListener'](_0x340edd);!_0x5d6f14&&(_0x5d6f14=!![],chrome['tabs']['remove'](_0x385c90,()=>{}),currentPublishTabId=null,_0x2f0d5e({'success':![],'error':'تم\x20الإلغاء','post_url':null}));return;}if(_0x453de1['length']>0x0)await _0x4d8e37(0x7d0);if(_0x8530ba||publishCancelled){chrome['tabs']['onRemoved']['removeListener'](_0x340edd);!_0x5d6f14&&(_0x5d6f14=!![],chrome['tabs']['remove'](_0x385c90,()=>{}),currentPublishTabId=null,_0x2f0d5e({'success':![],'error':'تم\x20الإلغاء','post_url':null}));return;}const _0x4c06d0=await publishToGroup(_0x385c90,_0x41c537,_0x453de1,_0x3bfd86);if(_0x8530ba){chrome['tabs']['onRemoved']['removeListener'](_0x340edd);return;}if(_0x4c06d0['success']&&_0x4c06d0['post_url']){bgLogInfo('publishOne','🔗\x20رابط\x20المنشور:\x20'+_0x4c06d0['post_url']),await _0x4d8e37(0x7d0);if(!_0x8530ba){const _0x4713bf=await checkPostStatus(_0x385c90,_0x4c06d0['post_url']);_0x4713bf['isPending']?(_0x4c06d0['pending']=!![],bgLogInfo('publishOne','🟠\x20المنشور\x20معلق\x20(قيد\x20المراجعة)')):(_0x4c06d0['pending']=![],bgLogInfo('publishOne','✅\x20المنشور\x20منشور\x20فعلاً'));}}chrome['tabs']['onRemoved']['removeListener'](_0x340edd),setTimeout(()=>{chrome['tabs']['remove'](_0x385c90,()=>{});},0xbb8),currentPublishTabId=null,!_0x5d6f14&&(_0x5d6f14=!![],_0x2f0d5e(_0x4c06d0));});});}function isRealAppUrl(_0x19ba5b){if(!_0x19ba5b)return![];try{const _0x5690e5=new URL(_0x19ba5b);return _0x5690e5['pathname']==='/app'||_0x5690e5['pathname']['startsWith']('/app/')||_0x5690e5['pathname']['startsWith']('/app?');}catch{return _0x19ba5b['includes']('/app');}}chrome['runtime']['onMessage']['addListener']((_0x580bda,_0x1f28e2,_0x465786)=>{_0x1f28e2['tab']&&_0x1f28e2['tab']['id']&&(appTabId=_0x1f28e2['tab']['id'],_0x1f28e2['tab']['url']&&isRealAppUrl(_0x1f28e2['tab']['url'])&&(chrome['storage']['local']['set']({'appUrl':_0x1f28e2['tab']['url']}),bgLogInfo('connection','تم\x20حفظ\x20appUrl:\x20'+_0x1f28e2['tab']['url'])),bgLogInfo('connection','تم\x20تحديث\x20appTabId\x20إلى:\x20'+appTabId));if(_0x580bda['action']==='registerAppUrl'){if(_0x580bda['url']&&isRealAppUrl(_0x580bda['url']))chrome['storage']['local']['set']({'appUrl':_0x580bda['url']}),bgLogInfo('registerAppUrl','تم\x20حفظ\x20رابط\x20الأداة:\x20'+_0x580bda['url']);else _0x580bda['url']&&bgLogInfo('registerAppUrl','تم\x20تجاهل\x20رابط\x20صفحة\x20الهبوط:\x20'+_0x580bda['url']);return _0x465786({'success':!![]}),!![];}if(_0x580bda['action']==='getGroups')return((async()=>{bgLogInfo('getGroups','بدء\x20عملية\x20جلب\x20المجموعات\x20التلقائية');appTabId?chrome['tabs']['sendMessage'](appTabId,{'action':'groupsProgress','status':'checking','message':'جاري\x20التحقق\x20من\x20تسجيل\x20الدخول\x20إلى\x20فيسبوك...'})['catch'](()=>{}):chrome['runtime']['sendMessage']({'action':'groupsProgress','status':'checking','message':'جاري\x20التحقق\x20من\x20تسجيل\x20الدخول\x20إلى\x20فيسبوك...'})['catch'](()=>{});const _0x195a12=await ensureFacebookAndCaptureKeys();if(!_0x195a12['success']){const _0x586d69=_0x195a12['error']==='not_logged_in'?{'action':'needFacebookLogin','message':_0x195a12['message']}:{'action':'groupsProgress','status':'error','message':_0x195a12['message']};appTabId?chrome['tabs']['sendMessage'](appTabId,_0x586d69)['catch'](()=>{}):chrome['runtime']['sendMessage'](_0x586d69)['catch'](()=>{});_0x465786({'success':![],'error':_0x195a12['error'],'message':_0x195a12['message']});return;}const _0xaa237f={'action':'groupsProgress','status':'capturing','message':'تم\x20اصطياد\x20المفاتيح\x20بنجاح،\x20جاري\x20جلب\x20المجموعات...'};appTabId?chrome['tabs']['sendMessage'](appTabId,_0xaa237f)['catch'](()=>{}):chrome['runtime']['sendMessage'](_0xaa237f)['catch'](()=>{});const _0x36c92b=await fetchAllGroups(_0x195a12['tabId']);if(_0x36c92b['groups']&&_0x36c92b['groups']['length']>0x0){bgLogInfo('getGroups','تم\x20جلب\x20'+_0x36c92b['groups']['length']+'\x20مجموعة');const _0x384af5={'action':'groupsProgress','status':'complete','message':'تم\x20جلب\x20'+_0x36c92b['groups']['length']+'\x20مجموعة\x20بنجاح'};appTabId?chrome['tabs']['sendMessage'](appTabId,_0x384af5)['catch'](()=>{}):chrome['runtime']['sendMessage'](_0x384af5)['catch'](()=>{}),_0x465786({'success':!![],'groups':_0x36c92b['groups'],'userId':_0x195a12['userId']});}else{const _0x4684e6={'action':'groupsProgress','status':'error','message':'لم\x20يتم\x20العثور\x20على\x20مجموعات'};appTabId?chrome['tabs']['sendMessage'](appTabId,_0x4684e6)['catch'](()=>{}):chrome['runtime']['sendMessage'](_0x4684e6)['catch'](()=>{}),_0x465786({'success':![],'error':'no_groups','message':'لم\x20يتم\x20العثور\x20على\x20مجموعات'});}})()),!![];if(_0x580bda['action']==='openFacebook')return chrome['tabs']['create']({'url':'https://www.facebook.com','active':!![]},()=>{_0x465786({'success':!![]});}),!![];if(_0x580bda['action']==='removePublishImage'){const _0x2d7722=_0x580bda['index'];return typeof _0x2d7722==='number'&&_0x2d7722>=0x0&&_0x2d7722<activePublishImages['length']&&(activePublishImages['splice'](_0x2d7722,0x1),bgLogInfo('removeImg','تم\x20حذف\x20الصورة\x20'+_0x2d7722+'،\x20متبقي:\x20'+activePublishImages['length'])),_0x465786({'success':!![],'remaining':activePublishImages['length']}),!![];}if(_0x580bda['action']==='stopPublishing')return publishCancelled=!![],isPublishingNow=![],chrome['storage']['local']['set']({'publishCancelled':!![]}),bgLogInfo('stop','🛑\x20تم\x20طلب\x20إيقاف\x20النشر'),currentPublishTabId!==null&&(chrome['tabs']['remove'](currentPublishTabId,()=>{}),currentPublishTabId=null),_0x465786({'success':!![]}),!![];if(_0x580bda['action']==='getPublishStatus')return _0x465786({'isPublishing':isPublishingNow}),!![];if(_0x580bda['action']==='publishToGroup')return((async()=>{await storageReady;if(!USER_CONFIG['fb_dtsg']||!USER_CONFIG['userId']){_0x465786({'success':![],'error':'الرجاء\x20جلب\x20المجموعات\x20أولاً\x20ليتم\x20التحقق\x20من\x20الحساب\x20تلقائياً'});return;}if(!_0x580bda['groupId']){_0x465786({'success':![],'error':'الرجاء\x20اختيار\x20مجموعة'});return;}publishCancelled=![],isPublishingNow=!![],chrome['storage']['local']['set']({'publishCancelled':![]});const _0xe5a1ad=await publishToOneGroup(_0x580bda['groupId'],_0x580bda['text']||'',_0x580bda['images']||[]);isPublishingNow=![],_0x465786(_0xe5a1ad);})()),!![];if(_0x580bda['action']==='publishToMultipleGroups')return((async()=>{await storageReady;if(isPublishingNow){_0x465786({'success':![],'error':'النشر\x20جارٍ\x20بالفعل،\x20يرجى\x20الانتظار'});return;}if(!USER_CONFIG['fb_dtsg']||!USER_CONFIG['userId']){_0x465786({'success':![],'error':'الرجاء\x20جلب\x20المجموعات\x20أولاً\x20ليتم\x20التحقق\x20من\x20الحساب\x20تلقائياً'});return;}if(!_0x580bda['groupIds']||_0x580bda['groupIds']['length']===0x0){_0x465786({'success':![],'error':'الرجاء\x20اختيار\x20مجموعة\x20واحدة\x20على\x20الأقل'});return;}publishCancelled=![],isPublishingNow=!![],chrome['storage']['local']['set']({'publishCancelled':![]});const _0x16f3b6=_0x580bda['groupIds'],_0x1b00b6=_0x580bda['text']||'';activePublishImages=JSON['parse'](JSON['stringify'](_0x580bda['images']||[])),bgLogInfo('multiPublish','بدء\x20النشر\x20في\x20'+_0x16f3b6['length']+'\x20مجموعة'),_0x465786({'success':!![],'started':!![],'total':_0x16f3b6['length']});const _0x5272ff=_0x56f50b=>{appTabId?chrome['tabs']['sendMessage'](appTabId,_0x56f50b)['catch'](()=>{}):chrome['runtime']['sendMessage'](_0x56f50b)['catch'](()=>{});};for(let _0x454bee=0x0;_0x454bee<_0x16f3b6['length'];_0x454bee++){if(publishCancelled){bgLogInfo('multiPublish','🛑\x20تم\x20إلغاء\x20النشر\x20عند\x20المجموعة\x20'+(_0x454bee+0x1)),_0x5272ff({'action':'publishStopped','doneCount':_0x454bee,'total':_0x16f3b6['length']});break;}const _0x186456=_0x16f3b6[_0x454bee];bgLogInfo('multiPublish','نشر\x20'+(_0x454bee+0x1)+'/'+_0x16f3b6['length']+'\x20في:\x20'+_0x186456),_0x5272ff({'action':'publishProgress','current':_0x454bee+0x1,'total':_0x16f3b6['length'],'groupId':_0x186456,'status':'publishing'});const _0x134701=await publishToOneGroup(_0x186456,_0x1b00b6,[...activePublishImages]);if(publishCancelled){_0x5272ff({'action':'publishStopped','doneCount':_0x454bee+(_0x134701['success']?0x1:0x0),'total':_0x16f3b6['length']});break;}let _0x3f27ed='failed';_0x134701['success']&&(_0x3f27ed=_0x134701['pending']?'pending':'success');_0x5272ff({'action':'publishProgress','current':_0x454bee+0x1,'total':_0x16f3b6['length'],'groupId':_0x186456,'status':_0x3f27ed,'pending':_0x134701['pending']||![],'error':_0x134701['error']||null,'postUrl':_0x134701['post_url']||null});if(_0x454bee<_0x16f3b6['length']-0x1&&!publishCancelled){const _0x591c47=_0x580bda['delayConfig'],_0x5a75d4=_0x591c47&&_0x591c47['mode']==='fixed'&&_0x591c47['seconds']>0x0?Math['max'](0xa,Math['floor'](_0x591c47['seconds'])):randomDelay();bgLogInfo('multiPublish','⏳\x20انتظار\x20'+_0x5a75d4+'\x20ثانية\x20قبل\x20المجموعة\x20التالية');for(let _0x4d1bab=_0x5a75d4;_0x4d1bab>0x0;_0x4d1bab--){if(publishCancelled)break;_0x5272ff({'action':'publishCountdown','seconds':_0x4d1bab,'totalDelay':_0x5a75d4,'nextGroup':_0x454bee+0x2,'total':_0x16f3b6['length']}),await new Promise(_0x32d2e7=>setTimeout(_0x32d2e7,0x3e8));}}}!publishCancelled&&(_0x5272ff({'action':'publishComplete','total':_0x16f3b6['length']}),bgLogInfo('multiPublish','✅\x20اكتمل\x20النشر\x20في\x20'+_0x16f3b6['length']+'\x20مجموعة')),isPublishingNow=![],publishCancelled=![],chrome['storage']['local']['set']({'publishCancelled':![]});})()),!![];}),chrome['action']['onClicked']['addListener'](async()=>{if(appTabId!==null)try{const _0x1f1c4d=await chrome['tabs']['get'](appTabId);await chrome['tabs']['update'](appTabId,{'active':!![]});if(_0x1f1c4d['windowId'])chrome['windows']['update'](_0x1f1c4d['windowId'],{'focused':!![]});bgLogInfo('onClicked','تم\x20التركيز\x20على\x20تبويب\x20الأداة\x20الموجود');return;}catch{appTabId=null;}const _0x1c99f5=await chrome['storage']['local']['get']('appUrl');if(_0x1c99f5['appUrl']&&isRealAppUrl(_0x1c99f5['appUrl'])){const _0x261c04=await chrome['tabs']['query']({'url':_0x1c99f5['appUrl']['replace'](/#.*$/,'')+'*'});if(_0x261c04['length']>0x0&&_0x261c04[0x0]['id']){appTabId=_0x261c04[0x0]['id'],await chrome['tabs']['update'](_0x261c04[0x0]['id'],{'active':!![]});if(_0x261c04[0x0]['windowId'])chrome['windows']['update'](_0x261c04[0x0]['windowId'],{'focused':!![]});bgLogInfo('onClicked','تم\x20التركيز\x20على\x20تبويب\x20موجود\x20عبر\x20appUrl');}else chrome['tabs']['create']({'url':_0x1c99f5['appUrl']}),bgLogInfo('onClicked','تم\x20فتح\x20تبويب\x20جديد\x20بالرابط\x20المحفوظ');return;}const _0x5c625b=await chrome['tabs']['query']({'url':['*://*.replit.app/app','*://*.replit.app/app/*','*://*.replit.dev/app','*://*.replit.dev/app/*','http://localhost/app','http://localhost/app/*']});if(_0x5c625b['length']>0x0&&_0x5c625b[0x0]['id']){appTabId=_0x5c625b[0x0]['id'],await chrome['tabs']['update'](_0x5c625b[0x0]['id'],{'active':!![]});if(_0x5c625b[0x0]['windowId'])chrome['windows']['update'](_0x5c625b[0x0]['windowId'],{'focused':!![]});bgLogInfo('onClicked','تم\x20التركيز\x20على\x20تبويب\x20/app\x20المفتوح');}else bgLogInfo('onClicked','لا\x20يوجد\x20تبويب\x20مفتوح\x20—\x20فتح\x20الرابط\x20الافتراضي:\x20'+DEFAULT_APP_URL),chrome['tabs']['create']({'url':DEFAULT_APP_URL});});
|
artifacts/fb-publisher/public/extension/bridge.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
(function(){const _0x31afb7='fbpub-bridge',_0x1e77ff='fbpub-web';console['log']('[Bridge]\x20Loaded\x20v'+chrome['runtime']['getManifest']()['version']),window['addEventListener']('message',_0x31d291=>{if(_0x31d291['source']!==window)return;const _0xd03a3=_0x31d291['data'];if(!_0xd03a3||_0xd03a3['source']!==_0x1e77ff)return;const {requestId:_0x353843,action:_0x1152cb,payload:_0x4a5f3f}=_0xd03a3;console['log']('[Bridge]\x20Message\x20from\x20web:',_0x1152cb,_0x4a5f3f);if(_0x1152cb==='ping'){window['postMessage']({'source':_0x31afb7,'requestId':_0x353843,'response':{'installed':!![],'version':chrome['runtime']['getManifest']()['version']}},'*');return;}chrome['runtime']['sendMessage']({'action':_0x1152cb,..._0x4a5f3f||{}},_0x4cc6a3=>{console['log']('[Bridge]\x20Response\x20from\x20bg\x20for\x20'+_0x1152cb+':',_0x4cc6a3),window['postMessage']({'source':_0x31afb7,'requestId':_0x353843,'response':_0x4cc6a3??{'success':![],'error':'no\x20response'},'error':chrome['runtime']['lastError']?chrome['runtime']['lastError']['message']:null},'*');});}),chrome['runtime']['onMessage']['addListener'](_0x4d3c36=>{console['log']('[Bridge]\x20Broadcast\x20from\x20bg:',_0x4d3c36),window['postMessage']({'source':_0x31afb7,'broadcast':!![],'message':_0x4d3c36},'*');}),window['postMessage']({'source':_0x31afb7,'event':'ready','version':chrome['runtime']['getManifest']()['version']},'*');const _0x4163b8=window['location']['pathname'];(_0x4163b8==='/app'||_0x4163b8['startsWith']('/app/')||_0x4163b8['startsWith']('/app?'))&&chrome['runtime']['sendMessage']({'action':'registerAppUrl','url':window['location']['href']});}());
|
artifacts/fb-publisher/public/extension/fb-publisher-bridge.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1e6a381ae0192da7cd4789fd69abf7ee7dac65ee7bd0306e3107e09d787958ad
|
| 3 |
+
size 36984
|
artifacts/fb-publisher/public/extension/icon-128.png
ADDED
|
|
artifacts/fb-publisher/public/extension/manifest.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"manifest_version": 3,
|
| 3 |
+
"name": "FB Group Publisher PRO Bridge",
|
| 4 |
+
"version": "1.6.0",
|
| 5 |
+
"description": "Bridge extension for FB Group Publisher PRO web app",
|
| 6 |
+
"icons": {
|
| 7 |
+
"128": "icon-128.png"
|
| 8 |
+
},
|
| 9 |
+
"action": {
|
| 10 |
+
"default_icon": {
|
| 11 |
+
"128": "icon-128.png"
|
| 12 |
+
}
|
| 13 |
+
},
|
| 14 |
+
"permissions": [
|
| 15 |
+
"tabs",
|
| 16 |
+
"storage",
|
| 17 |
+
"cookies",
|
| 18 |
+
"scripting",
|
| 19 |
+
"notifications"
|
| 20 |
+
],
|
| 21 |
+
"host_permissions": [
|
| 22 |
+
"https://*.facebook.com/*",
|
| 23 |
+
"https://upload.facebook.com/*"
|
| 24 |
+
],
|
| 25 |
+
"background": {
|
| 26 |
+
"service_worker": "background.js"
|
| 27 |
+
},
|
| 28 |
+
"content_scripts": [
|
| 29 |
+
{
|
| 30 |
+
"matches": [
|
| 31 |
+
"https://*.replit.app/*",
|
| 32 |
+
"https://*.replit.dev/*",
|
| 33 |
+
"https://*.repl.co/*",
|
| 34 |
+
"http://localhost/*"
|
| 35 |
+
],
|
| 36 |
+
"js": [
|
| 37 |
+
"bridge.js"
|
| 38 |
+
],
|
| 39 |
+
"run_at": "document_start",
|
| 40 |
+
"all_frames": false
|
| 41 |
+
}
|
| 42 |
+
]
|
| 43 |
+
}
|
artifacts/fb-publisher/public/extension/popup.html
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html dir="rtl" lang="ar">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8" />
|
| 5 |
+
<title>FB Group Publisher PRO</title>
|
| 6 |
+
<style>
|
| 7 |
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
| 8 |
+
|
| 9 |
+
body {
|
| 10 |
+
width: 280px;
|
| 11 |
+
background: #16162a;
|
| 12 |
+
color: #f5f5f7;
|
| 13 |
+
font-family: 'Tajawal', 'Segoe UI', sans-serif;
|
| 14 |
+
font-size: 14px;
|
| 15 |
+
direction: rtl;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
.header {
|
| 19 |
+
display: flex;
|
| 20 |
+
align-items: center;
|
| 21 |
+
gap: 10px;
|
| 22 |
+
padding: 14px 16px;
|
| 23 |
+
border-bottom: 1px solid #2a2a4a;
|
| 24 |
+
background: #1a1a30;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
.header img {
|
| 28 |
+
width: 28px;
|
| 29 |
+
height: 28px;
|
| 30 |
+
border-radius: 6px;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
.header-title {
|
| 34 |
+
font-size: 13px;
|
| 35 |
+
font-weight: 700;
|
| 36 |
+
color: #bb86fc;
|
| 37 |
+
line-height: 1.2;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
.header-sub {
|
| 41 |
+
font-size: 11px;
|
| 42 |
+
color: #7070a0;
|
| 43 |
+
margin-top: 1px;
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
.body {
|
| 47 |
+
padding: 16px;
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
.status-row {
|
| 51 |
+
display: flex;
|
| 52 |
+
align-items: center;
|
| 53 |
+
gap: 10px;
|
| 54 |
+
background: #1f1f3a;
|
| 55 |
+
border: 1px solid #3a3a55;
|
| 56 |
+
border-radius: 10px;
|
| 57 |
+
padding: 12px 14px;
|
| 58 |
+
margin-bottom: 14px;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
.dot {
|
| 62 |
+
width: 10px;
|
| 63 |
+
height: 10px;
|
| 64 |
+
border-radius: 50%;
|
| 65 |
+
flex-shrink: 0;
|
| 66 |
+
background: #555;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
.dot.idle { background: #555577; }
|
| 70 |
+
.dot.active { background: #4caf50; animation: pulse 1.2s infinite; }
|
| 71 |
+
.dot.stopped { background: #ff6b6b; }
|
| 72 |
+
|
| 73 |
+
@keyframes pulse {
|
| 74 |
+
0%, 100% { opacity: 1; transform: scale(1); }
|
| 75 |
+
50% { opacity: 0.5; transform: scale(0.8); }
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
.status-text {
|
| 79 |
+
font-size: 13px;
|
| 80 |
+
color: #c8c8e0;
|
| 81 |
+
line-height: 1.4;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
.status-text strong {
|
| 85 |
+
display: block;
|
| 86 |
+
font-size: 14px;
|
| 87 |
+
color: #f5f5f7;
|
| 88 |
+
}
|
| 89 |
+
|
| 90 |
+
.btn-stop {
|
| 91 |
+
width: 100%;
|
| 92 |
+
padding: 11px;
|
| 93 |
+
border: none;
|
| 94 |
+
border-radius: 10px;
|
| 95 |
+
background: linear-gradient(135deg, #e53935, #c62828);
|
| 96 |
+
color: #fff;
|
| 97 |
+
font-size: 14px;
|
| 98 |
+
font-weight: 700;
|
| 99 |
+
font-family: inherit;
|
| 100 |
+
cursor: pointer;
|
| 101 |
+
transition: opacity 0.2s;
|
| 102 |
+
letter-spacing: 0.3px;
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
.btn-stop:hover { opacity: 0.85; }
|
| 106 |
+
.btn-stop:active { opacity: 0.7; }
|
| 107 |
+
|
| 108 |
+
.btn-open {
|
| 109 |
+
width: 100%;
|
| 110 |
+
padding: 11px;
|
| 111 |
+
border: 1px solid #3a3a55;
|
| 112 |
+
border-radius: 10px;
|
| 113 |
+
background: transparent;
|
| 114 |
+
color: #bb86fc;
|
| 115 |
+
font-size: 14px;
|
| 116 |
+
font-weight: 600;
|
| 117 |
+
font-family: inherit;
|
| 118 |
+
cursor: pointer;
|
| 119 |
+
transition: background 0.2s;
|
| 120 |
+
letter-spacing: 0.3px;
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
.btn-open:hover { background: #1f1f3a; }
|
| 124 |
+
|
| 125 |
+
.hidden { display: none !important; }
|
| 126 |
+
|
| 127 |
+
.footer {
|
| 128 |
+
padding: 10px 16px;
|
| 129 |
+
text-align: center;
|
| 130 |
+
font-size: 11px;
|
| 131 |
+
color: #44445a;
|
| 132 |
+
border-top: 1px solid #1f1f3a;
|
| 133 |
+
}
|
| 134 |
+
</style>
|
| 135 |
+
</head>
|
| 136 |
+
<body>
|
| 137 |
+
<div class="header">
|
| 138 |
+
<img src="icon-128.png" alt="icon" />
|
| 139 |
+
<div>
|
| 140 |
+
<div class="header-title">FB Group Publisher PRO</div>
|
| 141 |
+
<div class="header-sub">إضافة المزامنة</div>
|
| 142 |
+
</div>
|
| 143 |
+
</div>
|
| 144 |
+
|
| 145 |
+
<div class="body">
|
| 146 |
+
<div class="status-row">
|
| 147 |
+
<div class="dot idle" id="dot"></div>
|
| 148 |
+
<div class="status-text">
|
| 149 |
+
<strong id="statusTitle">جاهز</strong>
|
| 150 |
+
<span id="statusSub">لا يوجد نشر نشط حالياً</span>
|
| 151 |
+
</div>
|
| 152 |
+
</div>
|
| 153 |
+
|
| 154 |
+
<button class="btn-stop hidden" id="btnStop">⏹ إيقاف النشر الآن</button>
|
| 155 |
+
<button class="btn-open" id="btnOpen">فتح التطبيق</button>
|
| 156 |
+
</div>
|
| 157 |
+
|
| 158 |
+
<div class="footer">FB Group Publisher PRO</div>
|
| 159 |
+
|
| 160 |
+
<script src="popup.js"></script>
|
| 161 |
+
</body>
|
| 162 |
+
</html>
|
artifacts/fb-publisher/public/extension/popup.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
(function () {
|
| 2 |
+
const dot = document.getElementById('dot');
|
| 3 |
+
const titleEl = document.getElementById('statusTitle');
|
| 4 |
+
const subEl = document.getElementById('statusSub');
|
| 5 |
+
const btnStop = document.getElementById('btnStop');
|
| 6 |
+
const btnOpen = document.getElementById('btnOpen');
|
| 7 |
+
|
| 8 |
+
let isPublishing = false;
|
| 9 |
+
let pollTimer = null;
|
| 10 |
+
|
| 11 |
+
function setIdle() {
|
| 12 |
+
isPublishing = false;
|
| 13 |
+
dot.className = 'dot idle';
|
| 14 |
+
titleEl.textContent = 'جاهز';
|
| 15 |
+
subEl.textContent = 'لا يوجد نشر نشط حالياً';
|
| 16 |
+
btnStop.classList.add('hidden');
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
function setActive() {
|
| 20 |
+
isPublishing = true;
|
| 21 |
+
dot.className = 'dot active';
|
| 22 |
+
titleEl.textContent = 'جارٍ النشر…';
|
| 23 |
+
subEl.textContent = 'اضغط إيقاف لإنهاء العملية';
|
| 24 |
+
btnStop.classList.remove('hidden');
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
function setStopped() {
|
| 28 |
+
isPublishing = false;
|
| 29 |
+
dot.className = 'dot stopped';
|
| 30 |
+
titleEl.textContent = 'تم الإيقاف';
|
| 31 |
+
subEl.textContent = 'تم إيقاف النشر بنجاح';
|
| 32 |
+
btnStop.classList.add('hidden');
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
function checkStatus() {
|
| 36 |
+
chrome.runtime.sendMessage({ action: 'getPublishStatus' }, (res) => {
|
| 37 |
+
if (chrome.runtime.lastError) return;
|
| 38 |
+
if (res && res.isPublishing) {
|
| 39 |
+
setActive();
|
| 40 |
+
} else if (!isPublishing) {
|
| 41 |
+
setIdle();
|
| 42 |
+
}
|
| 43 |
+
});
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
function stopPublishing() {
|
| 47 |
+
chrome.runtime.sendMessage({ action: 'stopPublishing' }, (res) => {
|
| 48 |
+
if (chrome.runtime.lastError) return;
|
| 49 |
+
if (res && res.success) setStopped();
|
| 50 |
+
});
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
btnStop.addEventListener('click', stopPublishing);
|
| 54 |
+
|
| 55 |
+
btnOpen.addEventListener('click', () => {
|
| 56 |
+
chrome.storage.local.get('appUrl', (data) => {
|
| 57 |
+
const url = (data && data.appUrl) || 'https://social-media-bridge-s2555.replit.app/app';
|
| 58 |
+
chrome.tabs.query({ url: url.replace(/#.*$/, '') + '*' }, (tabs) => {
|
| 59 |
+
if (tabs && tabs.length > 0) {
|
| 60 |
+
chrome.tabs.update(tabs[0].id, { active: true });
|
| 61 |
+
if (tabs[0].windowId) chrome.windows.update(tabs[0].windowId, { focused: true });
|
| 62 |
+
} else {
|
| 63 |
+
chrome.tabs.create({ url: url });
|
| 64 |
+
}
|
| 65 |
+
});
|
| 66 |
+
});
|
| 67 |
+
window.close();
|
| 68 |
+
});
|
| 69 |
+
|
| 70 |
+
window.addEventListener('pagehide', () => {
|
| 71 |
+
if (isPublishing) {
|
| 72 |
+
chrome.runtime.sendMessage({ action: 'stopPublishing' });
|
| 73 |
+
}
|
| 74 |
+
});
|
| 75 |
+
|
| 76 |
+
checkStatus();
|
| 77 |
+
pollTimer = setInterval(checkStatus, 2000);
|
| 78 |
+
|
| 79 |
+
window.addEventListener('unload', () => {
|
| 80 |
+
clearInterval(pollTimer);
|
| 81 |
+
});
|
| 82 |
+
})();
|
artifacts/fb-publisher/public/extension/src/.gitignore
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# الكود المصدري الأصلي — لا يُرفع للـ Git
|
| 2 |
+
*.source.js
|
artifacts/fb-publisher/public/extension/src/background.source.js
ADDED
|
@@ -0,0 +1,678 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// ========== رابط التطبيق الافتراضي (محدّث لـ Hugging Face) ==========
|
| 2 |
+
const DEFAULT_APP_URL = 'https://sare26-fb-publisher-backend.hf.space/app';
|
| 3 |
+
|
| 4 |
+
// ========== نطاق التطبيق الموثوق ==========
|
| 5 |
+
const APP_ORIGIN = 'https://sare26-fb-publisher-backend.hf.space';
|
| 6 |
+
|
| 7 |
+
// ========== نظام مراقبة الأخطاء ==========
|
| 8 |
+
function bgLogError(source, error, details = {}) {
|
| 9 |
+
console.error(`❌ [BG][${source}]`, error, details);
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
function bgLogInfo(source, message, data = {}) {
|
| 13 |
+
console.log(`✅ [BG][${source}]`, message, data);
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
// ========== الإعدادات الثابتة ==========
|
| 17 |
+
const USER_CONFIG = {
|
| 18 |
+
userId: null,
|
| 19 |
+
doc_id_groups: "3884641628300421",
|
| 20 |
+
doc_id_post_text: "3559434960802556",
|
| 21 |
+
doc_id_post_photo: "25528973250111023",
|
| 22 |
+
fb_dtsg: null,
|
| 23 |
+
lsd: null,
|
| 24 |
+
dyn: null,
|
| 25 |
+
jazoest: "25415",
|
| 26 |
+
upload_id: "jsc_c_9",
|
| 27 |
+
targetGroupId: null
|
| 28 |
+
};
|
| 29 |
+
|
| 30 |
+
// ========== حالة النشر المتعدد ==========
|
| 31 |
+
let publishCancelled = false;
|
| 32 |
+
let isPublishingNow = false;
|
| 33 |
+
let activePublishImages = [];
|
| 34 |
+
let appTabId = null;
|
| 35 |
+
let currentPublishTabId = null;
|
| 36 |
+
|
| 37 |
+
// Promise تنتهي عند اكتمال تحميل التخزين المحلي
|
| 38 |
+
let resolveStorageReady;
|
| 39 |
+
const storageReady = new Promise((resolve) => { resolveStorageReady = resolve; });
|
| 40 |
+
|
| 41 |
+
// تحميل القيم المخزنة
|
| 42 |
+
chrome.storage.local.get(['fb_dtsg', 'lsd', 'dyn', 'targetGroupId', 'publishCancelled', 'userId'], (data) => {
|
| 43 |
+
if (data.fb_dtsg) USER_CONFIG.fb_dtsg = data.fb_dtsg;
|
| 44 |
+
if (data.lsd) USER_CONFIG.lsd = data.lsd;
|
| 45 |
+
if (data.dyn) USER_CONFIG.dyn = data.dyn;
|
| 46 |
+
if (data.userId) USER_CONFIG.userId = data.userId;
|
| 47 |
+
if (data.targetGroupId) USER_CONFIG.targetGroupId = data.targetGroupId;
|
| 48 |
+
if (data.publishCancelled) {
|
| 49 |
+
publishCancelled = data.publishCancelled;
|
| 50 |
+
chrome.storage.local.set({ publishCancelled: false });
|
| 51 |
+
}
|
| 52 |
+
bgLogInfo('init', 'تم تحميل البيانات المخزنة');
|
| 53 |
+
resolveStorageReady();
|
| 54 |
+
});
|
| 55 |
+
|
| 56 |
+
// ========== تأخير عشوائي بين 62 و149 ثانية ==========
|
| 57 |
+
function randomDelay() {
|
| 58 |
+
return Math.floor(Math.random() * (195 - 80 + 1)) + 80;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
// ========== توليد نص عشوائي متغير لكل منشور (حماية من الحظر) ==========
|
| 62 |
+
function generateRandomText() {
|
| 63 |
+
const emojis = ["✨", "🌟", "💫", "🔥", "⚡", "🎯", "💎", "🔮", "🌈", "🎪", "🎭", "🎨", "🎵", "🎶", "🍀", "🌺", "🌸", "🌼", "🌻", "💐", "🦋", "🐝", "🐞", "🕊️", "🦅"];
|
| 64 |
+
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
| 65 |
+
const numbers = "0123456789";
|
| 66 |
+
|
| 67 |
+
const randomLetter = letters[Math.floor(Math.random() * letters.length)];
|
| 68 |
+
const randomNumber = numbers[Math.floor(Math.random() * numbers.length)];
|
| 69 |
+
const randomEmoji1 = emojis[Math.floor(Math.random() * emojis.length)];
|
| 70 |
+
const randomEmoji2 = emojis[Math.floor(Math.random() * emojis.length)];
|
| 71 |
+
|
| 72 |
+
let randomWord = "";
|
| 73 |
+
const wordLength = Math.floor(Math.random() * 6) + 5;
|
| 74 |
+
for (let i = 0; i < wordLength; i++) {
|
| 75 |
+
randomWord += letters[Math.floor(Math.random() * letters.length)];
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
return `${randomEmoji1} ${randomWord}${randomNumber} ${randomEmoji2}`;
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
// ========== دالة استخراج معرف الحساب الشخصي من الكوكيز ==========
|
| 82 |
+
function getFacebookUserId() {
|
| 83 |
+
return new Promise((resolve) => {
|
| 84 |
+
chrome.cookies.get({ url: 'https://www.facebook.com', name: 'c_user' }, (cookie) => {
|
| 85 |
+
if (cookie && cookie.value) {
|
| 86 |
+
bgLogInfo('cookies', `تم العثور على c_user (حساب شخصي): ${cookie.value}`);
|
| 87 |
+
resolve(cookie.value);
|
| 88 |
+
} else {
|
| 89 |
+
bgLogInfo('cookies', 'لم يتم العثور على c_user - قد لا يكون المستخدم مسجل الدخول');
|
| 90 |
+
resolve(null);
|
| 91 |
+
}
|
| 92 |
+
});
|
| 93 |
+
});
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
// ========== دالة استخراج معرف الصفحة من الكوكيز ==========
|
| 97 |
+
function getFacebookPageId() {
|
| 98 |
+
return new Promise((resolve) => {
|
| 99 |
+
chrome.cookies.get({ url: 'https://www.facebook.com', name: 'i_user' }, (cookie) => {
|
| 100 |
+
if (cookie && cookie.value) {
|
| 101 |
+
bgLogInfo('cookies', `✅ تم العثور على i_user (معرف الصفحة): ${cookie.value}`);
|
| 102 |
+
resolve(cookie.value);
|
| 103 |
+
} else {
|
| 104 |
+
bgLogInfo('cookies', '⚠️ لم يتم العثور على i_user');
|
| 105 |
+
resolve(null);
|
| 106 |
+
}
|
| 107 |
+
});
|
| 108 |
+
});
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
// ========== دالة استخراج fb_dtsg ==========
|
| 112 |
+
async function extractFbDtsg(tabId) {
|
| 113 |
+
return new Promise((resolve) => {
|
| 114 |
+
chrome.scripting.executeScript({
|
| 115 |
+
target: { tabId: tabId },
|
| 116 |
+
func: () => {
|
| 117 |
+
let fbDtsg = null;
|
| 118 |
+
let lsd = null;
|
| 119 |
+
|
| 120 |
+
const dtsgInput = document.querySelector('input[name="fb_dtsg"]');
|
| 121 |
+
if (dtsgInput && dtsgInput.value) fbDtsg = dtsgInput.value;
|
| 122 |
+
const lsdInput = document.querySelector('input[name="lsd"]');
|
| 123 |
+
if (lsdInput && lsdInput.value) lsd = lsdInput.value;
|
| 124 |
+
|
| 125 |
+
const fullText = document.documentElement.innerHTML;
|
| 126 |
+
|
| 127 |
+
if (!fbDtsg) {
|
| 128 |
+
let m = fullText.match(/DTSGInitialData[^{]*\{[^}]*"token"\s*:\s*"([^"]+)"/);
|
| 129 |
+
if (m) fbDtsg = m[1];
|
| 130 |
+
}
|
| 131 |
+
if (!fbDtsg) {
|
| 132 |
+
let m = fullText.match(/\["fb_dtsg"\s*,\s*\[\]\s*,\s*\{\s*"token"\s*:\s*"([^"]+)"/);
|
| 133 |
+
if (m) fbDtsg = m[1];
|
| 134 |
+
}
|
| 135 |
+
if (!fbDtsg) {
|
| 136 |
+
let m = fullText.match(/"fb_dtsg"\s*:\s*"([A-Za-z0-9_\-:\.]{10,120})"/);
|
| 137 |
+
if (m) fbDtsg = m[1];
|
| 138 |
+
}
|
| 139 |
+
if (!fbDtsg) {
|
| 140 |
+
const scripts = document.querySelectorAll('script');
|
| 141 |
+
for (let script of scripts) {
|
| 142 |
+
const content = script.textContent;
|
| 143 |
+
if (!content) continue;
|
| 144 |
+
let m = content.match(/DTSGInitialData[^}]*token["']\s*:\s*["']([^"']+)["']/);
|
| 145 |
+
if (m && m[1]) { fbDtsg = m[1]; break; }
|
| 146 |
+
m = content.match(/fb_dtsg["']\s*:\s*["']([^"']+)["']/);
|
| 147 |
+
if (m && m[1]) { fbDtsg = m[1]; break; }
|
| 148 |
+
m = content.match(/["']fb_dtsg["']\s*,\s*["']([^"']+)["']/);
|
| 149 |
+
if (m && m[1]) { fbDtsg = m[1]; break; }
|
| 150 |
+
m = content.match(/DTSGInitializer[^)]*"token"\s*:\s*"([^"]+)"/);
|
| 151 |
+
if (m && m[1]) { fbDtsg = m[1]; break; }
|
| 152 |
+
}
|
| 153 |
+
}
|
| 154 |
+
if (!lsd) {
|
| 155 |
+
let m = fullText.match(/\["LSD"\s*,\s*\[\]\s*,\s*\{\s*"token"\s*:\s*"([^"]+)"/);
|
| 156 |
+
if (m) lsd = m[1];
|
| 157 |
+
}
|
| 158 |
+
if (!lsd) {
|
| 159 |
+
let m = fullText.match(/["']LSD["']\s*,\s*\[\s*\]\s*,\s*\{\s*["']token["']\s*:\s*["']([^"']+)["']/);
|
| 160 |
+
if (m) lsd = m[1];
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
return { fb_dtsg: fbDtsg, lsd: lsd };
|
| 164 |
+
}
|
| 165 |
+
}, (results) => {
|
| 166 |
+
if (results && results[0] && results[0].result) resolve(results[0].result);
|
| 167 |
+
else resolve({ fb_dtsg: null, lsd: null });
|
| 168 |
+
});
|
| 169 |
+
});
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
// ========== دالة استخراج __dyn ==========
|
| 173 |
+
async function extractDyn(tabId) {
|
| 174 |
+
return new Promise((resolve) => {
|
| 175 |
+
chrome.scripting.executeScript({
|
| 176 |
+
target: { tabId: tabId },
|
| 177 |
+
func: () => {
|
| 178 |
+
let dyn = document.querySelector('input[name="__dyn"]')?.value;
|
| 179 |
+
if (dyn && dyn.length > 20) return dyn;
|
| 180 |
+
|
| 181 |
+
const scripts = document.querySelectorAll('script');
|
| 182 |
+
for (let script of scripts) {
|
| 183 |
+
const content = script.textContent;
|
| 184 |
+
if (content) {
|
| 185 |
+
let match = content.match(/__dyn\s*:\s*"([^"]+)"/);
|
| 186 |
+
if (match && match[1] && match[1].length > 20) return match[1];
|
| 187 |
+
match = content.match(/"__dyn":"([^"]+)"/);
|
| 188 |
+
if (match && match[1] && match[1].length > 20) return match[1];
|
| 189 |
+
match = content.match(/DYN\s*=\s*"([^"]+)"/);
|
| 190 |
+
if (match && match[1] && match[1].length > 20) return match[1];
|
| 191 |
+
}
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
const html = document.documentElement.innerHTML;
|
| 195 |
+
let match = html.match(/__dyn":"([^"]+?)"/);
|
| 196 |
+
if (match && match[1] && match[1].length > 20) return match[1];
|
| 197 |
+
match = html.match(/__dyn=([^&"\s]+)/);
|
| 198 |
+
if (match && match[1] && match[1].length > 20) return match[1];
|
| 199 |
+
|
| 200 |
+
return null;
|
| 201 |
+
}
|
| 202 |
+
}, (results) => {
|
| 203 |
+
const dyn = results?.[0]?.result || null;
|
| 204 |
+
if (dyn) bgLogInfo('extractDyn', `✅ تم استخراج __dyn`);
|
| 205 |
+
else bgLogInfo('extractDyn', '❌ لم يتم العثور على __dyn');
|
| 206 |
+
resolve(dyn);
|
| 207 |
+
});
|
| 208 |
+
});
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
// ========== دالة تحديث الجلسة ==========
|
| 212 |
+
async function refreshSession(tabId) {
|
| 213 |
+
bgLogInfo('session', 'جاري تحديث الجلسة');
|
| 214 |
+
try {
|
| 215 |
+
await chrome.scripting.executeScript({
|
| 216 |
+
target: { tabId: tabId },
|
| 217 |
+
func: () => fetch('https://www.facebook.com/help/463972400461409/?_rdr', { credentials: 'include', headers: { 'Accept': 'text/html' } })
|
| 218 |
+
});
|
| 219 |
+
await new Promise(r => setTimeout(r, 2000));
|
| 220 |
+
bgLogInfo('session', 'تم تحديث الجلسة بنجاح');
|
| 221 |
+
} catch(e) { bgLogError('session', e); }
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
// ========== دالة مدمجة: التحقق من الفيسبوك + اصطياد المفاتيح ==========
|
| 225 |
+
async function ensureFacebookAndCaptureKeys() {
|
| 226 |
+
return new Promise(async (resolve) => {
|
| 227 |
+
bgLogInfo('autoCapture', 'بدء عملية التحقق من فيسبوك واصطياد المفاتيح');
|
| 228 |
+
|
| 229 |
+
const personalUserId = await getFacebookUserId();
|
| 230 |
+
const pageId = await getFacebookPageId();
|
| 231 |
+
const effectiveUserId = pageId || personalUserId;
|
| 232 |
+
|
| 233 |
+
if (!effectiveUserId) {
|
| 234 |
+
resolve({ success: false, error: "not_logged_in", message: "يجب تسجيل الدخول إلى فيسبوك أولاً" });
|
| 235 |
+
return;
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
USER_CONFIG.userId = effectiveUserId;
|
| 239 |
+
bgLogInfo('autoCapture', `✅ استخدام المعرف: ${effectiveUserId}`);
|
| 240 |
+
|
| 241 |
+
const tabs = await chrome.tabs.query({ url: ["*://*.facebook.com/*", "*://*.web.facebook.com/*"] });
|
| 242 |
+
let targetTabId = null;
|
| 243 |
+
|
| 244 |
+
if (tabs.length === 0) {
|
| 245 |
+
const newTab = await chrome.tabs.create({ url: 'https://www.facebook.com', active: true });
|
| 246 |
+
targetTabId = newTab.id;
|
| 247 |
+
await new Promise(r => setTimeout(r, 5000));
|
| 248 |
+
} else {
|
| 249 |
+
targetTabId = tabs[0].id;
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
await refreshSession(targetTabId);
|
| 253 |
+
let result = await extractFbDtsg(targetTabId);
|
| 254 |
+
|
| 255 |
+
if (!result.fb_dtsg) {
|
| 256 |
+
await chrome.tabs.reload(targetTabId);
|
| 257 |
+
await new Promise(r => setTimeout(r, 5000));
|
| 258 |
+
const retryResult = await extractFbDtsg(targetTabId);
|
| 259 |
+
if (!retryResult.fb_dtsg) {
|
| 260 |
+
resolve({ success: false, error: "no_fb_dtsg", message: "لم يتم العثور على مفاتيح فيسبوك" });
|
| 261 |
+
return;
|
| 262 |
+
}
|
| 263 |
+
result = retryResult;
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
USER_CONFIG.fb_dtsg = result.fb_dtsg;
|
| 267 |
+
USER_CONFIG.lsd = result.lsd;
|
| 268 |
+
|
| 269 |
+
const dyn = await extractDyn(targetTabId);
|
| 270 |
+
if (dyn) USER_CONFIG.dyn = dyn;
|
| 271 |
+
|
| 272 |
+
chrome.storage.local.set({ fb_dtsg: result.fb_dtsg, lsd: result.lsd, dyn: dyn, userId: effectiveUserId });
|
| 273 |
+
bgLogInfo('autoCapture', '✅ تم اصطياد المفاتيح بنجاح');
|
| 274 |
+
resolve({ success: true, userId: effectiveUserId, tabId: targetTabId });
|
| 275 |
+
});
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
// ========== دالة رفع صورة ==========
|
| 279 |
+
async function uploadImage(tabId, imageBase64, fileName) {
|
| 280 |
+
return new Promise((resolve) => {
|
| 281 |
+
chrome.scripting.executeScript({
|
| 282 |
+
target: { tabId: tabId },
|
| 283 |
+
func: (config, imgBase64, name) => {
|
| 284 |
+
return new Promise((resolve) => {
|
| 285 |
+
try {
|
| 286 |
+
const byteString = atob(imgBase64.split(',')[1]);
|
| 287 |
+
const mimeType = imgBase64.split(',')[0].split(':')[1].split(';')[0];
|
| 288 |
+
const ab = new ArrayBuffer(byteString.length);
|
| 289 |
+
const ia = new Uint8Array(ab);
|
| 290 |
+
for (let i = 0; i < byteString.length; i++) ia[i] = byteString.charCodeAt(i);
|
| 291 |
+
const blob = new Blob([ab], { type: mimeType });
|
| 292 |
+
const formData = new FormData();
|
| 293 |
+
formData.append('fb_dtsg', config.fb_dtsg);
|
| 294 |
+
formData.append('profile_id', config.userId);
|
| 295 |
+
formData.append('source', '8');
|
| 296 |
+
formData.append('waterfallxapp', 'comet');
|
| 297 |
+
formData.append('farr', blob, name);
|
| 298 |
+
formData.append('upload_id', config.upload_id);
|
| 299 |
+
formData.append('jazoest', (25000 + Math.floor(Math.random() * 1000)).toString());
|
| 300 |
+
const uploadUrl = `https://upload.facebook.com/ajax/react_composer/attachments/photo/upload?av=${config.userId}&__user=${config.userId}&__a=1&fb_dtsg=${config.fb_dtsg}`;
|
| 301 |
+
fetch(uploadUrl, { method: 'OPTIONS', credentials: 'include' })
|
| 302 |
+
.then(() => fetch(uploadUrl, { method: 'POST', credentials: 'include', body: formData, headers: { 'Accept': '*/*', 'Origin': 'https://upload.facebook.com', 'Referer': 'https://upload.facebook.com/' } }))
|
| 303 |
+
.then(async (response) => {
|
| 304 |
+
const text = await response.text();
|
| 305 |
+
let photoId = null;
|
| 306 |
+
const photoIDMatch = text.match(/"photoID":"(\d+)"/);
|
| 307 |
+
if (photoIDMatch) photoId = photoIDMatch[1];
|
| 308 |
+
if (!photoId) { const m2 = text.match(/photoID["']\s*:\s*["'](\d+)["']/); if (m2) photoId = m2[1]; }
|
| 309 |
+
if (!photoId) { const m3 = text.match(/fbid["']\s*:\s*["'](\d+)["']/); if (m3) photoId = m3[1]; }
|
| 310 |
+
if (photoId) resolve({ success: true, photoId });
|
| 311 |
+
else resolve({ success: false, error: 'لم يتم استخراج photoID' });
|
| 312 |
+
})
|
| 313 |
+
.catch(err => resolve({ success: false, error: err.message }));
|
| 314 |
+
} catch (e) { resolve({ success: false, error: e.message }); }
|
| 315 |
+
});
|
| 316 |
+
},
|
| 317 |
+
args: [USER_CONFIG, imageBase64, fileName]
|
| 318 |
+
}, (results) => {
|
| 319 |
+
if (results && results[0] && results[0].result) resolve(results[0].result);
|
| 320 |
+
else resolve({ success: false, error: 'فشل تنفيذ السكربت' });
|
| 321 |
+
});
|
| 322 |
+
});
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
// ========== دالة فحص حالة المنشور ==========
|
| 326 |
+
async function checkPostStatus(tabId, postUrl) {
|
| 327 |
+
return new Promise((resolve) => {
|
| 328 |
+
if (!postUrl) { resolve({ isPending: false, isPublic: false }); return; }
|
| 329 |
+
chrome.scripting.executeScript({
|
| 330 |
+
target: { tabId: tabId },
|
| 331 |
+
func: (url) => {
|
| 332 |
+
return new Promise((resolve) => {
|
| 333 |
+
fetch(url, { credentials: 'include', headers: { 'Accept': 'text/html' } })
|
| 334 |
+
.then(res => res.text())
|
| 335 |
+
.then(html => {
|
| 336 |
+
const isPending = html.includes('pending_post') || html.includes('في انتظار موافقة المسؤول') || html.includes('pending approval');
|
| 337 |
+
const hasPendingFooter = html.includes('pending_post_footer_title');
|
| 338 |
+
resolve({ isPending: isPending || hasPendingFooter, isPublic: !isPending && !hasPendingFooter, hasPendingFooter });
|
| 339 |
+
})
|
| 340 |
+
.catch(() => resolve({ isPending: false, isPublic: false }));
|
| 341 |
+
});
|
| 342 |
+
},
|
| 343 |
+
args: [postUrl]
|
| 344 |
+
}, (results) => {
|
| 345 |
+
if (results && results[0] && results[0].result) resolve(results[0].result);
|
| 346 |
+
else resolve({ isPending: false, isPublic: false });
|
| 347 |
+
});
|
| 348 |
+
});
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
// ========== دالة نشر منشور في مجموعة (مع نص عشوائي للحماية) ==========
|
| 352 |
+
async function publishToGroup(tabId, text, photoIds, groupId) {
|
| 353 |
+
return new Promise((resolve) => {
|
| 354 |
+
if (!groupId) { resolve({ success: false, error: 'لم يتم تحديد المجموعة' }); return; }
|
| 355 |
+
const hasPhotos = photoIds && photoIds.length > 0;
|
| 356 |
+
const docId = hasPhotos ? USER_CONFIG.doc_id_post_photo : USER_CONFIG.doc_id_post_text;
|
| 357 |
+
chrome.scripting.executeScript({
|
| 358 |
+
target: { tabId: tabId },
|
| 359 |
+
func: (config, postText, imagesIds, targetGroupId, docIdValue) => {
|
| 360 |
+
return new Promise((resolve) => {
|
| 361 |
+
const hasPhotos = imagesIds && imagesIds.length > 0;
|
| 362 |
+
|
| 363 |
+
// ✅ توليد النص العشوائي داخل func - يتغير لكل منشور
|
| 364 |
+
const emojis = ["✨", "🌟", "💫", "🔥", "⚡", "🎯", "💎", "🔮", "🌈", "🎪", "🎭", "🎨", "🎵", "🎶", "🍀", "🌺", "🌸", "🌼", "🌻", "💐", "🦋", "🐝", "🐞", "🕊️", "🦅"];
|
| 365 |
+
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
| 366 |
+
const numbers = "0123456789";
|
| 367 |
+
const randomEmoji1 = emojis[Math.floor(Math.random() * emojis.length)];
|
| 368 |
+
const randomEmoji2 = emojis[Math.floor(Math.random() * emojis.length)];
|
| 369 |
+
const randomNumber = numbers[Math.floor(Math.random() * numbers.length)];
|
| 370 |
+
let randomWord = "";
|
| 371 |
+
const wordLength = Math.floor(Math.random() * 6) + 5;
|
| 372 |
+
for (let i = 0; i < wordLength; i++) {
|
| 373 |
+
randomWord += letters[Math.floor(Math.random() * letters.length)];
|
| 374 |
+
}
|
| 375 |
+
const randomText = `\n\n\n\n\n\n${randomEmoji1} ${randomWord}${randomNumber} ${randomEmoji2}`;
|
| 376 |
+
|
| 377 |
+
const finalText = (postText || "") + randomText;
|
| 378 |
+
|
| 379 |
+
const variables = {
|
| 380 |
+
input: {
|
| 381 |
+
source: "WWW",
|
| 382 |
+
attachments: hasPhotos ? imagesIds.map(id => ({ photo: { id: id } })) : [],
|
| 383 |
+
message: { ranges: [], text: finalText },
|
| 384 |
+
inline_activities: [],
|
| 385 |
+
explicit_place_id: "0",
|
| 386 |
+
tracking: [null],
|
| 387 |
+
audience: { to_id: targetGroupId },
|
| 388 |
+
actor_id: config.userId,
|
| 389 |
+
client_mutation_id: (Date.now() + Math.floor(Math.random() * 1000000)).toString()
|
| 390 |
+
}
|
| 391 |
+
};
|
| 392 |
+
const formData = new FormData();
|
| 393 |
+
formData.append('av', config.userId);
|
| 394 |
+
formData.append('__user', config.userId);
|
| 395 |
+
formData.append('__a', '1');
|
| 396 |
+
formData.append('fb_dtsg', config.fb_dtsg);
|
| 397 |
+
formData.append('variables', JSON.stringify(variables));
|
| 398 |
+
formData.append('doc_id', docIdValue);
|
| 399 |
+
formData.append('jazoest', config.jazoest);
|
| 400 |
+
const prePublishDelay = Math.floor(Math.random() * (3000 - 1000 + 1)) + 1000;
|
| 401 |
+
new Promise(r => setTimeout(r, prePublishDelay))
|
| 402 |
+
.then(() => fetch('https://www.facebook.com/api/graphql/', { method: 'OPTIONS', credentials: 'include', headers: { 'Accept': '*/*', 'Origin': 'https://www.facebook.com', 'Referer': `https://www.facebook.com/groups/${targetGroupId}/` } }))
|
| 403 |
+
.then(() => fetch('https://www.facebook.com/api/graphql/', { method: 'POST', credentials: 'include', body: formData, headers: { 'Accept': '*/*', 'Origin': 'https://www.facebook.com', 'Referer': `https://www.facebook.com/groups/${targetGroupId}/` } }))
|
| 404 |
+
.then(res => res.text())
|
| 405 |
+
.then(rawText => {
|
| 406 |
+
let legacyId = null, storyId = null, postUrl = null;
|
| 407 |
+
const legacyMatch = rawText.match(/"legacy_story_hideable_id":"(\d+)"/);
|
| 408 |
+
if (legacyMatch) { legacyId = legacyMatch[1]; postUrl = `https://www.facebook.com/groups/${targetGroupId}/posts/${legacyId}/`; }
|
| 409 |
+
if (!legacyId) { const storyMatch = rawText.match(/"story_id":"([^"]+)"/); if (storyMatch) { storyId = storyMatch[1]; postUrl = `https://www.facebook.com/groups/${targetGroupId}/permalink/${storyId}/`; } }
|
| 410 |
+
if (!legacyId && !storyId) { const idMatch = rawText.match(/"story":\s*\{\s*"id":\s*"([^"]+)"/); if (idMatch) { storyId = idMatch[1]; postUrl = `https://www.facebook.com/groups/${targetGroupId}/permalink/${storyId}/`; } }
|
| 411 |
+
const hasStory = rawText.includes('"story_id"') || rawText.includes('"story"');
|
| 412 |
+
const hasPendingFooter = rawText.includes('pending_post_footer_title');
|
| 413 |
+
let finalSuccess = false, initialPending = false;
|
| 414 |
+
if (postUrl) { finalSuccess = true; initialPending = false; }
|
| 415 |
+
else if (hasStory) { finalSuccess = true; initialPending = true; }
|
| 416 |
+
else if (hasPendingFooter) { finalSuccess = false; initialPending = true; }
|
| 417 |
+
resolve({ success: finalSuccess, pending: initialPending, story_id: legacyId || storyId, post_url: postUrl });
|
| 418 |
+
})
|
| 419 |
+
.catch(e => resolve({ success: false, pending: false, error: e.message, post_url: null }));
|
| 420 |
+
});
|
| 421 |
+
},
|
| 422 |
+
args: [USER_CONFIG, text, photoIds, groupId, docId] // ✅ بدون randomText هنا
|
| 423 |
+
}, (results) => {
|
| 424 |
+
if (results && results[0]) resolve(results[0].result);
|
| 425 |
+
else resolve({ success: false, error: 'فشل تنفيذ النشر', post_url: null });
|
| 426 |
+
});
|
| 427 |
+
});
|
| 428 |
+
}
|
| 429 |
+
|
| 430 |
+
// ========== دالة جلب المجموعات ==========
|
| 431 |
+
async function fetchAllGroups(tabId) {
|
| 432 |
+
return new Promise((resolve) => {
|
| 433 |
+
chrome.scripting.executeScript({
|
| 434 |
+
target: { tabId: tabId },
|
| 435 |
+
func: (fb_dtsg, av, dyn) => {
|
| 436 |
+
return new Promise((resolve) => {
|
| 437 |
+
const params = new URLSearchParams();
|
| 438 |
+
params.append('fb_dtsg', fb_dtsg);
|
| 439 |
+
params.append('av', av);
|
| 440 |
+
if (dyn) params.append('__dyn', dyn);
|
| 441 |
+
params.append('variables', JSON.stringify({ "adminGroupsCount": 999, "memberGroupsCount": 999, "scale": 1.5, "count": 999, "cursor": null }));
|
| 442 |
+
params.append('doc_id', '3884641628300421');
|
| 443 |
+
fetch('https://www.facebook.com/api/graphql/', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, credentials: 'include', body: params.toString() })
|
| 444 |
+
.then(async (res) => {
|
| 445 |
+
const text = await res.text();
|
| 446 |
+
try {
|
| 447 |
+
const data = JSON.parse(text);
|
| 448 |
+
const groups = [];
|
| 449 |
+
const getImageUrl = (node) => node?.image?.uri || node?.profile_picture?.uri || '';
|
| 450 |
+
if (data.data?.adminGroups?.groups_tab?.tab_groups_list?.edges) {
|
| 451 |
+
data.data.adminGroups.groups_tab.tab_groups_list.edges.forEach(edge => { const node = edge.node; if (node && node.id) groups.push({ name: node.name, id: node.id, image: getImageUrl(node), url: `https://www.facebook.com/groups/${node.id}`, isAdmin: true }); });
|
| 452 |
+
}
|
| 453 |
+
if (data.data?.viewer?.groups?.edges) {
|
| 454 |
+
data.data.viewer.groups.edges.forEach(edge => { const node = edge.node; if (node && node.id) groups.push({ name: node.name, id: node.id, image: getImageUrl(node), url: `https://www.facebook.com/groups/${node.id}`, isAdmin: false }); });
|
| 455 |
+
}
|
| 456 |
+
if (data.data?.nonAdminGroups?.groups_tab?.tab_groups_list?.edges) {
|
| 457 |
+
data.data.nonAdminGroups.groups_tab.tab_groups_list.edges.forEach(edge => { const node = edge.node; if (node && node.id) groups.push({ name: node.name, id: node.id, image: getImageUrl(node), url: `https://www.facebook.com/groups/${node.id}`, isAdmin: false }); });
|
| 458 |
+
}
|
| 459 |
+
resolve({ success: true, groups });
|
| 460 |
+
} catch (parseError) { resolve({ success: false, error: 'Invalid JSON' }); }
|
| 461 |
+
})
|
| 462 |
+
.catch(err => resolve({ success: false, error: err.message }));
|
| 463 |
+
});
|
| 464 |
+
},
|
| 465 |
+
args: [USER_CONFIG.fb_dtsg, USER_CONFIG.userId, USER_CONFIG.dyn || null]
|
| 466 |
+
}, (results) => {
|
| 467 |
+
const result = results?.[0]?.result;
|
| 468 |
+
if (result?.success && result.groups?.length > 0) {
|
| 469 |
+
result.groups.sort((a, b) => (a.isAdmin === b.isAdmin) ? 0 : a.isAdmin ? -1 : 1);
|
| 470 |
+
resolve({ groups: result.groups });
|
| 471 |
+
} else resolve({ groups: result?.groups || [] });
|
| 472 |
+
});
|
| 473 |
+
});
|
| 474 |
+
}
|
| 475 |
+
|
| 476 |
+
// ========== نشر في مجموعة واحدة ==========
|
| 477 |
+
async function publishToOneGroup(groupId, text, images) {
|
| 478 |
+
return new Promise(async (resolve) => {
|
| 479 |
+
const groupUrl = `https://www.facebook.com/groups/${groupId}/`;
|
| 480 |
+
chrome.tabs.create({ url: groupUrl, active: false }, async (groupTab) => {
|
| 481 |
+
const tabId = groupTab.id;
|
| 482 |
+
currentPublishTabId = tabId;
|
| 483 |
+
|
| 484 |
+
let tabClosedByUser = false;
|
| 485 |
+
let resolved = false;
|
| 486 |
+
|
| 487 |
+
const sendProgress = (payload) => {
|
| 488 |
+
if (appTabId) chrome.tabs.sendMessage(appTabId, payload).catch(() => {});
|
| 489 |
+
else chrome.runtime.sendMessage(payload).catch(() => {});
|
| 490 |
+
};
|
| 491 |
+
|
| 492 |
+
const onTabRemoved = (removedTabId) => {
|
| 493 |
+
if (removedTabId !== tabId) return;
|
| 494 |
+
chrome.tabs.onRemoved.removeListener(onTabRemoved);
|
| 495 |
+
tabClosedByUser = true;
|
| 496 |
+
currentPublishTabId = null;
|
| 497 |
+
publishCancelled = true;
|
| 498 |
+
sendProgress({ action: "publishStopped", doneCount: 0, total: 0 });
|
| 499 |
+
if (!resolved) { resolved = true; resolve({ success: false, error: 'أغلق المستخدم تبويب فيسبوك', tabClosed: true, post_url: null }); }
|
| 500 |
+
};
|
| 501 |
+
chrome.tabs.onRemoved.addListener(onTabRemoved);
|
| 502 |
+
|
| 503 |
+
const waitWithCheck = async (ms) => {
|
| 504 |
+
const step = 500; let elapsed = 0;
|
| 505 |
+
while (elapsed < ms) { if (publishCancelled || tabClosedByUser) return; await new Promise(r => setTimeout(r, Math.min(step, ms - elapsed))); elapsed += step; }
|
| 506 |
+
};
|
| 507 |
+
|
| 508 |
+
const initialWait = Math.floor(Math.random() * (20000 - 8000 + 1)) + 8000;
|
| 509 |
+
await waitWithCheck(initialWait);
|
| 510 |
+
if (tabClosedByUser || publishCancelled) { chrome.tabs.onRemoved.removeListener(onTabRemoved); if (!resolved) { resolved = true; chrome.tabs.remove(tabId, () => {}); currentPublishTabId = null; resolve({ success: false, error: 'تم الإلغاء', post_url: null }); } return; }
|
| 511 |
+
|
| 512 |
+
let photoIds = [];
|
| 513 |
+
if (!publishCancelled && !tabClosedByUser && images && images.length > 0) {
|
| 514 |
+
for (let i = 0; i < images.length; i++) {
|
| 515 |
+
if (publishCancelled || tabClosedByUser) break;
|
| 516 |
+
const img = images[i];
|
| 517 |
+
const result = await uploadImage(tabId, img.data, img.name);
|
| 518 |
+
if (result.success && result.photoId) photoIds.push(result.photoId);
|
| 519 |
+
if (i < images.length - 1) await waitWithCheck(1500);
|
| 520 |
+
}
|
| 521 |
+
}
|
| 522 |
+
if (tabClosedByUser || publishCancelled) { chrome.tabs.onRemoved.removeListener(onTabRemoved); if (!resolved) { resolved = true; chrome.tabs.remove(tabId, () => {}); currentPublishTabId = null; resolve({ success: false, error: 'تم الإلغاء', post_url: null }); } return; }
|
| 523 |
+
if (photoIds.length > 0) await waitWithCheck(2000);
|
| 524 |
+
if (tabClosedByUser || publishCancelled) { chrome.tabs.onRemoved.removeListener(onTabRemoved); if (!resolved) { resolved = true; chrome.tabs.remove(tabId, () => {}); currentPublishTabId = null; resolve({ success: false, error: 'تم الإلغاء', post_url: null }); } return; }
|
| 525 |
+
|
| 526 |
+
const publishResult = await publishToGroup(tabId, text, photoIds, groupId);
|
| 527 |
+
if (tabClosedByUser) { chrome.tabs.onRemoved.removeListener(onTabRemoved); return; }
|
| 528 |
+
if (publishResult.success && publishResult.post_url) {
|
| 529 |
+
await waitWithCheck(2000);
|
| 530 |
+
if (!tabClosedByUser) {
|
| 531 |
+
const statusCheck = await checkPostStatus(tabId, publishResult.post_url);
|
| 532 |
+
publishResult.pending = statusCheck.isPending;
|
| 533 |
+
}
|
| 534 |
+
}
|
| 535 |
+
chrome.tabs.onRemoved.removeListener(onTabRemoved);
|
| 536 |
+
setTimeout(() => { chrome.tabs.remove(tabId, () => {}); }, 3000);
|
| 537 |
+
currentPublishTabId = null;
|
| 538 |
+
if (!resolved) { resolved = true; resolve(publishResult); }
|
| 539 |
+
});
|
| 540 |
+
});
|
| 541 |
+
}
|
| 542 |
+
|
| 543 |
+
// ========== دوال مساعدة ==========
|
| 544 |
+
function isAppOrigin(url) { if (!url) return false; try { return new URL(url).origin === APP_ORIGIN; } catch { return false; } }
|
| 545 |
+
function isRealAppUrl(url) { if (!url) return false; try { const p = new URL(url); return p.pathname === '/app' || p.pathname.startsWith('/app/') || p.pathname.startsWith('/app?'); } catch { return url.includes('/app'); } }
|
| 546 |
+
|
| 547 |
+
// ========== معالجة الرسائل ==========
|
| 548 |
+
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
|
| 549 |
+
if (sender.tab && sender.tab.id && sender.tab.url && isAppOrigin(sender.tab.url)) {
|
| 550 |
+
appTabId = sender.tab.id;
|
| 551 |
+
if (isRealAppUrl(sender.tab.url)) chrome.storage.local.set({ appUrl: sender.tab.url });
|
| 552 |
+
}
|
| 553 |
+
|
| 554 |
+
if (request.action === "registerAppUrl") { sendResponse({ success: true }); return true; }
|
| 555 |
+
|
| 556 |
+
if (request.action === "getGroups") {
|
| 557 |
+
(async () => {
|
| 558 |
+
const sendProgress = (p) => { if (appTabId) chrome.tabs.sendMessage(appTabId, p).catch(() => {}); else chrome.runtime.sendMessage(p).catch(() => {}); };
|
| 559 |
+
sendProgress({ action: "groupsProgress", status: "checking", message: "جاري التحقق..." });
|
| 560 |
+
const captureResult = await ensureFacebookAndCaptureKeys();
|
| 561 |
+
if (!captureResult.success) { sendResponse({ success: false, error: captureResult.error, message: captureResult.message }); return; }
|
| 562 |
+
sendProgress({ action: "groupsProgress", status: "fetching", message: "جاري جلب المجموعات..." });
|
| 563 |
+
const groupsResult = await fetchAllGroups(captureResult.tabId);
|
| 564 |
+
if (groupsResult.groups && groupsResult.groups.length > 0) {
|
| 565 |
+
sendProgress({ action: "groupsProgress", status: "complete", message: `تم جلب ${groupsResult.groups.length} مجموعة` });
|
| 566 |
+
sendResponse({ success: true, groups: groupsResult.groups, userId: captureResult.userId });
|
| 567 |
+
} else {
|
| 568 |
+
sendResponse({ success: false, error: "no_groups", message: "لم يتم العثور على مجموعات" });
|
| 569 |
+
}
|
| 570 |
+
})();
|
| 571 |
+
return true;
|
| 572 |
+
}
|
| 573 |
+
|
| 574 |
+
if (request.action === "openFacebook") { chrome.tabs.create({ url: 'https://www.facebook.com', active: true }, () => sendResponse({ success: true })); return true; }
|
| 575 |
+
if (request.action === "removePublishImage") { if (typeof request.index === 'number' && request.index >= 0 && request.index < activePublishImages.length) activePublishImages.splice(request.index, 1); sendResponse({ success: true, remaining: activePublishImages.length }); return true; }
|
| 576 |
+
|
| 577 |
+
if (request.action === "stopPublishing") {
|
| 578 |
+
publishCancelled = true;
|
| 579 |
+
isPublishingNow = false;
|
| 580 |
+
chrome.storage.local.set({ publishCancelled: true });
|
| 581 |
+
if (currentPublishTabId !== null) { chrome.tabs.remove(currentPublishTabId, () => {}); currentPublishTabId = null; }
|
| 582 |
+
sendResponse({ success: true });
|
| 583 |
+
return true;
|
| 584 |
+
}
|
| 585 |
+
|
| 586 |
+
if (request.action === "getPublishStatus") { sendResponse({ isPublishing: isPublishingNow }); return true; }
|
| 587 |
+
|
| 588 |
+
if (request.action === "publishToGroup") {
|
| 589 |
+
(async () => {
|
| 590 |
+
await storageReady;
|
| 591 |
+
if (!USER_CONFIG.fb_dtsg || !USER_CONFIG.userId) { sendResponse({ success: false, error: "الرجاء جلب المجموعات أولاً" }); return; }
|
| 592 |
+
publishCancelled = false; isPublishingNow = true; chrome.storage.local.set({ publishCancelled: false });
|
| 593 |
+
const result = await publishToOneGroup(request.groupId, request.text || "", request.images || []);
|
| 594 |
+
isPublishingNow = false; sendResponse(result);
|
| 595 |
+
})();
|
| 596 |
+
return true;
|
| 597 |
+
}
|
| 598 |
+
|
| 599 |
+
if (request.action === "publishToMultipleGroups") {
|
| 600 |
+
(async () => {
|
| 601 |
+
await storageReady;
|
| 602 |
+
if (isPublishingNow) { sendResponse({ success: false, error: "النشر جارٍ بالفعل" }); return; }
|
| 603 |
+
if (!USER_CONFIG.fb_dtsg || !USER_CONFIG.userId) { sendResponse({ success: false, error: "الرجاء جلب المجموعات أولاً" }); return; }
|
| 604 |
+
if (!request.groupIds || request.groupIds.length === 0) { sendResponse({ success: false, error: "الرجاء اختيار مجموعة" }); return; }
|
| 605 |
+
|
| 606 |
+
publishCancelled = false; isPublishingNow = true;
|
| 607 |
+
chrome.storage.local.set({ publishCancelled: false });
|
| 608 |
+
|
| 609 |
+
const groupIds = request.groupIds;
|
| 610 |
+
const text = request.text || "";
|
| 611 |
+
activePublishImages = JSON.parse(JSON.stringify(request.images || []));
|
| 612 |
+
|
| 613 |
+
sendResponse({ success: true, started: true, total: groupIds.length });
|
| 614 |
+
|
| 615 |
+
const sendProgress = (payload) => {
|
| 616 |
+
if (appTabId) chrome.tabs.sendMessage(appTabId, payload).catch(() => {});
|
| 617 |
+
else chrome.runtime.sendMessage(payload).catch(() => {});
|
| 618 |
+
};
|
| 619 |
+
|
| 620 |
+
for (let i = 0; i < groupIds.length; i++) {
|
| 621 |
+
if (publishCancelled) {
|
| 622 |
+
const allProgress = [];
|
| 623 |
+
for (let j = i; j < groupIds.length; j++) {
|
| 624 |
+
allProgress.push({ groupId: groupIds[j], status: "failed", error: "تم إيقاف النشر - أُغلق تبويب فيسبوك" });
|
| 625 |
+
}
|
| 626 |
+
sendProgress({ action: "publishStopped", doneCount: i, total: groupIds.length, failedGroups: allProgress });
|
| 627 |
+
break;
|
| 628 |
+
}
|
| 629 |
+
|
| 630 |
+
const groupId = groupIds[i];
|
| 631 |
+
sendProgress({ action: "publishProgress", current: i + 1, total: groupIds.length, groupId, status: "publishing" });
|
| 632 |
+
|
| 633 |
+
const result = await publishToOneGroup(groupId, text, [...activePublishImages]);
|
| 634 |
+
|
| 635 |
+
if (publishCancelled) {
|
| 636 |
+
const allProgress = [];
|
| 637 |
+
for (let j = i + 1; j < groupIds.length; j++) {
|
| 638 |
+
allProgress.push({ groupId: groupIds[j], status: "failed", error: "تم إيقاف النشر - أُغلق تبويب فيسبوك" });
|
| 639 |
+
}
|
| 640 |
+
sendProgress({ action: "publishStopped", doneCount: i + (result.success ? 1 : 0), total: groupIds.length, failedGroups: allProgress });
|
| 641 |
+
break;
|
| 642 |
+
}
|
| 643 |
+
|
| 644 |
+
let status = result.success ? (result.pending ? "pending" : "success") : "failed";
|
| 645 |
+
sendProgress({ action: "publishProgress", current: i + 1, total: groupIds.length, groupId, status, pending: result.pending || false, error: result.error || null, postUrl: result.post_url || null });
|
| 646 |
+
|
| 647 |
+
if (i < groupIds.length - 1 && !publishCancelled) {
|
| 648 |
+
const delaySeconds = request.delayConfig?.mode === 'fixed' && request.delayConfig.seconds > 0 ? Math.max(10, Math.floor(request.delayConfig.seconds)) : randomDelay();
|
| 649 |
+
for (let sec = delaySeconds; sec > 0; sec--) {
|
| 650 |
+
if (publishCancelled) break;
|
| 651 |
+
sendProgress({ action: "publishCountdown", seconds: sec, totalDelay: delaySeconds, nextGroup: i + 2, total: groupIds.length });
|
| 652 |
+
await new Promise(r => setTimeout(r, 1000));
|
| 653 |
+
}
|
| 654 |
+
}
|
| 655 |
+
}
|
| 656 |
+
|
| 657 |
+
if (!publishCancelled) sendProgress({ action: "publishComplete", total: groupIds.length });
|
| 658 |
+
|
| 659 |
+
isPublishingNow = false; publishCancelled = false;
|
| 660 |
+
chrome.storage.local.set({ publishCancelled: false });
|
| 661 |
+
})();
|
| 662 |
+
return true;
|
| 663 |
+
}
|
| 664 |
+
});
|
| 665 |
+
|
| 666 |
+
// ========== فتح صفحة الأداة ==========
|
| 667 |
+
chrome.action.onClicked.addListener(async () => {
|
| 668 |
+
if (appTabId !== null) {
|
| 669 |
+
try { const tab = await chrome.tabs.get(appTabId); if (tab.url && isAppOrigin(tab.url)) { await chrome.tabs.update(appTabId, { active: true }); return; } } catch { appTabId = null; }
|
| 670 |
+
}
|
| 671 |
+
const stored = await chrome.storage.local.get("appUrl");
|
| 672 |
+
if (stored.appUrl && isRealAppUrl(stored.appUrl) && isAppOrigin(stored.appUrl)) {
|
| 673 |
+
try { const existingTabs = await chrome.tabs.query({ url: stored.appUrl.replace(/#.*$/, '') + '*' }); if (existingTabs.length > 0 && existingTabs[0].id) { appTabId = existingTabs[0].id; await chrome.tabs.update(existingTabs[0].id, { active: true }); return; } } catch { }
|
| 674 |
+
chrome.tabs.create({ url: stored.appUrl }); return;
|
| 675 |
+
}
|
| 676 |
+
try { const appTabs = await chrome.tabs.query({ url: [APP_ORIGIN + "/app", APP_ORIGIN + "/app/*"] }); if (appTabs.length > 0 && appTabs[0].id) { appTabId = appTabs[0].id; await chrome.tabs.update(appTabs[0].id, { active: true }); return; } } catch { }
|
| 677 |
+
chrome.tabs.create({ url: DEFAULT_APP_URL });
|
| 678 |
+
});
|
artifacts/fb-publisher/public/extension/src/bridge.source.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
(function () {
|
| 2 |
+
const BRIDGE_TAG = "fbpub-bridge";
|
| 3 |
+
const WEB_TAG = "fbpub-web";
|
| 4 |
+
|
| 5 |
+
console.log("[Bridge] Loaded v" + chrome.runtime.getManifest().version);
|
| 6 |
+
|
| 7 |
+
const APP_DOMAIN = "https://sare26-fb-publisher-backend.hf.space";
|
| 8 |
+
|
| 9 |
+
function isTrustedOrigin(origin) {
|
| 10 |
+
if (!origin || origin === "null") return false;
|
| 11 |
+
try {
|
| 12 |
+
const url = new URL(origin);
|
| 13 |
+
return url.origin === APP_DOMAIN;
|
| 14 |
+
} catch {
|
| 15 |
+
return false;
|
| 16 |
+
}
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
window.addEventListener("message", (event) => {
|
| 20 |
+
if (!isTrustedOrigin(event.origin)) {
|
| 21 |
+
console.warn("[Bridge] رسالة مرفوضة من مصدر غير موثوق:", event.origin);
|
| 22 |
+
return;
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
if (event.source !== window) return;
|
| 26 |
+
|
| 27 |
+
const msg = event.data;
|
| 28 |
+
if (!msg || msg.source !== WEB_TAG) return;
|
| 29 |
+
|
| 30 |
+
const { requestId, action, payload } = msg;
|
| 31 |
+
|
| 32 |
+
console.log("[Bridge] رسالة من الويب:", action, payload);
|
| 33 |
+
|
| 34 |
+
if (action === "ping") {
|
| 35 |
+
window.postMessage(
|
| 36 |
+
{
|
| 37 |
+
source: BRIDGE_TAG,
|
| 38 |
+
requestId,
|
| 39 |
+
response: {
|
| 40 |
+
installed: true,
|
| 41 |
+
version: chrome.runtime.getManifest().version,
|
| 42 |
+
},
|
| 43 |
+
},
|
| 44 |
+
APP_DOMAIN
|
| 45 |
+
);
|
| 46 |
+
return;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
chrome.runtime.sendMessage(
|
| 50 |
+
{ action, ...(payload || {}) },
|
| 51 |
+
(response) => {
|
| 52 |
+
console.log("[Bridge] رد الخلفية على", action, ":", response);
|
| 53 |
+
|
| 54 |
+
if (chrome.runtime.lastError) {
|
| 55 |
+
console.error(
|
| 56 |
+
"[Bridge] خطأ في الاتصال بالخلفية:",
|
| 57 |
+
chrome.runtime.lastError.message
|
| 58 |
+
);
|
| 59 |
+
window.postMessage(
|
| 60 |
+
{
|
| 61 |
+
source: BRIDGE_TAG,
|
| 62 |
+
requestId,
|
| 63 |
+
response: {
|
| 64 |
+
success: false,
|
| 65 |
+
error: chrome.runtime.lastError.message,
|
| 66 |
+
},
|
| 67 |
+
},
|
| 68 |
+
APP_DOMAIN
|
| 69 |
+
);
|
| 70 |
+
return;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
window.postMessage(
|
| 74 |
+
{
|
| 75 |
+
source: BRIDGE_TAG,
|
| 76 |
+
requestId,
|
| 77 |
+
response: response ?? { success: false, error: "no response" },
|
| 78 |
+
},
|
| 79 |
+
APP_DOMAIN
|
| 80 |
+
);
|
| 81 |
+
}
|
| 82 |
+
);
|
| 83 |
+
});
|
| 84 |
+
|
| 85 |
+
chrome.runtime.onMessage.addListener((message) => {
|
| 86 |
+
console.log("[Bridge] بث من الخلفية:", message);
|
| 87 |
+
window.postMessage(
|
| 88 |
+
{
|
| 89 |
+
source: BRIDGE_TAG,
|
| 90 |
+
broadcast: true,
|
| 91 |
+
message,
|
| 92 |
+
},
|
| 93 |
+
APP_DOMAIN
|
| 94 |
+
);
|
| 95 |
+
});
|
| 96 |
+
|
| 97 |
+
window.postMessage(
|
| 98 |
+
{
|
| 99 |
+
source: BRIDGE_TAG,
|
| 100 |
+
event: "ready",
|
| 101 |
+
version: chrome.runtime.getManifest().version,
|
| 102 |
+
},
|
| 103 |
+
APP_DOMAIN
|
| 104 |
+
);
|
| 105 |
+
|
| 106 |
+
const currentPath = window.location.pathname;
|
| 107 |
+
const isAppPage =
|
| 108 |
+
currentPath === "/app" ||
|
| 109 |
+
currentPath.startsWith("/app/") ||
|
| 110 |
+
currentPath.startsWith("/app?");
|
| 111 |
+
|
| 112 |
+
if (isAppPage) {
|
| 113 |
+
chrome.runtime.sendMessage({
|
| 114 |
+
action: "registerAppUrl",
|
| 115 |
+
url: window.location.href,
|
| 116 |
+
});
|
| 117 |
+
}
|
| 118 |
+
})();
|
artifacts/fb-publisher/public/favicon.png
ADDED
|
|
artifacts/fb-publisher/public/favicon.svg
ADDED
|
|
artifacts/fb-publisher/public/logo.png
ADDED
|
artifacts/fb-publisher/public/logo.svg
ADDED
|
|
artifacts/fb-publisher/public/opengraph.jpg
ADDED
|
artifacts/fb-publisher/public/robots.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
User-agent: *
|
| 2 |
+
Allow: /
|
artifacts/fb-publisher/src/App.tsx
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useEffect, useRef, useState } from "react";
|
| 2 |
+
import {
|
| 3 |
+
ClerkProvider,
|
| 4 |
+
SignIn,
|
| 5 |
+
SignUp,
|
| 6 |
+
Show,
|
| 7 |
+
useClerk,
|
| 8 |
+
useAuth,
|
| 9 |
+
} from "@clerk/react";
|
| 10 |
+
import { arSA } from "@clerk/localizations";
|
| 11 |
+
import { shadcn } from "@clerk/themes";
|
| 12 |
+
import { setAuthTokenGetter } from "@workspace/api-client-react";
|
| 13 |
+
import {
|
| 14 |
+
Switch,
|
| 15 |
+
Route,
|
| 16 |
+
useLocation,
|
| 17 |
+
Router as WouterRouter,
|
| 18 |
+
Redirect,
|
| 19 |
+
} from "wouter";
|
| 20 |
+
import { QueryClientProvider, useQueryClient } from "@tanstack/react-query";
|
| 21 |
+
import { queryClient } from "./lib/queryClient";
|
| 22 |
+
import { Toaster } from "@/components/ui/toaster";
|
| 23 |
+
import { TooltipProvider } from "@/components/ui/tooltip";
|
| 24 |
+
import Landing from "@/pages/Landing";
|
| 25 |
+
import Publisher from "@/pages/Publisher";
|
| 26 |
+
import Subscription from "@/pages/Subscription";
|
| 27 |
+
import SubscriptionReturn from "@/pages/SubscriptionReturn";
|
| 28 |
+
import InstallExtension from "@/pages/InstallExtension";
|
| 29 |
+
import Admin from "@/pages/Admin";
|
| 30 |
+
import NotFound from "@/pages/not-found";
|
| 31 |
+
import UpdateBanner from "@/components/UpdateBanner";
|
| 32 |
+
|
| 33 |
+
const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY as string;
|
| 34 |
+
const clerkProxyUrl = import.meta.env.VITE_CLERK_PROXY_URL as string | undefined;
|
| 35 |
+
const basePath = import.meta.env.BASE_URL.replace(/\/$/, "");
|
| 36 |
+
|
| 37 |
+
function stripBase(path: string): string {
|
| 38 |
+
return basePath && path.startsWith(basePath)
|
| 39 |
+
? path.slice(basePath.length) || "/"
|
| 40 |
+
: path;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
if (!clerkPubKey) {
|
| 44 |
+
throw new Error("Missing VITE_CLERK_PUBLISHABLE_KEY");
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
const clerkAppearance = {
|
| 48 |
+
theme: shadcn,
|
| 49 |
+
cssLayerName: "clerk",
|
| 50 |
+
options: {
|
| 51 |
+
logoPlacement: "inside" as const,
|
| 52 |
+
logoLinkUrl: basePath || "/",
|
| 53 |
+
logoImageUrl: `${window.location.origin}${basePath}/logo.svg`,
|
| 54 |
+
},
|
| 55 |
+
variables: {
|
| 56 |
+
colorPrimary: "#bb86fc",
|
| 57 |
+
colorForeground: "#f5f5f7",
|
| 58 |
+
colorMutedForeground: "#a8a8b3",
|
| 59 |
+
colorDanger: "#ff6b6b",
|
| 60 |
+
colorBackground: "#16162a",
|
| 61 |
+
colorInput: "#1f1f3a",
|
| 62 |
+
colorInputForeground: "#f5f5f7",
|
| 63 |
+
colorNeutral: "#3a3a55",
|
| 64 |
+
fontFamily: '"Tajawal", "Inter", system-ui, sans-serif',
|
| 65 |
+
borderRadius: "12px",
|
| 66 |
+
},
|
| 67 |
+
elements: {
|
| 68 |
+
rootBox: "w-full",
|
| 69 |
+
cardBox: "bg-[#16162a] rounded-2xl w-[440px] max-w-full overflow-hidden border border-[#3a3a55]/40",
|
| 70 |
+
card: "!shadow-none !border-0 !bg-transparent !rounded-none",
|
| 71 |
+
footer: "!shadow-none !border-0 !bg-transparent !rounded-none",
|
| 72 |
+
headerTitle: "text-white",
|
| 73 |
+
headerSubtitle: "text-gray-300",
|
| 74 |
+
socialButtonsBlockButtonText: "text-white",
|
| 75 |
+
formFieldLabel: "text-gray-200",
|
| 76 |
+
footerActionLink: "text-[#bb86fc] hover:text-[#d4b3ff]",
|
| 77 |
+
footerActionText: "text-gray-300",
|
| 78 |
+
dividerText: "text-gray-400",
|
| 79 |
+
identityPreviewEditButton: "text-[#bb86fc]",
|
| 80 |
+
formFieldSuccessText: "text-green-300",
|
| 81 |
+
alertText: "text-gray-200",
|
| 82 |
+
logoBox: "mb-2",
|
| 83 |
+
logoImage: "h-10 w-10",
|
| 84 |
+
socialButtonsBlockButton: "bg-[#1f1f3a] hover:bg-[#2a2a4a] border border-[#3a3a55] text-white",
|
| 85 |
+
formButtonPrimary: "btn-primary-grad",
|
| 86 |
+
formFieldInput: "bg-[#1f1f3a] border-[#3a3a55] text-white",
|
| 87 |
+
footerAction: "text-gray-300",
|
| 88 |
+
dividerLine: "bg-[#3a3a55]",
|
| 89 |
+
alert: "bg-[#1f1f3a] border-[#3a3a55]",
|
| 90 |
+
otpCodeFieldInput: "bg-[#1f1f3a] border-[#3a3a55] text-white",
|
| 91 |
+
main: "text-white",
|
| 92 |
+
},
|
| 93 |
+
};
|
| 94 |
+
|
| 95 |
+
function SignInPage() {
|
| 96 |
+
return (
|
| 97 |
+
<div className="flex min-h-[100dvh] items-center justify-center px-4">
|
| 98 |
+
<SignIn
|
| 99 |
+
routing="path"
|
| 100 |
+
path={`${basePath}/sign-in`}
|
| 101 |
+
signUpUrl={`${basePath}/sign-up`}
|
| 102 |
+
forceRedirectUrl={`${basePath}/app`}
|
| 103 |
+
/>
|
| 104 |
+
</div>
|
| 105 |
+
);
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
function SignUpPage() {
|
| 109 |
+
return (
|
| 110 |
+
<div className="flex min-h-[100dvh] items-center justify-center px-4">
|
| 111 |
+
<div className="w-full max-w-md space-y-4">
|
| 112 |
+
<SignUp
|
| 113 |
+
routing="path"
|
| 114 |
+
path={`${basePath}/sign-up`}
|
| 115 |
+
signInUrl={`${basePath}/sign-in`}
|
| 116 |
+
forceRedirectUrl={`${basePath}/app`}
|
| 117 |
+
/>
|
| 118 |
+
<div className="rounded-lg border border-red-500/30 bg-red-950/20 p-3 text-center text-sm text-red-300 backdrop-blur-sm">
|
| 119 |
+
⚠️ <strong>تنبيه هام لمكافحة السبام والرسائل المزعجة (Spam):</strong>{" "}
|
| 120 |
+
التسجيل باستخدام البريد الإلكتروني المؤقت (مثل{" "}
|
| 121 |
+
<code className="rounded bg-red-950/40 px-1">tempumail.art</code>)
|
| 122 |
+
ممنوع تمامًا وسيؤدي إلى <strong>حظر الحساب فور اكتشافه</strong>. يُرجى
|
| 123 |
+
استخدام بريد إلكتروني حقيقي ودائم.
|
| 124 |
+
</div>
|
| 125 |
+
</div>
|
| 126 |
+
</div>
|
| 127 |
+
);
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
function ProtectedPublisher() {
|
| 131 |
+
return (
|
| 132 |
+
<>
|
| 133 |
+
<Show when="signed-in">
|
| 134 |
+
<Publisher />
|
| 135 |
+
</Show>
|
| 136 |
+
<Show when="signed-out">
|
| 137 |
+
<Redirect to="/sign-in" />
|
| 138 |
+
</Show>
|
| 139 |
+
</>
|
| 140 |
+
);
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
function ProtectedSubscription() {
|
| 144 |
+
return (
|
| 145 |
+
<>
|
| 146 |
+
<Show when="signed-in">
|
| 147 |
+
<Subscription />
|
| 148 |
+
</Show>
|
| 149 |
+
<Show when="signed-out">
|
| 150 |
+
<Redirect to="/sign-in" />
|
| 151 |
+
</Show>
|
| 152 |
+
</>
|
| 153 |
+
);
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
function ProtectedExtension() {
|
| 157 |
+
return (
|
| 158 |
+
<>
|
| 159 |
+
<Show when="signed-in">
|
| 160 |
+
<InstallExtension />
|
| 161 |
+
</Show>
|
| 162 |
+
<Show when="signed-out">
|
| 163 |
+
<Redirect to="/sign-in" />
|
| 164 |
+
</Show>
|
| 165 |
+
</>
|
| 166 |
+
);
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
function ServerKeepAlive() {
|
| 170 |
+
const [waking, setWaking] = useState(false);
|
| 171 |
+
|
| 172 |
+
useEffect(() => {
|
| 173 |
+
let wakingTimer: ReturnType<typeof setTimeout>;
|
| 174 |
+
const ping = () => {
|
| 175 |
+
wakingTimer = setTimeout(() => setWaking(true), 4000);
|
| 176 |
+
fetch("/api/healthz", { cache: "no-store" })
|
| 177 |
+
.then(() => { clearTimeout(wakingTimer); setWaking(false); })
|
| 178 |
+
.catch(() => { clearTimeout(wakingTimer); setWaking(false); });
|
| 179 |
+
};
|
| 180 |
+
ping();
|
| 181 |
+
const interval = setInterval(ping, 10 * 60 * 1000);
|
| 182 |
+
return () => { clearInterval(interval); clearTimeout(wakingTimer); };
|
| 183 |
+
}, []);
|
| 184 |
+
|
| 185 |
+
if (!waking) return null;
|
| 186 |
+
|
| 187 |
+
return (
|
| 188 |
+
<div className="fixed inset-0 z-[9999] flex items-center justify-center bg-[#16162a]/90 backdrop-blur-sm" dir="rtl">
|
| 189 |
+
<div className="bg-[#1f1f3a] border border-[#3a3a55] rounded-2xl p-8 text-center max-w-sm mx-4 shadow-2xl">
|
| 190 |
+
<div className="text-5xl mb-4">⚙️</div>
|
| 191 |
+
<h2 className="text-white text-xl font-bold mb-2">جاري تشغيل الخادم…</h2>
|
| 192 |
+
<p className="text-gray-400 text-sm leading-relaxed">الخادم كان في وضع السكون.<br />يستغرق الأمر بضع ثوانٍ، شكراً لصبرك.</p>
|
| 193 |
+
<div className="mt-5 flex justify-center gap-2">
|
| 194 |
+
{[0, 1, 2].map((i) => (
|
| 195 |
+
<div key={i} className="w-2.5 h-2.5 bg-[#bb86fc] rounded-full animate-bounce" style={{ animationDelay: `${i * 0.18}s` }} />
|
| 196 |
+
))}
|
| 197 |
+
</div>
|
| 198 |
+
</div>
|
| 199 |
+
</div>
|
| 200 |
+
);
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
function ClerkAuthTokenSetter() {
|
| 204 |
+
const { getToken } = useAuth();
|
| 205 |
+
useEffect(() => {
|
| 206 |
+
setAuthTokenGetter(() => getToken());
|
| 207 |
+
return () => setAuthTokenGetter(null);
|
| 208 |
+
}, [getToken]);
|
| 209 |
+
return null;
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
function ClerkQueryClientCacheInvalidator() {
|
| 213 |
+
const { addListener } = useClerk();
|
| 214 |
+
const qc = useQueryClient();
|
| 215 |
+
const prevUserIdRef = useRef<string | null | undefined>(undefined);
|
| 216 |
+
|
| 217 |
+
useEffect(() => {
|
| 218 |
+
const unsubscribe = addListener(({ user }) => {
|
| 219 |
+
const userId = user?.id ?? null;
|
| 220 |
+
if (prevUserIdRef.current !== undefined && prevUserIdRef.current !== userId) {
|
| 221 |
+
qc.clear();
|
| 222 |
+
}
|
| 223 |
+
prevUserIdRef.current = userId;
|
| 224 |
+
});
|
| 225 |
+
return unsubscribe;
|
| 226 |
+
}, [addListener, qc]);
|
| 227 |
+
|
| 228 |
+
return null;
|
| 229 |
+
}
|
| 230 |
+
|
| 231 |
+
function ClerkProviderWithRoutes() {
|
| 232 |
+
const [, setLocation] = useLocation();
|
| 233 |
+
|
| 234 |
+
return (
|
| 235 |
+
<ClerkProvider
|
| 236 |
+
publishableKey={clerkPubKey}
|
| 237 |
+
proxyUrl={clerkProxyUrl}
|
| 238 |
+
appearance={clerkAppearance}
|
| 239 |
+
signInUrl={`${basePath}/sign-in`}
|
| 240 |
+
signUpUrl={`${basePath}/sign-up`}
|
| 241 |
+
localization={{
|
| 242 |
+
...arSA,
|
| 243 |
+
formFieldInputPlaceholder__emailAddress: "أدخل بريدك الإلكتروني",
|
| 244 |
+
signIn: { ...arSA.signIn, start: { ...((arSA.signIn as any)?.start), title: "تسجيل الدخول", subtitle: "أدخل بياناتك للوصول إلى حسابك" } },
|
| 245 |
+
signUp: { ...arSA.signUp, start: { ...((arSA.signUp as any)?.start), title: "إنشاء حساب جديد", subtitle: "ابدأ تجربتك المجانية لمدة شهر كامل" } },
|
| 246 |
+
signIn__forgotPasswordLink: "نسيت كلمة المرور؟",
|
| 247 |
+
}}
|
| 248 |
+
routerPush={(to) => setLocation(stripBase(to))}
|
| 249 |
+
routerReplace={(to) => setLocation(stripBase(to), { replace: true })}
|
| 250 |
+
>
|
| 251 |
+
<QueryClientProvider client={queryClient}>
|
| 252 |
+
<ServerKeepAlive />
|
| 253 |
+
<ClerkAuthTokenSetter />
|
| 254 |
+
<ClerkQueryClientCacheInvalidator />
|
| 255 |
+
<TooltipProvider>
|
| 256 |
+
<Switch>
|
| 257 |
+
<Route path="/" component={Landing} />
|
| 258 |
+
<Route path="/sign-in/*?" component={SignInPage} />
|
| 259 |
+
<Route path="/sign-up/*?" component={SignUpPage} />
|
| 260 |
+
<Route path="/app" component={ProtectedPublisher} />
|
| 261 |
+
<Route path="/subscription" component={ProtectedSubscription} />
|
| 262 |
+
<Route path="/subscription/return" component={SubscriptionReturn} />
|
| 263 |
+
<Route path="/extension" component={ProtectedExtension} />
|
| 264 |
+
<Route path="/admin" component={Admin} />
|
| 265 |
+
<Route component={NotFound} />
|
| 266 |
+
</Switch>
|
| 267 |
+
<UpdateBanner />
|
| 268 |
+
<Toaster />
|
| 269 |
+
</TooltipProvider>
|
| 270 |
+
</QueryClientProvider>
|
| 271 |
+
</ClerkProvider>
|
| 272 |
+
);
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
export default function App() {
|
| 276 |
+
return (
|
| 277 |
+
<WouterRouter base={basePath}>
|
| 278 |
+
<ClerkProviderWithRoutes />
|
| 279 |
+
</WouterRouter>
|
| 280 |
+
);
|
| 281 |
+
}
|
artifacts/fb-publisher/src/components/UpdateBanner.tsx
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { useEffect, useState } from "react";
|
| 2 |
+
import { useQuery } from "@tanstack/react-query";
|
| 3 |
+
import { useLocation } from "wouter";
|
| 4 |
+
|
| 5 |
+
interface UpdateStatus {
|
| 6 |
+
updateAvailable: boolean;
|
| 7 |
+
version: string | null;
|
| 8 |
+
message: string | null;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
async function fetchUpdateStatus(): Promise<UpdateStatus> {
|
| 12 |
+
const res = await fetch("/api/extension-update");
|
| 13 |
+
if (!res.ok) return { updateAvailable: false, version: null, message: null };
|
| 14 |
+
return res.json();
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
function compareVersions(installed: string, latest: string): number {
|
| 18 |
+
const a = installed.split(".").map(Number);
|
| 19 |
+
const b = latest.split(".").map(Number);
|
| 20 |
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
| 21 |
+
const diff = (a[i] ?? 0) - (b[i] ?? 0);
|
| 22 |
+
if (diff !== 0) return diff;
|
| 23 |
+
}
|
| 24 |
+
return 0;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
function pingExtension(): Promise<string | null> {
|
| 28 |
+
return new Promise((resolve) => {
|
| 29 |
+
const reqId = `ping-${Date.now()}`;
|
| 30 |
+
const timeout = setTimeout(() => {
|
| 31 |
+
window.removeEventListener("message", handler);
|
| 32 |
+
resolve(null);
|
| 33 |
+
}, 2000);
|
| 34 |
+
|
| 35 |
+
function handler(e: MessageEvent) {
|
| 36 |
+
if (e.source !== window) return;
|
| 37 |
+
const d = e.data;
|
| 38 |
+
if (!d) return;
|
| 39 |
+
if (d.source === "fbpub-bridge" && d.event === "ready") {
|
| 40 |
+
clearTimeout(timeout);
|
| 41 |
+
window.removeEventListener("message", handler);
|
| 42 |
+
resolve(d.version ?? null);
|
| 43 |
+
return;
|
| 44 |
+
}
|
| 45 |
+
if (d.source === "fbpub-bridge" && d.requestId === reqId) {
|
| 46 |
+
clearTimeout(timeout);
|
| 47 |
+
window.removeEventListener("message", handler);
|
| 48 |
+
resolve(d.response?.version ?? null);
|
| 49 |
+
}
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
window.addEventListener("message", handler);
|
| 53 |
+
|
| 54 |
+
window.postMessage(
|
| 55 |
+
{ source: "fbpub-web", requestId: reqId, action: "ping", payload: {} },
|
| 56 |
+
"*"
|
| 57 |
+
);
|
| 58 |
+
});
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
export default function UpdateBanner() {
|
| 62 |
+
const [, setLocation] = useLocation();
|
| 63 |
+
const [installedVersion, setInstalledVersion] = useState<string | null>(null);
|
| 64 |
+
const [checked, setChecked] = useState(false);
|
| 65 |
+
|
| 66 |
+
const { data } = useQuery({
|
| 67 |
+
queryKey: ["extension-update"],
|
| 68 |
+
queryFn: fetchUpdateStatus,
|
| 69 |
+
refetchInterval: 30 * 60 * 1000,
|
| 70 |
+
staleTime: 25 * 60 * 1000,
|
| 71 |
+
});
|
| 72 |
+
|
| 73 |
+
useEffect(() => {
|
| 74 |
+
pingExtension().then((v) => {
|
| 75 |
+
setInstalledVersion(v);
|
| 76 |
+
setChecked(true);
|
| 77 |
+
});
|
| 78 |
+
}, []);
|
| 79 |
+
|
| 80 |
+
if (!data?.updateAvailable) return null;
|
| 81 |
+
if (!checked) return null;
|
| 82 |
+
|
| 83 |
+
if (data.version && installedVersion) {
|
| 84 |
+
if (compareVersions(installedVersion, data.version) >= 0) return null;
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
const label = data.version ? `تحديث جديد v${data.version}` : "تحديث جديد متاح";
|
| 88 |
+
|
| 89 |
+
return (
|
| 90 |
+
<div className="fixed bottom-5 left-5 z-50" dir="rtl">
|
| 91 |
+
<button
|
| 92 |
+
onClick={() => setLocation("/extension")}
|
| 93 |
+
className="relative flex flex-col items-start gap-0.5 rounded-2xl px-4 py-3 text-white shadow-xl transition-transform hover:scale-105 active:scale-95"
|
| 94 |
+
style={{
|
| 95 |
+
background: "linear-gradient(135deg, #bb86fc 0%, #6200ee 100%)",
|
| 96 |
+
animation: "pulse-glow 2s infinite",
|
| 97 |
+
}}
|
| 98 |
+
>
|
| 99 |
+
<div className="flex items-center gap-2 font-bold text-sm">
|
| 100 |
+
<span className="relative flex h-3 w-3">
|
| 101 |
+
<span
|
| 102 |
+
className="absolute inline-flex h-full w-full rounded-full opacity-75 bg-white"
|
| 103 |
+
style={{ animation: "ping 1.2s cubic-bezier(0,0,0.2,1) infinite" }}
|
| 104 |
+
/>
|
| 105 |
+
<span className="relative inline-flex h-3 w-3 rounded-full bg-white" />
|
| 106 |
+
</span>
|
| 107 |
+
🔔 {label}
|
| 108 |
+
</div>
|
| 109 |
+
{data.message && (
|
| 110 |
+
<div className="text-xs text-white/80 pr-5">{data.message}</div>
|
| 111 |
+
)}
|
| 112 |
+
</button>
|
| 113 |
+
|
| 114 |
+
<style>{`
|
| 115 |
+
@keyframes pulse-glow {
|
| 116 |
+
0%, 100% { box-shadow: 0 0 12px 3px rgba(187,134,252,0.6); }
|
| 117 |
+
50% { box-shadow: 0 0 28px 10px rgba(187,134,252,0.3); }
|
| 118 |
+
}
|
| 119 |
+
@keyframes ping {
|
| 120 |
+
75%, 100% { transform: scale(2); opacity: 0; }
|
| 121 |
+
}
|
| 122 |
+
`}</style>
|
| 123 |
+
</div>
|
| 124 |
+
);
|
| 125 |
+
}
|
artifacts/fb-publisher/src/components/ui/accordion.tsx
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import * as React from "react"
|
| 2 |
+
import * as AccordionPrimitive from "@radix-ui/react-accordion"
|
| 3 |
+
import { ChevronDown } from "lucide-react"
|
| 4 |
+
|
| 5 |
+
import { cn } from "@/lib/utils"
|
| 6 |
+
|
| 7 |
+
const Accordion = AccordionPrimitive.Root
|
| 8 |
+
|
| 9 |
+
const AccordionItem = React.forwardRef<
|
| 10 |
+
React.ElementRef<typeof AccordionPrimitive.Item>,
|
| 11 |
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
| 12 |
+
>(({ className, ...props }, ref) => (
|
| 13 |
+
<AccordionPrimitive.Item
|
| 14 |
+
ref={ref}
|
| 15 |
+
className={cn("border-b", className)}
|
| 16 |
+
{...props}
|
| 17 |
+
/>
|
| 18 |
+
))
|
| 19 |
+
AccordionItem.displayName = "AccordionItem"
|
| 20 |
+
|
| 21 |
+
const AccordionTrigger = React.forwardRef<
|
| 22 |
+
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
| 23 |
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
| 24 |
+
>(({ className, children, ...props }, ref) => (
|
| 25 |
+
<AccordionPrimitive.Header className="flex">
|
| 26 |
+
<AccordionPrimitive.Trigger
|
| 27 |
+
ref={ref}
|
| 28 |
+
className={cn(
|
| 29 |
+
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline text-left [&[data-state=open]>svg]:rotate-180",
|
| 30 |
+
className
|
| 31 |
+
)}
|
| 32 |
+
{...props}
|
| 33 |
+
>
|
| 34 |
+
{children}
|
| 35 |
+
<ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
|
| 36 |
+
</AccordionPrimitive.Trigger>
|
| 37 |
+
</AccordionPrimitive.Header>
|
| 38 |
+
))
|
| 39 |
+
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
|
| 40 |
+
|
| 41 |
+
const AccordionContent = React.forwardRef<
|
| 42 |
+
React.ElementRef<typeof AccordionPrimitive.Content>,
|
| 43 |
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
| 44 |
+
>(({ className, children, ...props }, ref) => (
|
| 45 |
+
<AccordionPrimitive.Content
|
| 46 |
+
ref={ref}
|
| 47 |
+
className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
| 48 |
+
{...props}
|
| 49 |
+
>
|
| 50 |
+
<div className={cn("pb-4 pt-0", className)}>{children}</div>
|
| 51 |
+
</AccordionPrimitive.Content>
|
| 52 |
+
))
|
| 53 |
+
AccordionContent.displayName = AccordionPrimitive.Content.displayName
|
| 54 |
+
|
| 55 |
+
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
|
artifacts/fb-publisher/src/components/ui/alert-dialog.tsx
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import * as React from "react"
|
| 2 |
+
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
|
| 3 |
+
|
| 4 |
+
import { cn } from "@/lib/utils"
|
| 5 |
+
import { buttonVariants } from "@/components/ui/button"
|
| 6 |
+
|
| 7 |
+
const AlertDialog = AlertDialogPrimitive.Root
|
| 8 |
+
|
| 9 |
+
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
|
| 10 |
+
|
| 11 |
+
const AlertDialogPortal = AlertDialogPrimitive.Portal
|
| 12 |
+
|
| 13 |
+
const AlertDialogOverlay = React.forwardRef<
|
| 14 |
+
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
|
| 15 |
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
|
| 16 |
+
>(({ className, ...props }, ref) => (
|
| 17 |
+
<AlertDialogPrimitive.Overlay
|
| 18 |
+
className={cn(
|
| 19 |
+
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
| 20 |
+
className
|
| 21 |
+
)}
|
| 22 |
+
{...props}
|
| 23 |
+
ref={ref}
|
| 24 |
+
/>
|
| 25 |
+
))
|
| 26 |
+
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
|
| 27 |
+
|
| 28 |
+
const AlertDialogContent = React.forwardRef<
|
| 29 |
+
React.ElementRef<typeof AlertDialogPrimitive.Content>,
|
| 30 |
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
|
| 31 |
+
>(({ className, ...props }, ref) => (
|
| 32 |
+
<AlertDialogPortal>
|
| 33 |
+
<AlertDialogOverlay />
|
| 34 |
+
<AlertDialogPrimitive.Content
|
| 35 |
+
ref={ref}
|
| 36 |
+
className={cn(
|
| 37 |
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
| 38 |
+
className
|
| 39 |
+
)}
|
| 40 |
+
{...props}
|
| 41 |
+
/>
|
| 42 |
+
</AlertDialogPortal>
|
| 43 |
+
))
|
| 44 |
+
AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
|
| 45 |
+
|
| 46 |
+
const AlertDialogHeader = ({
|
| 47 |
+
className,
|
| 48 |
+
...props
|
| 49 |
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
| 50 |
+
<div
|
| 51 |
+
className={cn(
|
| 52 |
+
"flex flex-col space-y-2 text-center sm:text-left",
|
| 53 |
+
className
|
| 54 |
+
)}
|
| 55 |
+
{...props}
|
| 56 |
+
/>
|
| 57 |
+
)
|
| 58 |
+
AlertDialogHeader.displayName = "AlertDialogHeader"
|
| 59 |
+
|
| 60 |
+
const AlertDialogFooter = ({
|
| 61 |
+
className,
|
| 62 |
+
...props
|
| 63 |
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
| 64 |
+
<div
|
| 65 |
+
className={cn(
|
| 66 |
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
| 67 |
+
className
|
| 68 |
+
)}
|
| 69 |
+
{...props}
|
| 70 |
+
/>
|
| 71 |
+
)
|
| 72 |
+
AlertDialogFooter.displayName = "AlertDialogFooter"
|
| 73 |
+
|
| 74 |
+
const AlertDialogTitle = React.forwardRef<
|
| 75 |
+
React.ElementRef<typeof AlertDialogPrimitive.Title>,
|
| 76 |
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>
|
| 77 |
+
>(({ className, ...props }, ref) => (
|
| 78 |
+
<AlertDialogPrimitive.Title
|
| 79 |
+
ref={ref}
|
| 80 |
+
className={cn("text-lg font-semibold", className)}
|
| 81 |
+
{...props}
|
| 82 |
+
/>
|
| 83 |
+
))
|
| 84 |
+
AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
|
| 85 |
+
|
| 86 |
+
const AlertDialogDescription = React.forwardRef<
|
| 87 |
+
React.ElementRef<typeof AlertDialogPrimitive.Description>,
|
| 88 |
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>
|
| 89 |
+
>(({ className, ...props }, ref) => (
|
| 90 |
+
<AlertDialogPrimitive.Description
|
| 91 |
+
ref={ref}
|
| 92 |
+
className={cn("text-sm text-muted-foreground", className)}
|
| 93 |
+
{...props}
|
| 94 |
+
/>
|
| 95 |
+
))
|
| 96 |
+
AlertDialogDescription.displayName =
|
| 97 |
+
AlertDialogPrimitive.Description.displayName
|
| 98 |
+
|
| 99 |
+
const AlertDialogAction = React.forwardRef<
|
| 100 |
+
React.ElementRef<typeof AlertDialogPrimitive.Action>,
|
| 101 |
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
|
| 102 |
+
>(({ className, ...props }, ref) => (
|
| 103 |
+
<AlertDialogPrimitive.Action
|
| 104 |
+
ref={ref}
|
| 105 |
+
className={cn(buttonVariants(), className)}
|
| 106 |
+
{...props}
|
| 107 |
+
/>
|
| 108 |
+
))
|
| 109 |
+
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
|
| 110 |
+
|
| 111 |
+
const AlertDialogCancel = React.forwardRef<
|
| 112 |
+
React.ElementRef<typeof AlertDialogPrimitive.Cancel>,
|
| 113 |
+
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
|
| 114 |
+
>(({ className, ...props }, ref) => (
|
| 115 |
+
<AlertDialogPrimitive.Cancel
|
| 116 |
+
ref={ref}
|
| 117 |
+
className={cn(
|
| 118 |
+
buttonVariants({ variant: "outline" }),
|
| 119 |
+
"mt-2 sm:mt-0",
|
| 120 |
+
className
|
| 121 |
+
)}
|
| 122 |
+
{...props}
|
| 123 |
+
/>
|
| 124 |
+
))
|
| 125 |
+
AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
|
| 126 |
+
|
| 127 |
+
export {
|
| 128 |
+
AlertDialog,
|
| 129 |
+
AlertDialogPortal,
|
| 130 |
+
AlertDialogOverlay,
|
| 131 |
+
AlertDialogTrigger,
|
| 132 |
+
AlertDialogContent,
|
| 133 |
+
AlertDialogHeader,
|
| 134 |
+
AlertDialogFooter,
|
| 135 |
+
AlertDialogTitle,
|
| 136 |
+
AlertDialogDescription,
|
| 137 |
+
AlertDialogAction,
|
| 138 |
+
AlertDialogCancel,
|
| 139 |
+
}
|
artifacts/fb-publisher/src/components/ui/alert.tsx
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import * as React from "react"
|
| 2 |
+
import { cva, type VariantProps } from "class-variance-authority"
|
| 3 |
+
|
| 4 |
+
import { cn } from "@/lib/utils"
|
| 5 |
+
|
| 6 |
+
const alertVariants = cva(
|
| 7 |
+
"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
|
| 8 |
+
{
|
| 9 |
+
variants: {
|
| 10 |
+
variant: {
|
| 11 |
+
default: "bg-background text-foreground",
|
| 12 |
+
destructive:
|
| 13 |
+
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
| 14 |
+
},
|
| 15 |
+
},
|
| 16 |
+
defaultVariants: {
|
| 17 |
+
variant: "default",
|
| 18 |
+
},
|
| 19 |
+
}
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
const Alert = React.forwardRef<
|
| 23 |
+
HTMLDivElement,
|
| 24 |
+
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
| 25 |
+
>(({ className, variant, ...props }, ref) => (
|
| 26 |
+
<div
|
| 27 |
+
ref={ref}
|
| 28 |
+
role="alert"
|
| 29 |
+
className={cn(alertVariants({ variant }), className)}
|
| 30 |
+
{...props}
|
| 31 |
+
/>
|
| 32 |
+
))
|
| 33 |
+
Alert.displayName = "Alert"
|
| 34 |
+
|
| 35 |
+
const AlertTitle = React.forwardRef<
|
| 36 |
+
HTMLParagraphElement,
|
| 37 |
+
React.HTMLAttributes<HTMLHeadingElement>
|
| 38 |
+
>(({ className, ...props }, ref) => (
|
| 39 |
+
<h5
|
| 40 |
+
ref={ref}
|
| 41 |
+
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
| 42 |
+
{...props}
|
| 43 |
+
/>
|
| 44 |
+
))
|
| 45 |
+
AlertTitle.displayName = "AlertTitle"
|
| 46 |
+
|
| 47 |
+
const AlertDescription = React.forwardRef<
|
| 48 |
+
HTMLParagraphElement,
|
| 49 |
+
React.HTMLAttributes<HTMLParagraphElement>
|
| 50 |
+
>(({ className, ...props }, ref) => (
|
| 51 |
+
<div
|
| 52 |
+
ref={ref}
|
| 53 |
+
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
| 54 |
+
{...props}
|
| 55 |
+
/>
|
| 56 |
+
))
|
| 57 |
+
AlertDescription.displayName = "AlertDescription"
|
| 58 |
+
|
| 59 |
+
export { Alert, AlertTitle, AlertDescription }
|
artifacts/fb-publisher/src/components/ui/aspect-ratio.tsx
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"
|
| 2 |
+
|
| 3 |
+
const AspectRatio = AspectRatioPrimitive.Root
|
| 4 |
+
|
| 5 |
+
export { AspectRatio }
|
artifacts/fb-publisher/src/components/ui/avatar.tsx
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"use client"
|
| 2 |
+
|
| 3 |
+
import * as React from "react"
|
| 4 |
+
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
| 5 |
+
|
| 6 |
+
import { cn } from "@/lib/utils"
|
| 7 |
+
|
| 8 |
+
const Avatar = React.forwardRef<
|
| 9 |
+
React.ElementRef<typeof AvatarPrimitive.Root>,
|
| 10 |
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
|
| 11 |
+
>(({ className, ...props }, ref) => (
|
| 12 |
+
<AvatarPrimitive.Root
|
| 13 |
+
ref={ref}
|
| 14 |
+
className={cn(
|
| 15 |
+
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
|
| 16 |
+
className
|
| 17 |
+
)}
|
| 18 |
+
{...props}
|
| 19 |
+
/>
|
| 20 |
+
))
|
| 21 |
+
Avatar.displayName = AvatarPrimitive.Root.displayName
|
| 22 |
+
|
| 23 |
+
const AvatarImage = React.forwardRef<
|
| 24 |
+
React.ElementRef<typeof AvatarPrimitive.Image>,
|
| 25 |
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
|
| 26 |
+
>(({ className, ...props }, ref) => (
|
| 27 |
+
<AvatarPrimitive.Image
|
| 28 |
+
ref={ref}
|
| 29 |
+
className={cn("aspect-square h-full w-full", className)}
|
| 30 |
+
{...props}
|
| 31 |
+
/>
|
| 32 |
+
))
|
| 33 |
+
AvatarImage.displayName = AvatarPrimitive.Image.displayName
|
| 34 |
+
|
| 35 |
+
const AvatarFallback = React.forwardRef<
|
| 36 |
+
React.ElementRef<typeof AvatarPrimitive.Fallback>,
|
| 37 |
+
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
|
| 38 |
+
>(({ className, ...props }, ref) => (
|
| 39 |
+
<AvatarPrimitive.Fallback
|
| 40 |
+
ref={ref}
|
| 41 |
+
className={cn(
|
| 42 |
+
"flex h-full w-full items-center justify-center rounded-full bg-muted",
|
| 43 |
+
className
|
| 44 |
+
)}
|
| 45 |
+
{...props}
|
| 46 |
+
/>
|
| 47 |
+
))
|
| 48 |
+
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
|
| 49 |
+
|
| 50 |
+
export { Avatar, AvatarImage, AvatarFallback }
|
artifacts/fb-publisher/src/components/ui/badge.tsx
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import * as React from "react"
|
| 2 |
+
import { cva, type VariantProps } from "class-variance-authority"
|
| 3 |
+
|
| 4 |
+
import { cn } from "@/lib/utils"
|
| 5 |
+
|
| 6 |
+
const badgeVariants = cva(
|
| 7 |
+
// @replit
|
| 8 |
+
// Whitespace-nowrap: Badges should never wrap.
|
| 9 |
+
"whitespace-nowrap inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2" +
|
| 10 |
+
" hover-elevate ",
|
| 11 |
+
{
|
| 12 |
+
variants: {
|
| 13 |
+
variant: {
|
| 14 |
+
default:
|
| 15 |
+
// @replit shadow-xs instead of shadow, no hover because we use hover-elevate
|
| 16 |
+
"border-transparent bg-primary text-primary-foreground shadow-xs",
|
| 17 |
+
secondary:
|
| 18 |
+
// @replit no hover because we use hover-elevate
|
| 19 |
+
"border-transparent bg-secondary text-secondary-foreground",
|
| 20 |
+
destructive:
|
| 21 |
+
// @replit shadow-xs instead of shadow, no hover because we use hover-elevate
|
| 22 |
+
"border-transparent bg-destructive text-destructive-foreground shadow-xs",
|
| 23 |
+
// @replit shadow-xs" - use badge outline variable
|
| 24 |
+
outline: "text-foreground border [border-color:var(--badge-outline)]",
|
| 25 |
+
},
|
| 26 |
+
},
|
| 27 |
+
defaultVariants: {
|
| 28 |
+
variant: "default",
|
| 29 |
+
},
|
| 30 |
+
}
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
export interface BadgeProps
|
| 34 |
+
extends React.HTMLAttributes<HTMLDivElement>,
|
| 35 |
+
VariantProps<typeof badgeVariants> {}
|
| 36 |
+
|
| 37 |
+
function Badge({ className, variant, ...props }: BadgeProps) {
|
| 38 |
+
return (
|
| 39 |
+
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
| 40 |
+
)
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
export { Badge, badgeVariants }
|
artifacts/fb-publisher/src/components/ui/breadcrumb.tsx
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import * as React from "react"
|
| 2 |
+
import { Slot } from "@radix-ui/react-slot"
|
| 3 |
+
import { ChevronRight, MoreHorizontal } from "lucide-react"
|
| 4 |
+
|
| 5 |
+
import { cn } from "@/lib/utils"
|
| 6 |
+
|
| 7 |
+
const Breadcrumb = React.forwardRef<
|
| 8 |
+
HTMLElement,
|
| 9 |
+
React.ComponentPropsWithoutRef<"nav"> & {
|
| 10 |
+
separator?: React.ReactNode
|
| 11 |
+
}
|
| 12 |
+
>(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />)
|
| 13 |
+
Breadcrumb.displayName = "Breadcrumb"
|
| 14 |
+
|
| 15 |
+
const BreadcrumbList = React.forwardRef<
|
| 16 |
+
HTMLOListElement,
|
| 17 |
+
React.ComponentPropsWithoutRef<"ol">
|
| 18 |
+
>(({ className, ...props }, ref) => (
|
| 19 |
+
<ol
|
| 20 |
+
ref={ref}
|
| 21 |
+
className={cn(
|
| 22 |
+
"flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5",
|
| 23 |
+
className
|
| 24 |
+
)}
|
| 25 |
+
{...props}
|
| 26 |
+
/>
|
| 27 |
+
))
|
| 28 |
+
BreadcrumbList.displayName = "BreadcrumbList"
|
| 29 |
+
|
| 30 |
+
const BreadcrumbItem = React.forwardRef<
|
| 31 |
+
HTMLLIElement,
|
| 32 |
+
React.ComponentPropsWithoutRef<"li">
|
| 33 |
+
>(({ className, ...props }, ref) => (
|
| 34 |
+
<li
|
| 35 |
+
ref={ref}
|
| 36 |
+
className={cn("inline-flex items-center gap-1.5", className)}
|
| 37 |
+
{...props}
|
| 38 |
+
/>
|
| 39 |
+
))
|
| 40 |
+
BreadcrumbItem.displayName = "BreadcrumbItem"
|
| 41 |
+
|
| 42 |
+
const BreadcrumbLink = React.forwardRef<
|
| 43 |
+
HTMLAnchorElement,
|
| 44 |
+
React.ComponentPropsWithoutRef<"a"> & {
|
| 45 |
+
asChild?: boolean
|
| 46 |
+
}
|
| 47 |
+
>(({ asChild, className, ...props }, ref) => {
|
| 48 |
+
const Comp = asChild ? Slot : "a"
|
| 49 |
+
|
| 50 |
+
return (
|
| 51 |
+
<Comp
|
| 52 |
+
ref={ref}
|
| 53 |
+
className={cn("transition-colors hover:text-foreground", className)}
|
| 54 |
+
{...props}
|
| 55 |
+
/>
|
| 56 |
+
)
|
| 57 |
+
})
|
| 58 |
+
BreadcrumbLink.displayName = "BreadcrumbLink"
|
| 59 |
+
|
| 60 |
+
const BreadcrumbPage = React.forwardRef<
|
| 61 |
+
HTMLSpanElement,
|
| 62 |
+
React.ComponentPropsWithoutRef<"span">
|
| 63 |
+
>(({ className, ...props }, ref) => (
|
| 64 |
+
<span
|
| 65 |
+
ref={ref}
|
| 66 |
+
role="link"
|
| 67 |
+
aria-disabled="true"
|
| 68 |
+
aria-current="page"
|
| 69 |
+
className={cn("font-normal text-foreground", className)}
|
| 70 |
+
{...props}
|
| 71 |
+
/>
|
| 72 |
+
))
|
| 73 |
+
BreadcrumbPage.displayName = "BreadcrumbPage"
|
| 74 |
+
|
| 75 |
+
const BreadcrumbSeparator = ({
|
| 76 |
+
children,
|
| 77 |
+
className,
|
| 78 |
+
...props
|
| 79 |
+
}: React.ComponentProps<"li">) => (
|
| 80 |
+
<li
|
| 81 |
+
role="presentation"
|
| 82 |
+
aria-hidden="true"
|
| 83 |
+
className={cn("[&>svg]:w-3.5 [&>svg]:h-3.5", className)}
|
| 84 |
+
{...props}
|
| 85 |
+
>
|
| 86 |
+
{children ?? <ChevronRight />}
|
| 87 |
+
</li>
|
| 88 |
+
)
|
| 89 |
+
BreadcrumbSeparator.displayName = "BreadcrumbSeparator"
|
| 90 |
+
|
| 91 |
+
const BreadcrumbEllipsis = ({
|
| 92 |
+
className,
|
| 93 |
+
...props
|
| 94 |
+
}: React.ComponentProps<"span">) => (
|
| 95 |
+
<span
|
| 96 |
+
role="presentation"
|
| 97 |
+
aria-hidden="true"
|
| 98 |
+
className={cn("flex h-9 w-9 items-center justify-center", className)}
|
| 99 |
+
{...props}
|
| 100 |
+
>
|
| 101 |
+
<MoreHorizontal className="h-4 w-4" />
|
| 102 |
+
<span className="sr-only">More</span>
|
| 103 |
+
</span>
|
| 104 |
+
)
|
| 105 |
+
BreadcrumbEllipsis.displayName = "BreadcrumbElipssis"
|
| 106 |
+
|
| 107 |
+
export {
|
| 108 |
+
Breadcrumb,
|
| 109 |
+
BreadcrumbList,
|
| 110 |
+
BreadcrumbItem,
|
| 111 |
+
BreadcrumbLink,
|
| 112 |
+
BreadcrumbPage,
|
| 113 |
+
BreadcrumbSeparator,
|
| 114 |
+
BreadcrumbEllipsis,
|
| 115 |
+
}
|
artifacts/fb-publisher/src/components/ui/button-group.tsx
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Slot } from "@radix-ui/react-slot"
|
| 2 |
+
import { cva, type VariantProps } from "class-variance-authority"
|
| 3 |
+
|
| 4 |
+
import { cn } from "@/lib/utils"
|
| 5 |
+
import { Separator } from "@/components/ui/separator"
|
| 6 |
+
|
| 7 |
+
const buttonGroupVariants = cva(
|
| 8 |
+
"flex w-fit items-stretch has-[>[data-slot=button-group]]:gap-2 [&>*]:focus-visible:relative [&>*]:focus-visible:z-10 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
|
| 9 |
+
{
|
| 10 |
+
variants: {
|
| 11 |
+
orientation: {
|
| 12 |
+
horizontal:
|
| 13 |
+
"[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none",
|
| 14 |
+
vertical:
|
| 15 |
+
"flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none",
|
| 16 |
+
},
|
| 17 |
+
},
|
| 18 |
+
defaultVariants: {
|
| 19 |
+
orientation: "horizontal",
|
| 20 |
+
},
|
| 21 |
+
}
|
| 22 |
+
)
|
| 23 |
+
|
| 24 |
+
function ButtonGroup({
|
| 25 |
+
className,
|
| 26 |
+
orientation,
|
| 27 |
+
...props
|
| 28 |
+
}: React.ComponentProps<"div"> & VariantProps<typeof buttonGroupVariants>) {
|
| 29 |
+
return (
|
| 30 |
+
<div
|
| 31 |
+
role="group"
|
| 32 |
+
data-slot="button-group"
|
| 33 |
+
data-orientation={orientation}
|
| 34 |
+
className={cn(buttonGroupVariants({ orientation }), className)}
|
| 35 |
+
{...props}
|
| 36 |
+
/>
|
| 37 |
+
)
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
function ButtonGroupText({
|
| 41 |
+
className,
|
| 42 |
+
asChild = false,
|
| 43 |
+
...props
|
| 44 |
+
}: React.ComponentProps<"div"> & {
|
| 45 |
+
asChild?: boolean
|
| 46 |
+
}) {
|
| 47 |
+
const Comp = asChild ? Slot : "div"
|
| 48 |
+
|
| 49 |
+
return (
|
| 50 |
+
<Comp
|
| 51 |
+
className={cn(
|
| 52 |
+
"bg-muted shadow-xs flex items-center gap-2 rounded-md border px-4 text-sm font-medium [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none",
|
| 53 |
+
className
|
| 54 |
+
)}
|
| 55 |
+
{...props}
|
| 56 |
+
/>
|
| 57 |
+
)
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
function ButtonGroupSeparator({
|
| 61 |
+
className,
|
| 62 |
+
orientation = "vertical",
|
| 63 |
+
...props
|
| 64 |
+
}: React.ComponentProps<typeof Separator>) {
|
| 65 |
+
return (
|
| 66 |
+
<Separator
|
| 67 |
+
data-slot="button-group-separator"
|
| 68 |
+
orientation={orientation}
|
| 69 |
+
className={cn(
|
| 70 |
+
"bg-input relative !m-0 self-stretch data-[orientation=vertical]:h-auto",
|
| 71 |
+
className
|
| 72 |
+
)}
|
| 73 |
+
{...props}
|
| 74 |
+
/>
|
| 75 |
+
)
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
export {
|
| 79 |
+
ButtonGroup,
|
| 80 |
+
ButtonGroupSeparator,
|
| 81 |
+
ButtonGroupText,
|
| 82 |
+
buttonGroupVariants,
|
| 83 |
+
}
|
artifacts/fb-publisher/src/components/ui/button.tsx
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import * as React from "react"
|
| 2 |
+
import { Slot } from "@radix-ui/react-slot"
|
| 3 |
+
import { cva, type VariantProps } from "class-variance-authority"
|
| 4 |
+
|
| 5 |
+
import { cn } from "@/lib/utils"
|
| 6 |
+
|
| 7 |
+
const buttonVariants = cva(
|
| 8 |
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0" +
|
| 9 |
+
" hover-elevate active-elevate-2",
|
| 10 |
+
{
|
| 11 |
+
variants: {
|
| 12 |
+
variant: {
|
| 13 |
+
default:
|
| 14 |
+
// @replit: no hover, and add primary border
|
| 15 |
+
"bg-primary text-primary-foreground border border-primary-border",
|
| 16 |
+
destructive:
|
| 17 |
+
"bg-destructive text-destructive-foreground shadow-sm border-destructive-border",
|
| 18 |
+
outline:
|
| 19 |
+
// @replit Shows the background color of whatever card / sidebar / accent background it is inside of.
|
| 20 |
+
// Inherits the current text color. Uses shadow-xs. no shadow on active
|
| 21 |
+
// No hover state
|
| 22 |
+
" border [border-color:var(--button-outline)] shadow-xs active:shadow-none ",
|
| 23 |
+
secondary:
|
| 24 |
+
// @replit border, no hover, no shadow, secondary border.
|
| 25 |
+
"border bg-secondary text-secondary-foreground border border-secondary-border ",
|
| 26 |
+
// @replit no hover, transparent border
|
| 27 |
+
ghost: "border border-transparent",
|
| 28 |
+
link: "text-primary underline-offset-4 hover:underline",
|
| 29 |
+
},
|
| 30 |
+
size: {
|
| 31 |
+
// @replit changed sizes
|
| 32 |
+
default: "min-h-9 px-4 py-2",
|
| 33 |
+
sm: "min-h-8 rounded-md px-3 text-xs",
|
| 34 |
+
lg: "min-h-10 rounded-md px-8",
|
| 35 |
+
icon: "h-9 w-9",
|
| 36 |
+
},
|
| 37 |
+
},
|
| 38 |
+
defaultVariants: {
|
| 39 |
+
variant: "default",
|
| 40 |
+
size: "default",
|
| 41 |
+
},
|
| 42 |
+
}
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
export interface ButtonProps
|
| 46 |
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
| 47 |
+
VariantProps<typeof buttonVariants> {
|
| 48 |
+
asChild?: boolean
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
| 52 |
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
| 53 |
+
const Comp = asChild ? Slot : "button"
|
| 54 |
+
return (
|
| 55 |
+
<Comp
|
| 56 |
+
className={cn(buttonVariants({ variant, size, className }))}
|
| 57 |
+
ref={ref}
|
| 58 |
+
{...props}
|
| 59 |
+
/>
|
| 60 |
+
)
|
| 61 |
+
}
|
| 62 |
+
)
|
| 63 |
+
Button.displayName = "Button"
|
| 64 |
+
|
| 65 |
+
export { Button, buttonVariants }
|