sare26 commited on
Commit
3952ae4
·
verified ·
1 Parent(s): 8b37604

Upload 181 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. artifacts/fb-publisher/.replit-artifact/artifact.toml +23 -0
  2. artifacts/fb-publisher/components.json +20 -0
  3. artifacts/fb-publisher/dist/public/assets/index-CBpzG2YA.css +0 -0
  4. artifacts/fb-publisher/dist/public/assets/index-oEk3pbMd.js +0 -0
  5. artifacts/fb-publisher/dist/public/extension/background.js +1 -0
  6. artifacts/fb-publisher/dist/public/extension/bridge.js +1 -0
  7. artifacts/fb-publisher/dist/public/extension/fb-publisher-bridge.zip +3 -0
  8. artifacts/fb-publisher/dist/public/extension/icon-128.png +0 -0
  9. artifacts/fb-publisher/dist/public/extension/manifest.json +43 -0
  10. artifacts/fb-publisher/dist/public/extension/popup.html +162 -0
  11. artifacts/fb-publisher/dist/public/extension/popup.js +82 -0
  12. artifacts/fb-publisher/dist/public/extension/src/.gitignore +2 -0
  13. artifacts/fb-publisher/dist/public/extension/src/background.source.js +678 -0
  14. artifacts/fb-publisher/dist/public/extension/src/bridge.source.js +74 -0
  15. artifacts/fb-publisher/dist/public/favicon.png +0 -0
  16. artifacts/fb-publisher/dist/public/favicon.svg +3 -0
  17. artifacts/fb-publisher/dist/public/index.html +18 -0
  18. artifacts/fb-publisher/dist/public/logo.png +0 -0
  19. artifacts/fb-publisher/dist/public/logo.svg +10 -0
  20. artifacts/fb-publisher/dist/public/opengraph.jpg +0 -0
  21. artifacts/fb-publisher/dist/public/robots.txt +2 -0
  22. artifacts/fb-publisher/index.html +17 -0
  23. artifacts/fb-publisher/package.json +83 -0
  24. artifacts/fb-publisher/public/extension/background.js +1 -0
  25. artifacts/fb-publisher/public/extension/bridge.js +1 -0
  26. artifacts/fb-publisher/public/extension/fb-publisher-bridge.zip +3 -0
  27. artifacts/fb-publisher/public/extension/icon-128.png +0 -0
  28. artifacts/fb-publisher/public/extension/manifest.json +43 -0
  29. artifacts/fb-publisher/public/extension/popup.html +162 -0
  30. artifacts/fb-publisher/public/extension/popup.js +82 -0
  31. artifacts/fb-publisher/public/extension/src/.gitignore +2 -0
  32. artifacts/fb-publisher/public/extension/src/background.source.js +678 -0
  33. artifacts/fb-publisher/public/extension/src/bridge.source.js +118 -0
  34. artifacts/fb-publisher/public/favicon.png +0 -0
  35. artifacts/fb-publisher/public/favicon.svg +3 -0
  36. artifacts/fb-publisher/public/logo.png +0 -0
  37. artifacts/fb-publisher/public/logo.svg +10 -0
  38. artifacts/fb-publisher/public/opengraph.jpg +0 -0
  39. artifacts/fb-publisher/public/robots.txt +2 -0
  40. artifacts/fb-publisher/src/App.tsx +281 -0
  41. artifacts/fb-publisher/src/components/UpdateBanner.tsx +125 -0
  42. artifacts/fb-publisher/src/components/ui/accordion.tsx +55 -0
  43. artifacts/fb-publisher/src/components/ui/alert-dialog.tsx +139 -0
  44. artifacts/fb-publisher/src/components/ui/alert.tsx +59 -0
  45. artifacts/fb-publisher/src/components/ui/aspect-ratio.tsx +5 -0
  46. artifacts/fb-publisher/src/components/ui/avatar.tsx +50 -0
  47. artifacts/fb-publisher/src/components/ui/badge.tsx +43 -0
  48. artifacts/fb-publisher/src/components/ui/breadcrumb.tsx +115 -0
  49. artifacts/fb-publisher/src/components/ui/button-group.tsx +83 -0
  50. 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 }