From b3e0be053352bf7a576956b8b02fb607f035f60b Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Mon, 29 Jul 2024 22:02:08 +0700 Subject: [PATCH 01/43] douyin - WIP --- popup/tabs.js | 1 + scripts/@index.js | 1 + scripts/douyin_batchDownload.js | 60 +++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 scripts/douyin_batchDownload.js diff --git a/popup/tabs.js b/popup/tabs.js index 7fe04b35..11ad2c2e 100644 --- a/popup/tabs.js +++ b/popup/tabs.js @@ -201,6 +201,7 @@ const tabs = [ s.tiktok_downloadVideo, s.tiktok_batchDownload, createTitle("--- Douyin ---", "--- Douyin ---"), + s.douyin_batchDownload, s.douyin_downloadWachingVideo, s.douyin_downloadAllVideoUser, ], diff --git a/scripts/@index.js b/scripts/@index.js index 36e07c54..1ed2f8cb 100644 --- a/scripts/@index.js +++ b/scripts/@index.js @@ -171,3 +171,4 @@ export { default as youtube_changeCountry } from "./youtube_changeCountry.js"; export { default as fb_autoLike } from "./fb_autoLike.js"; export { default as guland_VIP } from "./guland_VIP.js"; export { default as pip_anything } from "./pip_anything.js"; +export { default as douyin_batchDownload } from "./douyin_batchDownload.js"; diff --git a/scripts/douyin_batchDownload.js b/scripts/douyin_batchDownload.js new file mode 100644 index 00000000..3c611c9a --- /dev/null +++ b/scripts/douyin_batchDownload.js @@ -0,0 +1,60 @@ +import { hookFetch, hookXHR } from "./libs/ajax-hook/index.js"; + +export default { + icon: "", + name: { + en: "Douyin - Batch download", + vi: "Douyin - Tải hàng loạt", + }, + description: { + en: "", + vi: "", + img: "", + }, + + infoLink: "", + + changeLogs: { + date: "description", + }, + + whiteList: ["https://www.douyin.com/*"], + + pageScript: { + onDocumentStart: (details) => { + const CACHED = { + list: [], + byAwemeId: new Map(), + }; + + window.ufs_douyin_batchDownload = CACHED; + + hookXHR({ + onAfterSend: async ( + { method, url, async, user, password }, + dataSend, + response + ) => { + console.log(method, url, dataSend, response); + + if (url.includes("aweme/") && url.includes("/post")) { + const res = response.clone(); + const json = await res.json(); + console.log(json); + + if (json?.aweme_list?.length) { + CACHED.list.push(...json.aweme_list); + json.aweme_list.forEach((item) => { + if (!CACHED.byAwemeId.has(item.aweme_id)) { + CACHED.byAwemeId.set(item.aweme_id, item); + } + }); + } + } + }, + }); + }, + + onClick: () => {}, + }, +}; From d53509bca0a97ca148c94bb803b160bd14f95390 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Tue, 30 Jul 2024 14:28:58 +0700 Subject: [PATCH 02/43] minor fix --- scripts/douyin_batchDownload.js | 10 +++++----- scripts/showHiddenFields.js | 7 +++++-- scripts/tiktok_batchDownload.js | 3 --- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts/douyin_batchDownload.js b/scripts/douyin_batchDownload.js index 3c611c9a..aa23e310 100644 --- a/scripts/douyin_batchDownload.js +++ b/scripts/douyin_batchDownload.js @@ -35,11 +35,11 @@ export default { dataSend, response ) => { - console.log(method, url, dataSend, response); - - if (url.includes("aweme/") && url.includes("/post")) { - const res = response.clone(); - const json = await res.json(); + if ( + url.includes("aweme/") && + (url.includes("/post") || url.includes("/tab/feed")) + ) { + const json = JSON.parse(response); console.log(json); if (json?.aweme_list?.length) { diff --git a/scripts/showHiddenFields.js b/scripts/showHiddenFields.js index d664f61f..6da4a409 100644 --- a/scripts/showHiddenFields.js +++ b/scripts/showHiddenFields.js @@ -20,11 +20,12 @@ export default { div, label, ne, - found = false; + found = false, + D = document, + count = 0; for (i = 0; (f = document.forms[i]); ++i) for (j = 0; (e = f[j]); ++j) if (e.type == "hidden") { - D = document; function C(t) { return D.createElement(t); } @@ -45,9 +46,11 @@ export default { --j; /*for moz*/ found = true; + count++; } if (!found) alert("Nothing is hidden! / Không có thành phần nào bị ẩn!"); + alert("Showed " + count + " hidden fields."); }, }, }; diff --git a/scripts/tiktok_batchDownload.js b/scripts/tiktok_batchDownload.js index bfb6dcdb..282226cd 100644 --- a/scripts/tiktok_batchDownload.js +++ b/scripts/tiktok_batchDownload.js @@ -422,9 +422,6 @@ export default { format(v) { return formatter.format(v); }, - onClickContainer(e) { - if (e.target === this.$el) this.showModal = false; - }, }, }).$mount(div); From 392c546dc60f85c21c36ceccb2f2bde414b5cec1 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Tue, 30 Jul 2024 17:52:20 +0700 Subject: [PATCH 03/43] transfer repo --- popup/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/popup/index.js b/popup/index.js index d02ace66..2eca57c0 100644 --- a/popup/index.js +++ b/popup/index.js @@ -452,6 +452,7 @@ function showError(e) { function checkIsPreview(script) { if ( + location.hostname === "useful-scripts-extension.github.io" || location.hostname === "hoangtran0410.github.io" || location.hostname === "127.0.0.1" ) { From 44ba21564fe7cdd3af579783be3442f0a318cba8 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 31 Jul 2024 00:37:34 +0700 Subject: [PATCH 04/43] douyin batch download - WIP --- scripts/douyin_batchDownload.js | 417 +++++++++++++++++++++++++++++-- scripts/tiktok_batchDownload.css | 1 + 2 files changed, 402 insertions(+), 16 deletions(-) diff --git a/scripts/douyin_batchDownload.js b/scripts/douyin_batchDownload.js index aa23e310..a7a4470c 100644 --- a/scripts/douyin_batchDownload.js +++ b/scripts/douyin_batchDownload.js @@ -1,7 +1,10 @@ -import { hookFetch, hookXHR } from "./libs/ajax-hook/index.js"; +import { UfsGlobal } from "./content-scripts/ufs_global.js"; +import { BADGES } from "./helpers/badge.js"; +import { hookXHR } from "./libs/ajax-hook/index.js"; +import { scrollToVeryEnd } from "./scrollToVeryEnd.js"; export default { - icon: "", + icon: "https://www.douyin.com/favicon.ico", name: { en: "Douyin - Batch download", vi: "Douyin - Tải hàng loạt", @@ -12,19 +15,20 @@ export default { img: "", }, - infoLink: "", + badges: [BADGES.new, BADGES.hot], changeLogs: { - date: "description", + "2024-07-30": "init", }, whiteList: ["https://www.douyin.com/*"], pageScript: { - onDocumentStart: (details) => { + onDocumentStart: async (details) => { const CACHED = { list: [], - byAwemeId: new Map(), + hasNew: true, + videoById: new Map(), }; window.ufs_douyin_batchDownload = CACHED; @@ -35,26 +39,407 @@ export default { dataSend, response ) => { - if ( - url.includes("aweme/") && - (url.includes("/post") || url.includes("/tab/feed")) - ) { - const json = JSON.parse(response); - console.log(json); + try { + const json = + typeof response == "string" ? JSON.parse(response) : response; if (json?.aweme_list?.length) { + console.log(json); + CACHED.list.push(...json.aweme_list); json.aweme_list.forEach((item) => { - if (!CACHED.byAwemeId.has(item.aweme_id)) { - CACHED.byAwemeId.set(item.aweme_id, item); + if (!CACHED.videoById.has(item.aweme_id)) { + CACHED.videoById.set(item.aweme_id, item); + CACHED.hasNew = true; } }); } + + if (json?.cards?.length) { + const list = json.cards.map((c) => JSON.parse(c.aweme)); + list.forEach((item) => { + if (!CACHED.videoById.has(item.aweme_id)) { + CACHED.videoById.set(item.aweme_id, item); + CACHED.hasNew = true; + } + }); + } + } catch (e) { + console.log("ERROR:", e); } }, }); - }, - onClick: () => {}, + UfsGlobal.Extension.getURL("/scripts/tiktok_batchDownload.css").then( + UfsGlobal.DOM.injectCssFile + ); + await UfsGlobal.DOM.injectScriptSrcAsync( + await UfsGlobal.Extension.getURL("/scripts/libs/vue/index.js") + ); + + const div = document.createElement("div"); + document.documentElement.appendChild(div); + + const formatter = UfsGlobal.Utils.getNumberFormatter("compactShort"); + function getNow() { + // return year + month + day + hour + minute + second + const day = new Date(); + return ( + [day.getFullYear(), day.getMonth() + 1, day.getDate()].join("-") + + "_" + + [day.getHours(), day.getMinutes(), day.getSeconds()].join("-") + ); + } + + const app = new Vue({ + template: /*html*/ ` +
+
📥 {{totalCount}}
+
+
+

Douyin - Useful Scripts

+

Found {{totalCount}} videos

+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
#🎬 VideoTitle👤 UserLikeLengthDownload

No video

{{v.index}}
+ +
+ + + +

{{v.desc}}

+ + {{v.author.nickname}}
+
+ {{v.author.uid}} +
{{format(v.statistics.digg_count)}}{{formatTime(v.video.duration)}}s +

+ 🎬 Video
+ 🖼️ Cover
+ + 👤 Avatar +
+ + 🎧 Music: {{v.music.title}} + +

+
+ + +
+
+
+
`, + created() { + setInterval(() => { + if (CACHED.hasNew) { + this.videos = Array.from(CACHED.videoById.values()) + // inject index + .map((v, i) => ({ ...v, index: i + 1 })); + + CACHED.hasNew = false; + } + }, 1000); + }, + data() { + return { + showModal: false, + videos: [], + search: "", + sortBy: "index", + sortDir: "asc", + downloading: {}, + selected: {}, + }; + }, + computed: { + selectedIds() { + return Object.entries(this.selected) + .filter((v) => v[1]) + .map((v) => v[0]); + }, + selectedCount() { + return Object.values(this.selected).filter((v) => v).length; + }, + hasSelected() { + return this.selectedCount > 0; + }, + videoToDownload() { + return this.hasSelected + ? this.videosToShow.filter((v) => this.selected[v.aweme_id]) + : this.videosToShow; + }, + audioToDownload() { + const list = this.hasSelected + ? this.videosToShow.filter((v) => this.selected[v.aweme_id]) + : this.videosToShow; + + // get unique + const result = new Map(); + for (const item of list) { + if (item.music?.id && !result.has(item.music?.id)) + result.set(item.music.id, item); + } + return Array.from(result.values()); + }, + videoTitle() { + if (this.downloading.video) { + return ( + "Downloading " + + this.downloading.video + + "/" + + this.videoToDownload.length + + " video" + ); + } + return ( + "Download " + + this.videoToDownload.length + + (this.hasSelected ? " selected" : "") + + " video" + ); + }, + audioTitle() { + if (this.downloading.audio) { + return ( + "Downloading " + + this.downloading.audio + + "/" + + this.audioToDownload.length + + " audio" + ); + } + return ( + "Download " + + this.audioToDownload.length + + (this.hasSelected ? " selected" : "") + + " audio" + ); + }, + totalCount() { + return this.videos.length; + }, + videosToShow() { + return ( + this.videos + // filter by search + .filter((v) => { + return [ + v.desc, + v.author?.uid, + v.author?.nickname, + v.author?.sec_uid, + ].some((s) => + s?.toLowerCase()?.includes(this.search.toLowerCase()) + ); + }) + // sorting + .sort((a, b) => { + switch (this.sortBy) { + case "index": + return this.sortDir === "asc" + ? a.index - b.index + : b.index - a.index; + case "title": + return this.sortDir === "asc" + ? a.desc.localeCompare(b.desc) + : b.desc.localeCompare(a.desc); + case "author": + return this.sortDir === "asc" + ? a.author.nickname.localeCompare(b.author.nickname) + : b.author.nickname.localeCompare(a.author.nickname); + case "like": + return this.sortDir === "asc" + ? a.statistics.digg_count - b.statistics.digg_count + : b.statistics.digg_count - a.statistics.digg_count; + case "duration": + return this.sortDir === "asc" + ? a.video.duration - b.video.duration + : b.video.duration - a.video.duration; + } + }) + ); + }, + }, + methods: { + async downloadVideo() { + const total = this.videoToDownload.length; + if (!total) return; + let success = 0; + await download({ + folderName: "douyin_videos_" + getNow(), + expectBlobTypes: ["video/mp4"], + data: this.videoToDownload + .map((_, i) => { + return { + url: _.video.play_addr.url_list.at(-1), + filename: + i + + 1 + + "_" + + UfsGlobal.Utils.sanitizeName(_.aweme_id, false) + + ".mp4", + }; + }) + .flat() + .filter((_) => _.url), + onProgressItem: (i, total) => { + this.downloading.video = i; + }, + onFinishItem: (i, total) => { + success++; + }, + }); + this.downloading.video = false; + alert("Downloaded " + success + "/" + total + " videos!"); + }, + async downloadAudio() { + const total = this.audioToDownload.length; + if (!total) return; + let success = 0; + await download({ + folderName: "douyin_musics_" + getNow(), + data: this.audioToDownload.map((_, i) => ({ + url: _.music.playUrl, + filename: + i + + 1 + + "_" + + UfsGlobal.Utils.sanitizeName( + _.music.title.substr(0, 50) || "audio", + false + ) + + ".mp3", + })), + onProgressItem: (i, total) => { + this.downloading.audio = i; + }, + onFinishItem: (i, total) => { + success++; + }, + }); + this.downloading.audio = false; + alert("Downloaded " + success + "/" + total + " videos!"); + }, + downloadJson() { + UfsGlobal.Utils.downloadData( + JSON.stringify(this.videosToShow, null, 4), + this.videosToShow.length + "_videos_douyin.json" + ); + }, + scrollToVeryEnd() { + setTimeout(() => scrollToVeryEnd(false), 100); + }, + scrollToTop(e) { + e.target.parentElement.scrollTo({ top: 0, behavior: "smooth" }); + }, + clearSelected() { + this.selectedIds.forEach((vidId) => { + CACHED.videoById.delete(vidId); + }); + this.selected = {}; + }, + clear() { + if (confirm("Are you sure want to clear all?")) { + CACHED.videoById.clear(); + this.videos = []; + } + }, + setSortBy(key) { + this.sortBy = key; + if (key === this.sortBy) + this.sortDir = this.sortDir === "asc" ? "desc" : "asc"; + }, + openUser(id) { + window.open("https://www.douyin.com/user/" + id, "_blank"); + }, + format(v) { + return formatter.format(v); + }, + formatTime(seconds) { + // return hh:mm:ss + return new Date(seconds) + .toISOString() + .substr(11, 8) + .split(":") + .filter((v) => v !== "00") + .join(":"); + }, + }, + }).$mount(div); + + async function download({ + folderName = "douyin", + expectBlobTypes, + data, + onProgressItem, + onFinishItem, + }) { + const dir = await UfsGlobal.Utils.chooseFolderToDownload(folderName); + onProgressItem?.(0, data.length); + + UfsGlobal.Extension.trackEvent("douyin_batchDownload-download"); + for (let i = 0; i < data.length; ++i) { + try { + onProgressItem?.(i + 1, data.length); + const { url, filename } = data[i]; + const realUrl = await UfsGlobal.Utils.getRedirectedUrl(url); + await UfsGlobal.Utils.downloadToFolder({ + url: realUrl, + fileName: filename, + dirHandler: dir, + expectBlobTypes, + }); + onFinishItem?.(i + 1, data.length); + } catch (e) { + console.error(e); + } + } + } + }, }, }; diff --git a/scripts/tiktok_batchDownload.css b/scripts/tiktok_batchDownload.css index b9575d00..45779529 100644 --- a/scripts/tiktok_batchDownload.css +++ b/scripts/tiktok_batchDownload.css @@ -70,6 +70,7 @@ .ufs_popup td { border: 1px solid #aaa; border-collapse: collapse; + vertical-align: middle; } .ufs_popup table td { From 7d49b0ee5f98e9699067d96d7af4f627ae4abe95 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 31 Jul 2024 16:50:50 +0700 Subject: [PATCH 05/43] hotfix --- scripts/content-scripts/ufs_global.js | 8 ++++++++ scripts/dino_hack.js | 7 +++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/scripts/content-scripts/ufs_global.js b/scripts/content-scripts/ufs_global.js index 07b65868..ab7ac7a5 100644 --- a/scripts/content-scripts/ufs_global.js +++ b/scripts/content-scripts/ufs_global.js @@ -12,6 +12,8 @@ export const UfsGlobal = { waitForTabToLoad, }, DOM: { + keyDown, + keyUp, closest, notifyStack, notify, @@ -139,6 +141,12 @@ function download(options) { // #endregion // #region DOM + +//prettier-ignore +function keyDown(e){let n=document.createEvent("KeyboardEvent");Object.defineProperty(n,"keyCode",{get:function(){return this.keyCodeVal}}),n.initKeyboardEvent?n.initKeyboardEvent("keydown",!0,!0,document.defaultView,e,e,"","",!1,""):n.initKeyEvent("keydown",!0,!0,document.defaultView,!1,!1,!1,!1,e,0),n.keyCodeVal=e,document.body.dispatchEvent(n)} +//prettier-ignore +function keyUp(e){let n=document.createEvent("KeyboardEvent");Object.defineProperty(n,"keyCode",{get:function(){return this.keyCodeVal}}),n.initKeyboardEvent?n.initKeyboardEvent("keyup",!0,!0,document.defaultView,e,e,"","",!1,""):n.initKeyEvent("keyup",!0,!0,document.defaultView,!1,!1,!1,!1,e,0),n.keyCodeVal=e,document.body.dispatchEvent(n)} + function closest(element, selector) { let el = element; while (el !== null) { diff --git a/scripts/dino_hack.js b/scripts/dino_hack.js index 21787822..97359d83 100644 --- a/scripts/dino_hack.js +++ b/scripts/dino_hack.js @@ -8,6 +8,9 @@ export default { en: "A bot that plays the Google Chrome T-Rex game for you", vi: "Tự động chơi game Google Chrome T-Rex", }, + changeLogs: { + "2024-07-31": "hotfix", + }, pageScript: { onClick: function () { @@ -15,9 +18,9 @@ export default { function hack() { //prettier-ignore - function keyDown(e){Podium={};var n=document.createEvent("KeyboardEvent");Object.defineProperty(n,"keyCode",{get:function(){return this.keyCodeVal}}),n.initKeyboardEvent?n.initKeyboardEvent("keydown",!0,!0,document.defaultView,e,e,"","",!1,""):n.initKeyEvent("keydown",!0,!0,document.defaultView,!1,!1,!1,!1,e,0),n.keyCodeVal=e,document.body.dispatchEvent(n)} + function keyDown(e){var n=document.createEvent("KeyboardEvent");Object.defineProperty(n,"keyCode",{get:function(){return this.keyCodeVal}}),n.initKeyboardEvent?n.initKeyboardEvent("keydown",!0,!0,document.defaultView,e,e,"","",!1,""):n.initKeyEvent("keydown",!0,!0,document.defaultView,!1,!1,!1,!1,e,0),n.keyCodeVal=e,document.body.dispatchEvent(n)} //prettier-ignore - function keyUp(e){Podium={};var n=document.createEvent("KeyboardEvent");Object.defineProperty(n,"keyCode",{get:function(){return this.keyCodeVal}}),n.initKeyboardEvent?n.initKeyboardEvent("keyup",!0,!0,document.defaultView,e,e,"","",!1,""):n.initKeyEvent("keyup",!0,!0,document.defaultView,!1,!1,!1,!1,e,0),n.keyCodeVal=e,document.body.dispatchEvent(n)} + function keyUp(e){var n=document.createEvent("KeyboardEvent");Object.defineProperty(n,"keyCode",{get:function(){return this.keyCodeVal}}),n.initKeyboardEvent?n.initKeyboardEvent("keyup",!0,!0,document.defaultView,e,e,"","",!1,""):n.initKeyEvent("keyup",!0,!0,document.defaultView,!1,!1,!1,!1,e,0),n.keyCodeVal=e,document.body.dispatchEvent(n)} let timeoutId = setInterval(function () { Runner.instance_.horizon.obstacles.length > 0 && From 6cdc208f615e0d369e0abac5a3d8135bbdace5f4 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Thu, 1 Aug 2024 14:31:26 +0700 Subject: [PATCH 06/43] update --- scripts/ufs_statistic.css | 7 ++- scripts/ufs_statistic.js | 114 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 116 insertions(+), 5 deletions(-) diff --git a/scripts/ufs_statistic.css b/scripts/ufs_statistic.css index 281ad85f..623ae16b 100644 --- a/scripts/ufs_statistic.css +++ b/scripts/ufs_statistic.css @@ -20,6 +20,11 @@ li:hover { li a { display: inline-block; + color: yellow; +} + +li a:visited { + color: green; } li a img { @@ -31,7 +36,7 @@ li a img { } li:hover img { - transform: scale(2); + transform: scale(3.5); z-index: 2; } diff --git a/scripts/ufs_statistic.js b/scripts/ufs_statistic.js index 376594ba..6e80680a 100644 --- a/scripts/ufs_statistic.js +++ b/scripts/ufs_statistic.js @@ -1,4 +1,5 @@ import { UfsGlobal } from "./content-scripts/ufs_global.js"; +import { getUserAvatarFromUid, getUserInfoFromUid } from "./fb_GLOBAL.js"; export default { icon: "/assets/icon32.png", @@ -65,8 +66,10 @@ async function onDocumentEnd() { li.innerHTML = data.log + ` - - - fb + + fb `; } else { li.textContent = data.log; @@ -76,6 +79,34 @@ async function onDocumentEnd() { }); console.log(all_li); + // load fb profiles + const allUid = allLogs + .filter((log) => isFbUid(log?.uid)) + .map((log) => log.uid); + + const uniqueUid = [...new Set(allUid)].filter(Boolean); + + if (uniqueUid.length) { + initCache().then(() => { + const promises = uniqueUid.map( + (uid) => () => + getFbProfile(uid).then((info) => { + document + .querySelectorAll(`[data-profile-name="${uid}"]`) + .forEach((el) => { + el.textContent = info.name; + }); + document + .querySelectorAll(`[data-profile-avatar="${uid}"]`) + .forEach((el) => { + el.src = info.avatar.replace(/\\\//g, "/") || el.src; + }); + }) + ); + UfsGlobal.Utils.promiseAllStepN(5, promises); + }); + } + const traceUidCheckmark = document.createElement("input"); traceUidCheckmark.id = "trace-uid"; traceUidCheckmark.type = "checkbox"; @@ -489,8 +520,83 @@ function isFbUid(uid) { (uid?.startsWith("100") || (uid?.length && uid?.length != 13)) ); } -function fbAvatarFromUid(uid) { - return `https://graph.facebook.com/${uid}/picture?height=50&access_token=6628568379%7Cc1e620fa708a1d5696fb991c1bde5662`; + +const CACHED = { + fbProfile: null, + fb_dtsg: null, +}; + +async function initCache() { + if (!CACHED.fbProfile) { + CACHED.fbProfile = new Map(); + try { + const c = localStorage.getItem("fbProfile"); + if (c) { + const arr = JSON.parse(c); + console.log(arr); + arr.forEach((info) => CACHED.fbProfile.set(info.uid, info)); + } + } catch (e) { + console.error(e); + } + } + + if (!CACHED.fb_dtsg) { + let res = await UfsGlobal.Extension.runInBackground("fetch", [ + "https://mbasic.facebook.com/photos/upload/", + ]); + CACHED.fb_dtsg = RegExp(/name="fb_dtsg" value="(.*?)"/).exec(res.body)?.[1]; + } +} + +async function getFbProfile(uid) { + if (CACHED.fbProfile.has(uid)) return CACHED.fbProfile.get(uid); + + const variables = { + userID: uid, + shouldDeferProfilePic: false, + useVNextHeader: false, + scale: 1.5, + }; + let f = new URLSearchParams(); + f.append("fb_dtsg", CACHED.fb_dtsg); + f.append("fb_api_req_friendly_name", "ProfileCometHeaderQuery"); + f.append("variables", JSON.stringify(variables)); + f.append("doc_id", "4159355184147969"); + + let res = await UfsGlobal.Extension.runInBackground("fetch", [ + "https://www.facebook.com/api/graphql/", + { + method: "POST", + headers: { "content-type": "application/x-www-form-urlencoded" }, + body: f.toString(), + }, + ]); + + let text = await res.body; + const info = { + uid: uid, + name: UfsGlobal.DEBUG.decodeEscapedUnicodeString( + /"name":"(.*?)"/.exec(text)?.[1] + ), + avatar: UfsGlobal.DEBUG.decodeEscapedUnicodeString( + /"profilePicLarge":{"uri":"(.*?)"/.exec(text)?.[1] || + /"profilePicMedium":{"uri":"(.*?)"/.exec(text)?.[1] || + /"profilePicSmall":{"uri":"(.*?)"/.exec(text)?.[1] || + /"profilePic160":{"uri":"(.*?)"/.exec(text)?.[1] + ), + gender: /"gender":"(.*?)"/.exec(text)?.[1], + alternateName: UfsGlobal.DEBUG.decodeEscapedUnicodeString( + /"alternate_name":"(.*?)"/.exec(text)?.[1] + ), + }; + CACHED.fbProfile.set(uid, info); + localStorage.setItem( + "fbProfile", + JSON.stringify(Array.from(CACHED.fbProfile.values())) + ); + console.log(info); + return info; } // log example: 5/31/2024, 9:13:41 AM: OPEN-TAB-unlock (1.67-1717121281787) -> 43 From 46032187c4fa3f989e88fd347d344a5c46c12726 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Thu, 1 Aug 2024 14:32:03 +0700 Subject: [PATCH 07/43] refactor --- scripts/ufs_statistic.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ufs_statistic.js b/scripts/ufs_statistic.js index 6e80680a..e0a9c589 100644 --- a/scripts/ufs_statistic.js +++ b/scripts/ufs_statistic.js @@ -1,5 +1,5 @@ import { UfsGlobal } from "./content-scripts/ufs_global.js"; -import { getUserAvatarFromUid, getUserInfoFromUid } from "./fb_GLOBAL.js"; +import { getUserAvatarFromUid } from "./fb_GLOBAL.js"; export default { icon: "/assets/icon32.png", From 3423446c6b33c6416a24d0f7ec5c44f8902b901b Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Thu, 1 Aug 2024 23:02:34 +0700 Subject: [PATCH 08/43] fix --- scripts/tiktok_batchDownload.js | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/tiktok_batchDownload.js b/scripts/tiktok_batchDownload.js index 282226cd..a31c3909 100644 --- a/scripts/tiktok_batchDownload.js +++ b/scripts/tiktok_batchDownload.js @@ -403,6 +403,7 @@ export default { this.selectedIds.forEach((vidId) => { CACHED.videoById.delete(vidId); }); + CACHED.hasNew = true; this.selected = {}; }, clear() { From 6c1efd18896278d55df7a37967b027ebce2f3942 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Fri, 2 Aug 2024 12:09:08 +0700 Subject: [PATCH 09/43] track new fb users --- scripts/ufs_statistic.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/scripts/ufs_statistic.js b/scripts/ufs_statistic.js index e0a9c589..1c6a5dce 100644 --- a/scripts/ufs_statistic.js +++ b/scripts/ufs_statistic.js @@ -103,7 +103,17 @@ async function onDocumentEnd() { }); }) ); - UfsGlobal.Utils.promiseAllStepN(5, promises); + UfsGlobal.Utils.promiseAllStepN(5, promises).then(() => { + if (CACHED.newFbUsers.size) { + const arr = Array.from(CACHED.newFbUsers.values()); + console.log("New users", arr); + alert( + CACHED.newFbUsers.size + + " new users:\n\n" + + arr.map((_) => _.name).join("\n") + ); + } + }); }); } @@ -524,6 +534,7 @@ function isFbUid(uid) { const CACHED = { fbProfile: null, fb_dtsg: null, + newFbUsers: new Map(), }; async function initCache() { @@ -590,6 +601,7 @@ async function getFbProfile(uid) { /"alternate_name":"(.*?)"/.exec(text)?.[1] ), }; + CACHED.newFbUsers.set(uid, info); CACHED.fbProfile.set(uid, info); localStorage.setItem( "fbProfile", From ed080c5dd9d045195e6a59f6538c8ac608ad17be Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Sat, 3 Aug 2024 23:28:38 +0700 Subject: [PATCH 10/43] test --- scripts/backup/ext/fb-ext/assets/icon32.png | Bin 0 -> 1918 bytes scripts/backup/ext/fb-ext/manifest.json | 12 ++ scripts/backup/ext/fb-ext/popup/index.html | 39 +++++ scripts/backup/ext/fb-ext/popup/main.js | 157 ++++++++++++++++++++ scripts/backup/ext/fb-ext/popup/style.css | 134 +++++++++++++++++ 5 files changed, 342 insertions(+) create mode 100644 scripts/backup/ext/fb-ext/assets/icon32.png create mode 100644 scripts/backup/ext/fb-ext/manifest.json create mode 100644 scripts/backup/ext/fb-ext/popup/index.html create mode 100644 scripts/backup/ext/fb-ext/popup/main.js create mode 100644 scripts/backup/ext/fb-ext/popup/style.css diff --git a/scripts/backup/ext/fb-ext/assets/icon32.png b/scripts/backup/ext/fb-ext/assets/icon32.png new file mode 100644 index 0000000000000000000000000000000000000000..29aff1d5151552e20854aff47dd14b0a0d97776e GIT binary patch literal 1918 zcmZ`)X*3&X8+|jDTB@bCmI^6KN;I*Simmp+5Hd}R+H2oFp$V;>YVDIusjV$yY%>jG z_Z6jRN2SER*VvZ|#!i^@=lA0~?|I&P&hwmm&OPTo=RVJ@txWiM#CQMzd}gMG7#2!> zFD_2j9}*tE!vc0seM@}+s?vE+TsXcD-Apl-0EAx$AUYm^BUUSV8GukE0ISXbXk`N+ z8kE;$qsZbAy0S~)@9|!yrM9`8g2|L? z!oh7*@UKNemmnHR-2Q5b>wix#TmiXBu5Ic*G~p zp#>s*SK44zIwe>aSg3UAs;yUv!YRLk&HRsq+}^tE;8O@pMiI|6>WNb|_~@hbA(gyv zYrCK0{I zBqG-FTB6)A@%7-Xn5HffZ4JEOnwUM6U_z8fNcUuYHQkY;lpGH?JP*K*v^K!jxTtHFCzdWV1P7@gnfSfDLgz;SznJ zuL+zN3@*%9hQ5?w8<9LO#TT(3lXFDvzMV*1)5x_*y36n^81i8jW>iZDJ_&AnvOqe7 z_^D60mY0(}k96AWrhQGZp&0DY+Zo2m^769WEzyySEql#}5FsP89$XcDX)A5EUqr*a z;~?k{?jQ7GGs3AynJs#Kee&@umNF>ga=@XtQ4%+i?pmdNWDQU#lx{|oK#L5 zc>e8^*r9lq;$nI8!oE~Dha{BwP+9RH-pQq(HzsT5K)P-}eY%BZBIP2>@-fNzT&He49W-^^`2J`ix1>x~K zf+ESC?Uy4k1Es{0V70xvg`$$oj6(StM{9PNi~>TXM-rl1=j6o^O#nf`#vkX9Nq=q(4WaZ+mEnYw7=enmu1LMagTQ{fT z8fq)A6_?*PH9fol=Ewci_2F2E6bWThgnB3<yD9p6lyBPfXoYkfY+YuK`lI^a&jHwS%5 zBg3(>L-mlHNoIqqN7|>xO15avguw`W2XwTm-e09(_3ub;?^H6ewl&L8sm=L;tZdBWsF>@0PIs>n_nG+24)Zt% zq_U1yN=r+-J<;4ADXrJO?e(xy9VYYn^8Q*P6Wv8bzzkb$t9;_;k;crF%n?;fd*uqR z)=F>cE7a;h!hLAtspQR3e^N75`1{F{#T()9?k;!_Ew}%8umC6{l~oZ)4FnQpht$wg sMro;`6p%"] +} diff --git a/scripts/backup/ext/fb-ext/popup/index.html b/scripts/backup/ext/fb-ext/popup/index.html new file mode 100644 index 00000000..37008513 --- /dev/null +++ b/scripts/backup/ext/fb-ext/popup/index.html @@ -0,0 +1,39 @@ + + + + + + + Facebook Extension + + + + + +

Facebook Tools

+ +

Chọn hành động

+ + + +
+

Thời gian chờ:

+ +
+ +

Duyệt tối đa bao nhiêu bài?

+ (nhập 0 để duyệt hết)
+ + + + + + + + diff --git a/scripts/backup/ext/fb-ext/popup/main.js b/scripts/backup/ext/fb-ext/popup/main.js new file mode 100644 index 00000000..9d09809d --- /dev/null +++ b/scripts/backup/ext/fb-ext/popup/main.js @@ -0,0 +1,157 @@ +const sliderWaitTime = document.getElementById("slider-wait-time"); +const spanWaitTime = document.getElementById("wait-time"); +const inputMaxPosts = document.getElementById("max-posts"); +const radioAction = document.getElementsByName("action"); +const startBtn = document.getElementById("start-btn"); + +sliderWaitTime.value = localStorage.getItem("wait-time") || 3000; +spanWaitTime.innerHTML = renderTime(sliderWaitTime.value); +sliderWaitTime.oninput = function () { + spanWaitTime.innerHTML = renderTime(this.value); + localStorage.setItem("wait-time", this.value); +}; + +function renderTime(time) { + return (time / 1000).toFixed(1) + "s"; +} + +async function main() { + const tab = await getCurrentTab(); + + if (!tab.url.includes("groups") || !tab.url.includes("spam")) { + return prompt( + "Bạn cần mở trang duyệt bài spam của group trước. Ví dụ:", + "https://www.facebook.com/groups/gamecode/spam" + ); + } + + startBtn.addEventListener("click", async () => { + let action = radioAction[0].checked ? 1 : 2; + let max = parseInt(inputMaxPosts.value); + let wait = parseInt(sliderWaitTime.value); + + const currentTab = await getCurrentTab(); + runScriptInTab({ + target: { tabId: currentTab.id }, + func: start, + args: [action, max, wait], + }); + }); +} + +function start(action, max, wait) { + const selector = + action == 1 + ? '[role="main"] [aria-label="Đăng"]' + : '[role="main"] [aria-label="Từ chối"]'; + + if (max == 0) max = Infinity; + + const btns = Array.from(document.querySelectorAll(selector)); + onElementsAdded(selector, (nodes) => { + for (let node of nodes) { + if (btns.includes(node)) continue; + btns.push(node); + } + }); + + async function main() { + let counter = 0; + while (counter < max) { + if (btns.length > 0) { + const btn = btns.shift(); + + btn.scrollIntoView({ + block: "center", + // behavior: "smooth", + }); + console.log("click", btn); + btn.click(); + + counter++; + } + + if (wait) await sleep(wait); + } + alert("Duyệt xong " + counter + " bài"); + } + main(); + + function sleep(time) { + return new Promise((resolve) => setTimeout(resolve, time)); + } + + function onElementsAdded(selector, callback, once) { + let nodes = document.querySelectorAll(selector); + if (nodes?.length) { + callback(nodes); + if (once) return; + } + + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (!mutation.addedNodes) return; + + for (let node of mutation.addedNodes) { + if (node.nodeType != 1) continue; // only process Node.ELEMENT_NODE + + let n = node.matches(selector) + ? [node] + : Array.from(node.querySelectorAll(selector)); + + if (n?.length) { + callback(n); + if (once) observer.disconnect(); + } + } + }); + }); + + observer.observe(document, { + childList: true, + subtree: true, + attributes: false, + characterData: false, + }); + + // return disconnect function + return () => observer.disconnect(); + } +} + +async function getCurrentTab() { + let tabs = await chrome.tabs.query({ + active: true, + currentWindow: true, + }); + return tabs[0]; +} +const runScriptInTab = async (config = {}) => { + return new Promise((resolve, reject) => { + chrome.scripting.executeScript( + mergeObject( + { + world: "MAIN", + injectImmediately: true, + }, + config + ), + (injectionResults) => { + if (chrome.runtime.lastError) { + console.error(chrome.runtime.lastError); + reject(chrome.runtime.lastError); + } + // https://developer.chrome.com/docs/extensions/reference/scripting/#handling-results + else resolve(injectionResults?.find?.((_) => _.result)?.result); + } + ); + }); +}; +const mergeObject = (...objs) => { + // merge without null value + let res = {}; + for (let obj of objs) for (let key in obj) if (obj[key]) res[key] = obj[key]; + return res; +}; + +main(); diff --git a/scripts/backup/ext/fb-ext/popup/style.css b/scripts/backup/ext/fb-ext/popup/style.css new file mode 100644 index 00000000..f8d8a2aa --- /dev/null +++ b/scripts/backup/ext/fb-ext/popup/style.css @@ -0,0 +1,134 @@ +body { + min-width: 350px; + min-height: 200px; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-size: 16px; +} + + +h3 { + text-decoration: underline; + margin-bottom: 8px; +} + + +/* The container */ +.container { + display: block; + position: relative; + padding-left: 35px; + margin-bottom: 12px; + cursor: pointer; + font-size: 22px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* Hide the browser's default radio button */ +.container input { + position: absolute; + opacity: 0; + cursor: pointer; +} + +/* Create a custom radio button */ +.checkmark { + position: absolute; + top: 0; + left: 0; + height: 25px; + width: 25px; + background-color: #eee; + border-radius: 50%; +} + +/* On mouse-over, add a grey background color */ +.container:hover input~.checkmark { + background-color: #ccc; +} + +/* When the radio button is checked, add a blue background */ +.container input:checked~.checkmark { + background-color: #2196F3; +} + +/* Create the indicator (the dot/circle - hidden when not checked) */ +.checkmark:after { + content: ""; + position: absolute; + display: none; +} + +/* Show the indicator (dot/circle) when checked */ +.container input:checked~.checkmark:after { + display: block; +} + +/* Style the indicator (dot/circle) */ +.container .checkmark:after { + top: 9px; + left: 9px; + width: 8px; + height: 8px; + border-radius: 50%; + background: white; +} + + +/* ======================= Range ====================== */ + +.slidecontainer { + width: 100%; +} + +.slider { + -webkit-appearance: none; + width: 100%; + height: 25px; + background: #d3d3d3; + outline: none; + opacity: 0.7; + -webkit-transition: .2s; + transition: opacity .2s; +} + +.slider:hover { + opacity: 1; +} + +.slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 25px; + height: 25px; + background: #04AA6D; + cursor: pointer; +} + +.slider::-moz-range-thumb { + width: 25px; + height: 25px; + background: #04AA6D; + cursor: pointer; +} + +input[type=number] { + padding: 5px; + font-size: large; +} + +.button { + background-color: #04AA6D; + border: none; + color: white; + padding: 15px 32px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + cursor: pointer; + width: 100%; + margin-top: 30px; +} From 471d126350b98c2da05ae0b813147139e488dbee Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Mon, 5 Aug 2024 10:53:02 +0700 Subject: [PATCH 11/43] rename --- .../ext/{fb-ext => fb-group-ext}/assets/icon32.png | Bin .../ext/{fb-ext => fb-group-ext}/manifest.json | 0 .../ext/{fb-ext => fb-group-ext}/popup/index.html | 0 .../ext/{fb-ext => fb-group-ext}/popup/main.js | 0 .../ext/{fb-ext => fb-group-ext}/popup/style.css | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename scripts/backup/ext/{fb-ext => fb-group-ext}/assets/icon32.png (100%) rename scripts/backup/ext/{fb-ext => fb-group-ext}/manifest.json (100%) rename scripts/backup/ext/{fb-ext => fb-group-ext}/popup/index.html (100%) rename scripts/backup/ext/{fb-ext => fb-group-ext}/popup/main.js (100%) rename scripts/backup/ext/{fb-ext => fb-group-ext}/popup/style.css (100%) diff --git a/scripts/backup/ext/fb-ext/assets/icon32.png b/scripts/backup/ext/fb-group-ext/assets/icon32.png similarity index 100% rename from scripts/backup/ext/fb-ext/assets/icon32.png rename to scripts/backup/ext/fb-group-ext/assets/icon32.png diff --git a/scripts/backup/ext/fb-ext/manifest.json b/scripts/backup/ext/fb-group-ext/manifest.json similarity index 100% rename from scripts/backup/ext/fb-ext/manifest.json rename to scripts/backup/ext/fb-group-ext/manifest.json diff --git a/scripts/backup/ext/fb-ext/popup/index.html b/scripts/backup/ext/fb-group-ext/popup/index.html similarity index 100% rename from scripts/backup/ext/fb-ext/popup/index.html rename to scripts/backup/ext/fb-group-ext/popup/index.html diff --git a/scripts/backup/ext/fb-ext/popup/main.js b/scripts/backup/ext/fb-group-ext/popup/main.js similarity index 100% rename from scripts/backup/ext/fb-ext/popup/main.js rename to scripts/backup/ext/fb-group-ext/popup/main.js diff --git a/scripts/backup/ext/fb-ext/popup/style.css b/scripts/backup/ext/fb-group-ext/popup/style.css similarity index 100% rename from scripts/backup/ext/fb-ext/popup/style.css rename to scripts/backup/ext/fb-group-ext/popup/style.css From 80cd7a704a73e4e8022678a318327292759705d9 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Mon, 5 Aug 2024 11:05:48 +0700 Subject: [PATCH 12/43] update --- .../backup/ext/fb-group-ext/assets/icon.png | Bin 0 -> 327 bytes .../backup/ext/fb-group-ext/assets/icon32.png | Bin 1918 -> 0 bytes .../fb-group-ext/assets/icon_default_128.png | Bin 0 -> 3664 bytes .../fb-group-ext/assets/icon_default_16.png | Bin 0 -> 402 bytes .../fb-group-ext/assets/icon_default_48.png | Bin 0 -> 1112 bytes scripts/backup/ext/fb-group-ext/manifest.json | 28 +- .../backup/ext/fb-group-ext/popup/index.html | 78 ++-- scripts/backup/ext/fb-group-ext/popup/main.js | 344 ++++++++++-------- .../backup/ext/fb-group-ext/popup/style.css | 273 +++++++------- 9 files changed, 381 insertions(+), 342 deletions(-) create mode 100644 scripts/backup/ext/fb-group-ext/assets/icon.png delete mode 100644 scripts/backup/ext/fb-group-ext/assets/icon32.png create mode 100644 scripts/backup/ext/fb-group-ext/assets/icon_default_128.png create mode 100644 scripts/backup/ext/fb-group-ext/assets/icon_default_16.png create mode 100644 scripts/backup/ext/fb-group-ext/assets/icon_default_48.png diff --git a/scripts/backup/ext/fb-group-ext/assets/icon.png b/scripts/backup/ext/fb-group-ext/assets/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b8bfbff10fdcb1bf94c20449c6d95917c886a845 GIT binary patch literal 327 zcmV-N0l5B&P)drGwG}5)gh<AbC~Xf#vQZcfxU`d#5D=sL(8hbbmRn>O ZasliiMe6;P^=1G7002ovPDHLkV1lr=hOhtt literal 0 HcmV?d00001 diff --git a/scripts/backup/ext/fb-group-ext/assets/icon32.png b/scripts/backup/ext/fb-group-ext/assets/icon32.png deleted file mode 100644 index 29aff1d5151552e20854aff47dd14b0a0d97776e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1918 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 literal 0 HcmV?d00001 diff --git a/scripts/backup/ext/fb-group-ext/assets/icon_default_48.png b/scripts/backup/ext/fb-group-ext/assets/icon_default_48.png new file mode 100644 index 0000000000000000000000000000000000000000..f385c0073511ea6f10fa60268aa9d4b1708134df GIT binary patch literal 1112 zcmV-e1gHCnP)6GG2~?8^K}9FUpJeiCwrBWnC+r<^;2Wu~5$<1#;_rOP0z)hn z8J&?{y<>;8EF2HGpIq;-C;HaWg~J^b>BxTzEz&WLrzBl`o}#x$e2GOU+UX8nLs>~l+hcM4>L#|L%s#GU5kiae)H6p{{{`Ao9Qfb^~`<4|V-f zh1m_>-Fp2&W62fH8W^tr2y>nEW9cE!R#-a4qZ>st@o>U zT|6TVc})sdjyZcH!`%}0`9_6LzFStHpMPh{L?I1IpiH9ytlA-xKT5#4Ky0#bEBx7~ z75e@+LxZ;ybpC0IQVv5Nc_@RjsDrw$12ZbtTt(gsRzvrwb((KFH&#>p_r$Bu@WOcX zrY6hx021ICVU1>Vrr%<)fw7RQs0WY{XyYyvfb|!i5ty{`?uP&t<*ljUaw-}^8dlli zr8uSE+a5DZCEozJDk^{sctqeCX=hKMZ71&xILqb$5(`71oqSLPT?fxdD_1A9rMxjU zJe1(8I)sp^115I=uIOIw(qS)~ot9HG#wNvWpe?jnvpg55IfP1u1op26rHTah&mN_U e;JuxHxA_n85!6a`7~g0B0000"] -} +{ + "manifest_version": 3, + "name": "Facebook Group Extension", + "description": "Quản lý nhóm facebook", + "version": "1.0", + "action": { + "default_popup": "./popup/index.html" + }, + "icons": { + "16": "assets/icon_default_16.png", + "48": "assets/icon_default_48.png", + "128": "assets/icon_default_128.png" + }, + "permissions": ["tabs", "scripting"], + "host_permissions": ["*://*/*", ""] +} diff --git a/scripts/backup/ext/fb-group-ext/popup/index.html b/scripts/backup/ext/fb-group-ext/popup/index.html index 37008513..4a7d5369 100644 --- a/scripts/backup/ext/fb-group-ext/popup/index.html +++ b/scripts/backup/ext/fb-group-ext/popup/index.html @@ -1,39 +1,39 @@ - - - - - - - Facebook Extension - - - - - -

Facebook Tools

- -

Chọn hành động

- - - -
-

Thời gian chờ:

- -
- -

Duyệt tối đa bao nhiêu bài?

- (nhập 0 để duyệt hết)
- - - - - - - - + + + + + + + Quản lý nhóm Facebook + + + + + +

Facebook Tools

+ +

Chọn hành động

+ + + +
+

Thời gian chờ:

+ +
+ +

Duyệt tối đa bao nhiêu bài?

+ (nhập 0 để duyệt hết)
+ + + + + + + + diff --git a/scripts/backup/ext/fb-group-ext/popup/main.js b/scripts/backup/ext/fb-group-ext/popup/main.js index 9d09809d..e5891053 100644 --- a/scripts/backup/ext/fb-group-ext/popup/main.js +++ b/scripts/backup/ext/fb-group-ext/popup/main.js @@ -1,157 +1,187 @@ -const sliderWaitTime = document.getElementById("slider-wait-time"); -const spanWaitTime = document.getElementById("wait-time"); -const inputMaxPosts = document.getElementById("max-posts"); -const radioAction = document.getElementsByName("action"); -const startBtn = document.getElementById("start-btn"); - -sliderWaitTime.value = localStorage.getItem("wait-time") || 3000; -spanWaitTime.innerHTML = renderTime(sliderWaitTime.value); -sliderWaitTime.oninput = function () { - spanWaitTime.innerHTML = renderTime(this.value); - localStorage.setItem("wait-time", this.value); -}; - -function renderTime(time) { - return (time / 1000).toFixed(1) + "s"; -} - -async function main() { - const tab = await getCurrentTab(); - - if (!tab.url.includes("groups") || !tab.url.includes("spam")) { - return prompt( - "Bạn cần mở trang duyệt bài spam của group trước. Ví dụ:", - "https://www.facebook.com/groups/gamecode/spam" - ); - } - - startBtn.addEventListener("click", async () => { - let action = radioAction[0].checked ? 1 : 2; - let max = parseInt(inputMaxPosts.value); - let wait = parseInt(sliderWaitTime.value); - - const currentTab = await getCurrentTab(); - runScriptInTab({ - target: { tabId: currentTab.id }, - func: start, - args: [action, max, wait], - }); - }); -} - -function start(action, max, wait) { - const selector = - action == 1 - ? '[role="main"] [aria-label="Đăng"]' - : '[role="main"] [aria-label="Từ chối"]'; - - if (max == 0) max = Infinity; - - const btns = Array.from(document.querySelectorAll(selector)); - onElementsAdded(selector, (nodes) => { - for (let node of nodes) { - if (btns.includes(node)) continue; - btns.push(node); - } - }); - - async function main() { - let counter = 0; - while (counter < max) { - if (btns.length > 0) { - const btn = btns.shift(); - - btn.scrollIntoView({ - block: "center", - // behavior: "smooth", - }); - console.log("click", btn); - btn.click(); - - counter++; - } - - if (wait) await sleep(wait); - } - alert("Duyệt xong " + counter + " bài"); - } - main(); - - function sleep(time) { - return new Promise((resolve) => setTimeout(resolve, time)); - } - - function onElementsAdded(selector, callback, once) { - let nodes = document.querySelectorAll(selector); - if (nodes?.length) { - callback(nodes); - if (once) return; - } - - const observer = new MutationObserver((mutations) => { - mutations.forEach((mutation) => { - if (!mutation.addedNodes) return; - - for (let node of mutation.addedNodes) { - if (node.nodeType != 1) continue; // only process Node.ELEMENT_NODE - - let n = node.matches(selector) - ? [node] - : Array.from(node.querySelectorAll(selector)); - - if (n?.length) { - callback(n); - if (once) observer.disconnect(); - } - } - }); - }); - - observer.observe(document, { - childList: true, - subtree: true, - attributes: false, - characterData: false, - }); - - // return disconnect function - return () => observer.disconnect(); - } -} - -async function getCurrentTab() { - let tabs = await chrome.tabs.query({ - active: true, - currentWindow: true, - }); - return tabs[0]; -} -const runScriptInTab = async (config = {}) => { - return new Promise((resolve, reject) => { - chrome.scripting.executeScript( - mergeObject( - { - world: "MAIN", - injectImmediately: true, - }, - config - ), - (injectionResults) => { - if (chrome.runtime.lastError) { - console.error(chrome.runtime.lastError); - reject(chrome.runtime.lastError); - } - // https://developer.chrome.com/docs/extensions/reference/scripting/#handling-results - else resolve(injectionResults?.find?.((_) => _.result)?.result); - } - ); - }); -}; -const mergeObject = (...objs) => { - // merge without null value - let res = {}; - for (let obj of objs) for (let key in obj) if (obj[key]) res[key] = obj[key]; - return res; -}; - -main(); +const sliderWaitTime = document.getElementById("slider-wait-time"); +const spanWaitTime = document.getElementById("wait-time"); +const inputMaxPosts = document.getElementById("max-posts"); +const radioAction = document.getElementsByName("action"); +const startBtn = document.getElementById("start-btn"); + +sliderWaitTime.value = localStorage.getItem("wait-time") || 3000; +spanWaitTime.innerHTML = renderTime(sliderWaitTime.value); +sliderWaitTime.oninput = function () { + spanWaitTime.innerHTML = renderTime(this.value); + localStorage.setItem("wait-time", this.value); +}; + +function renderTime(time) { + return (time / 1000).toFixed(1) + "s"; +} + +async function main() { + const tab = await getCurrentTab(); + + if (!tab.url.includes("groups") || !tab.url.includes("spam")) { + return prompt( + "Bạn cần mở trang duyệt bài spam của group trước. Ví dụ:", + "https://www.facebook.com/groups/gamecode/spam" + ); + } + + startBtn.addEventListener("click", async () => { + let action = radioAction[0].checked ? 1 : 2; + let max = parseInt(inputMaxPosts.value); + let wait = parseInt(sliderWaitTime.value); + runScriptInTab({ + target: { tabId: tab.id }, + func: start, + args: [action, max, wait], + }); + }); + + // check is running + (async function checkIsRunning() { + const isRunning = await runScriptInTab({ + target: { tabId: tab.id }, + func: () => window.fb_group_ext_running, + }); + if (isRunning) { + // disable button + startBtn.disabled = true; + startBtn.innerHTML = "Đang xử lý..."; + startBtn.classList.add("disabled"); + } else { + // enable button + startBtn.disabled = false; + startBtn.innerHTML = "Bắt đầu"; + startBtn.classList.remove("disabled"); + } + setTimeout(checkIsRunning, 500); + })(); +} + +function start(action, max, wait) { + const selector = + action == 1 + ? '[role="main"] [aria-label="Đăng"]' + : '[role="main"] [aria-label="Từ chối"]'; + + if (max == 0) max = Infinity; + + const btns = Array.from(document.querySelectorAll(selector)); + + if (!btns.length) { + alert("Không tìm thấy bài nào"); + return; + } + + onElementsAdded(selector, (nodes) => { + for (let node of nodes) { + if (btns.includes(node)) continue; + btns.push(node); + } + }); + + async function main() { + window.fb_group_ext_running = true; + + // for test + // setTimeout(() => (window.fb_group_ext_running = false), 5000); + // return; + + let counter = 0; + while (counter < max) { + if (btns.length > 0) { + const btn = btns.shift(); + + btn.scrollIntoView({ block: "center", behavior: "smooth" }); + console.log("click", btn); + btn.click(); + + counter++; + } + + if (wait) await sleep(wait); + } + window.fb_group_ext_running = false; + alert("Duyệt xong " + counter + " bài"); + } + + main(); + + function sleep(time) { + return new Promise((resolve) => setTimeout(resolve, time)); + } + + function onElementsAdded(selector, callback, once) { + let nodes = document.querySelectorAll(selector); + if (nodes?.length) { + callback(nodes); + if (once) return; + } + + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (!mutation.addedNodes) return; + + for (let node of mutation.addedNodes) { + if (node.nodeType != 1) continue; // only process Node.ELEMENT_NODE + + let n = node.matches(selector) + ? [node] + : Array.from(node.querySelectorAll(selector)); + + if (n?.length) { + callback(n); + if (once) observer.disconnect(); + } + } + }); + }); + + observer.observe(document, { + childList: true, + subtree: true, + attributes: false, + characterData: false, + }); + + // return disconnect function + return () => observer.disconnect(); + } +} + +async function getCurrentTab() { + let tabs = await chrome.tabs.query({ + active: true, + currentWindow: true, + }); + return tabs[0]; +} + +const runScriptInTab = async (config = {}) => { + return new Promise((resolve, reject) => { + chrome.scripting.executeScript( + mergeObject( + { + world: "MAIN", + injectImmediately: true, + }, + config + ), + (injectionResults) => { + if (chrome.runtime.lastError) { + console.error(chrome.runtime.lastError); + reject(chrome.runtime.lastError); + } + // https://developer.chrome.com/docs/extensions/reference/scripting/#handling-results + else resolve(injectionResults?.find?.((_) => _.result)?.result); + } + ); + }); +}; +const mergeObject = (...objs) => { + // merge without null value + let res = {}; + for (let obj of objs) for (let key in obj) if (obj[key]) res[key] = obj[key]; + return res; +}; + +main(); diff --git a/scripts/backup/ext/fb-group-ext/popup/style.css b/scripts/backup/ext/fb-group-ext/popup/style.css index f8d8a2aa..1faef683 100644 --- a/scripts/backup/ext/fb-group-ext/popup/style.css +++ b/scripts/backup/ext/fb-group-ext/popup/style.css @@ -1,134 +1,139 @@ -body { - min-width: 350px; - min-height: 200px; - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - font-size: 16px; -} - - -h3 { - text-decoration: underline; - margin-bottom: 8px; -} - - -/* The container */ -.container { - display: block; - position: relative; - padding-left: 35px; - margin-bottom: 12px; - cursor: pointer; - font-size: 22px; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -/* Hide the browser's default radio button */ -.container input { - position: absolute; - opacity: 0; - cursor: pointer; -} - -/* Create a custom radio button */ -.checkmark { - position: absolute; - top: 0; - left: 0; - height: 25px; - width: 25px; - background-color: #eee; - border-radius: 50%; -} - -/* On mouse-over, add a grey background color */ -.container:hover input~.checkmark { - background-color: #ccc; -} - -/* When the radio button is checked, add a blue background */ -.container input:checked~.checkmark { - background-color: #2196F3; -} - -/* Create the indicator (the dot/circle - hidden when not checked) */ -.checkmark:after { - content: ""; - position: absolute; - display: none; -} - -/* Show the indicator (dot/circle) when checked */ -.container input:checked~.checkmark:after { - display: block; -} - -/* Style the indicator (dot/circle) */ -.container .checkmark:after { - top: 9px; - left: 9px; - width: 8px; - height: 8px; - border-radius: 50%; - background: white; -} - - -/* ======================= Range ====================== */ - -.slidecontainer { - width: 100%; -} - -.slider { - -webkit-appearance: none; - width: 100%; - height: 25px; - background: #d3d3d3; - outline: none; - opacity: 0.7; - -webkit-transition: .2s; - transition: opacity .2s; -} - -.slider:hover { - opacity: 1; -} - -.slider::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 25px; - height: 25px; - background: #04AA6D; - cursor: pointer; -} - -.slider::-moz-range-thumb { - width: 25px; - height: 25px; - background: #04AA6D; - cursor: pointer; -} - -input[type=number] { - padding: 5px; - font-size: large; -} - -.button { - background-color: #04AA6D; - border: none; - color: white; - padding: 15px 32px; - text-align: center; - text-decoration: none; - display: inline-block; - font-size: 16px; - cursor: pointer; - width: 100%; - margin-top: 30px; -} +body { + min-width: 350px; + min-height: 200px; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-size: 16px; +} + + +h3 { + text-decoration: underline; + margin-bottom: 8px; +} + + +/* The container */ +.container { + display: block; + position: relative; + padding-left: 35px; + margin-bottom: 12px; + cursor: pointer; + font-size: 22px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* Hide the browser's default radio button */ +.container input { + position: absolute; + opacity: 0; + cursor: pointer; +} + +/* Create a custom radio button */ +.checkmark { + position: absolute; + top: 0; + left: 0; + height: 25px; + width: 25px; + background-color: #eee; + border-radius: 50%; +} + +/* On mouse-over, add a grey background color */ +.container:hover input~.checkmark { + background-color: #ccc; +} + +/* When the radio button is checked, add a blue background */ +.container input:checked~.checkmark { + background-color: #2196F3; +} + +/* Create the indicator (the dot/circle - hidden when not checked) */ +.checkmark:after { + content: ""; + position: absolute; + display: none; +} + +/* Show the indicator (dot/circle) when checked */ +.container input:checked~.checkmark:after { + display: block; +} + +/* Style the indicator (dot/circle) */ +.container .checkmark:after { + top: 9px; + left: 9px; + width: 8px; + height: 8px; + border-radius: 50%; + background: white; +} + + +/* ======================= Range ====================== */ + +.slidecontainer { + width: 100%; +} + +.slider { + -webkit-appearance: none; + width: 100%; + height: 25px; + background: #d3d3d3; + outline: none; + opacity: 0.7; + -webkit-transition: .2s; + transition: opacity .2s; +} + +.slider:hover { + opacity: 1; +} + +.slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 25px; + height: 25px; + background: #04AA6D; + cursor: pointer; +} + +.slider::-moz-range-thumb { + width: 25px; + height: 25px; + background: #04AA6D; + cursor: pointer; +} + +input[type=number] { + padding: 5px; + font-size: large; +} + +.button { + background-color: #04AA6D; + border: none; + color: white; + padding: 15px 32px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + cursor: pointer; + width: 100%; + margin-top: 30px; +} + +.button.disabled { + background-color: rgb(151, 12, 12); + cursor: not-allowed; +} From 824d31d1f9b241f477060087274851cd65231e44 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Mon, 5 Aug 2024 12:22:55 +0700 Subject: [PATCH 13/43] update UI --- scripts/ufs_statistic.css | 28 ++++++- scripts/ufs_statistic.js | 154 +++++++++++++++++++++++--------------- 2 files changed, 116 insertions(+), 66 deletions(-) diff --git a/scripts/ufs_statistic.css b/scripts/ufs_statistic.css index 623ae16b..6223236d 100644 --- a/scripts/ufs_statistic.css +++ b/scripts/ufs_statistic.css @@ -11,6 +11,7 @@ canvas { li { position: relative; + white-space: pre; } li:hover { @@ -19,8 +20,16 @@ li:hover { } li a { - display: inline-block; + position: absolute; + display: flex; + align-items: center; color: yellow; + right: 0; + top: 0; +} + +li a span { + margin-right: 5px; } li a:visited { @@ -30,9 +39,6 @@ li a:visited { li a img { width: 30px; transition: all 0.2s ease; - position: absolute; - right: 0; - top: 0; } li:hover img { @@ -59,3 +65,17 @@ button:hover { .btn-active { box-shadow: inset 0 0 10px 5px #333; } + +.show-on-hover { + display: inline-block; + width: 0; + opacity: 0; + transition: all 0.5s ease 2s; + overflow: hidden; +} + +li:hover .show-on-hover { + width: max-content; + opacity: 1; + transition: all 0s ease 0.5s; +} diff --git a/scripts/ufs_statistic.js b/scripts/ufs_statistic.js index 1c6a5dce..5c09f2ac 100644 --- a/scripts/ufs_statistic.js +++ b/scripts/ufs_statistic.js @@ -36,15 +36,24 @@ async function onDocumentEnd() { let hasLog = logs[0] != "Log not found" && logs[0] != "Waking up"; const allLogs = logs.map((log) => { - return { + const data = { log, uid: extractUid(log), time: new Date(extractTime(log)), - timeString: extractTime(log), + timeString: extractTime(log).replace(/\d+\/\d+\/\d+, /, ""), eventName: extractEventName(log), version: extractVersion(log), + totalCount: extractTotalCount(log), isScript: isScript(log), }; + const eventNameWithoutVersion = data.eventName + .replace("(" + data.version + ")", "") + .trim(); + const version = padStr(data.version, 4, " "); + if (version && eventNameWithoutVersion) + data.logPretty = `${data.timeString} | ${version} | ${eventNameWithoutVersion} ${data.totalCount} ${data.uid}`; + else data.logPretty = data.log; + return data; }); const container = document.createElement("div"); @@ -54,9 +63,10 @@ async function onDocumentEnd() { UfsGlobal.DOM.injectCssFile ); - // #region add search box + // #region re-make UI document.body.innerText = ""; + // #region create list const ol = document.createElement("ol"); ol.setAttribute("reversed", true); document.body.appendChild(ol); @@ -64,59 +74,23 @@ async function onDocumentEnd() { let li = document.createElement("li"); if (isFbUid(data?.uid)) { li.innerHTML = - data.log + - ` - + data.logPretty + + ` + fb - fb `; } else { - li.textContent = data.log; + li.innerHTML = data.logPretty; } ol.appendChild(li); return { li, data }; }); console.log(all_li); + // #endregion - // load fb profiles - const allUid = allLogs - .filter((log) => isFbUid(log?.uid)) - .map((log) => log.uid); - - const uniqueUid = [...new Set(allUid)].filter(Boolean); - - if (uniqueUid.length) { - initCache().then(() => { - const promises = uniqueUid.map( - (uid) => () => - getFbProfile(uid).then((info) => { - document - .querySelectorAll(`[data-profile-name="${uid}"]`) - .forEach((el) => { - el.textContent = info.name; - }); - document - .querySelectorAll(`[data-profile-avatar="${uid}"]`) - .forEach((el) => { - el.src = info.avatar.replace(/\\\//g, "/") || el.src; - }); - }) - ); - UfsGlobal.Utils.promiseAllStepN(5, promises).then(() => { - if (CACHED.newFbUsers.size) { - const arr = Array.from(CACHED.newFbUsers.values()); - console.log("New users", arr); - alert( - CACHED.newFbUsers.size + - " new users:\n\n" + - arr.map((_) => _.name).join("\n") - ); - } - }); - }); - } - + // #region trace uid const traceUidCheckmark = document.createElement("input"); traceUidCheckmark.id = "trace-uid"; traceUidCheckmark.type = "checkbox"; @@ -129,7 +103,9 @@ async function onDocumentEnd() { label.textContent = "Trace by uid"; label.setAttribute("for", traceUidCheckmark.id); container.appendChild(label); + // #endregion + // #region search box const searchBox = document.createElement("input"); searchBox.placeholder = "Search logs..."; container.prepend(searchBox); @@ -211,6 +187,7 @@ async function onDocumentEnd() { } } }); + // #endregion // #endregion @@ -403,7 +380,7 @@ async function onDocumentEnd() { // #endregion - // ======================== show scripts only ======================== + // #region ======================== show scripts only ======================== let scriptOnlyState = false; const scriptOnlyToggle = document.createElement("button"); @@ -422,8 +399,9 @@ async function onDocumentEnd() { } }); }; + // #endregion - // ======================== Average section ======================== + // #region ======================== Average section ======================== const scriptsUsed = new Map(); allLogs.forEach((data) => { if (data.isScript) { @@ -445,8 +423,8 @@ async function onDocumentEnd() { ${scriptsUsed.size} unique scripts

${logByUid.size} unique users
${fbUsers.length} facebook users`; + // #endregion - // ======================== Append Charts ======================== container.prepend( h1, toggleShowHideAllBtn, @@ -456,6 +434,47 @@ async function onDocumentEnd() { scriptOnlyToggle ); // #endregion + + // #region list by uid/script + + // #region load fb profiles + const allUid = allLogs + .filter((log) => isFbUid(log?.uid)) + .map((log) => log.uid); + + const uniqueUid = [...new Set(allUid)].filter(Boolean); + + if (uniqueUid.length) { + initCache().then(() => { + const promises = uniqueUid.map( + (uid) => () => + getFbProfile(uid).then((info) => { + document + .querySelectorAll(`[data-profile-name="${uid}"]`) + .forEach((el) => { + el.textContent = limitString(info.name, 40); + }); + document + .querySelectorAll(`[data-profile-avatar="${uid}"]`) + .forEach((el) => { + el.src = info.avatar.replace(/\\\//g, "/") || el.src; + }); + }) + ); + UfsGlobal.Utils.promiseAllStepN(5, promises).then(() => { + if (CACHED.newFbUsers.size) { + const arr = Array.from(CACHED.newFbUsers.values()); + console.log("New users", arr); + alert( + CACHED.newFbUsers.size + + " new users:\n\n" + + arr.map((_) => _.name).join("\n") + ); + } + }); + }); + } + // #endregion } // #region add date selector @@ -613,25 +632,22 @@ async function getFbProfile(uid) { // log example: 5/31/2024, 9:13:41 AM: OPEN-TAB-unlock (1.67-1717121281787) -> 43 function extractUid(log) { - return /-(\d+)\)/.exec(log)?.[1]; + return /-(\d+)\)/.exec(log)?.[1] || "?"; } function extractTime(log) { - let lastColon = log.lastIndexOf(":"); - let time = log.substring(0, lastColon); - return time; + return ( + /\d{1,2}\/\d{1,2}\/\d{4}, \d{1,2}:\d{1,2}:\d{1,2} \w{2}/.exec(log)?.[0] || + "" + ); } function extractEventName(log) { - let logWithoutUid = log.replace(/-\d+/, ""); - let lastColon = logWithoutUid.lastIndexOf(":"); - let lastArrow = logWithoutUid.lastIndexOf("->"); - let scriptName = logWithoutUid.substring(lastColon + 1, lastArrow - 1).trim(); - return scriptName; + return /: (.*?) \(/.exec(log)?.[1] || ""; +} +function extractTotalCount(log) { + return / -> (\d+)/.exec(log)?.[1] || ""; } - function extractVersion(log) { - let lastBracket = log.lastIndexOf("("); - let nextDash = log.indexOf("-", lastBracket); - return log.substring(lastBracket + 1, nextDash - 1).trim(); + return / \((.*?)-\d*\)/.exec(log)?.[1] || ""; } function isScript(log) { @@ -642,6 +658,20 @@ function isScript(log) { log.includes("CLICK_") || log.includes("-INFO") || log.includes("-FAVORITE") || - log.includes("-VIEW-SOURCE") + log.includes("-VIEW-SOURCE") || + log.includes("CHECK-FOR-UPDATE") || + log.includes("RESTORE") || + log.includes("BACKUP") || + log.includes("CHANGE-THEME") || + log.includes("CHANGE-SMOOTH-SCROLL") ); } + +function limitString(string, length) { + if (string.length <= length) return string; + return string.substring(0, length - 3) + "..."; +} + +function padStr(string, length, char = " ") { + return string + char.repeat(length - string.length); +} From 4c6a590538ecceacdc46286ebc0f3fe2a7e620d9 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Mon, 5 Aug 2024 16:22:10 +0700 Subject: [PATCH 14/43] update UI --- scripts/ufs_statistic.css | 69 +++++++++++++++++++++++++++++++++++--- scripts/ufs_statistic.js | 70 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 133 insertions(+), 6 deletions(-) diff --git a/scripts/ufs_statistic.css b/scripts/ufs_statistic.css index 6223236d..1248ecec 100644 --- a/scripts/ufs_statistic.css +++ b/scripts/ufs_statistic.css @@ -9,7 +9,7 @@ canvas { max-height: 500px; } -li { +ol.log-list li { position: relative; white-space: pre; } @@ -19,7 +19,7 @@ li:hover { color: white; } -li a { +ol.log-list li a { position: absolute; display: flex; align-items: center; @@ -32,11 +32,12 @@ li a span { margin-right: 5px; } -li a:visited { +ol.log-list li a:visited { color: green; } -li a img { +li a img, +.avatar { width: 30px; transition: all 0.2s ease; } @@ -79,3 +80,63 @@ li:hover .show-on-hover { opacity: 1; transition: all 0s ease 0.5s; } + +.modal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + flex-direction: column; + align-items: center; + justify-content: center; + z-index: 9999; +} + +.modal-content { + background: #2c2c2c; + padding: 20px; + border-radius: 5px; + box-shadow: 0 0 10px #000; + max-width: 90vw; + max-height: 90vh; + overflow-y: auto; + overflow-x: hidden; + min-width: 50vw; + display: flex; + flex-direction: column; + align-items: center; +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; +} + +.modal-body { + margin-bottom: 10px; +} + +.modal .close { + cursor: pointer; + position: absolute; + top: 10px; + right: 10px; + width: 20px; + height: 20px; + cursor: pointer; +} + +.modal ol { + padding: 0; + list-style: none; +} + +.modal ol a { + color: white; + text-decoration: none; +} diff --git a/scripts/ufs_statistic.js b/scripts/ufs_statistic.js index 5c09f2ac..b964edbe 100644 --- a/scripts/ufs_statistic.js +++ b/scripts/ufs_statistic.js @@ -68,6 +68,7 @@ async function onDocumentEnd() { // #region create list const ol = document.createElement("ol"); + ol.classList.add("log-list"); ol.setAttribute("reversed", true); document.body.appendChild(ol); const all_li = allLogs.map((data) => { @@ -425,18 +426,83 @@ async function onDocumentEnd() { ${fbUsers.length} facebook users`; // #endregion + // #region ======================== modal sortby uid/script ======================== + const modalByUid = document.createElement("div"); + modalByUid.classList.add("modal"); + modalByUid.innerHTML = ` + `; + + const modalByScript = document.createElement("div"); + modalByScript.classList.add("modal"); + modalByScript.innerHTML = ` + `; + + const btnOpenModalByUid = document.createElement("button"); + btnOpenModalByUid.textContent = "Rank by uid"; + btnOpenModalByUid.onclick = () => { + modalByUid.style.display = "flex"; + }; + + const btnOpenModalByScript = document.createElement("button"); + btnOpenModalByScript.textContent = "Rank by event name"; + btnOpenModalByScript.onclick = () => { + modalByScript.style.display = "flex"; + }; + + document.body.addEventListener("click", (event) => { + if (event.target.classList.contains("modal")) { + event.target.style.display = "none"; + } + }); + + document.body.append(modalByUid, modalByScript); + // #endregion + container.prepend( h1, toggleShowHideAllBtn, canvas_eventPerHour, + btnOpenModalByScript, canvas_events, + btnOpenModalByUid, canvas_uid, scriptOnlyToggle ); // #endregion - // #region list by uid/script - // #region load fb profiles const allUid = allLogs .filter((log) => isFbUid(log?.uid)) From 3eaabc446477c46289876990b3782dceb474c5dd Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Mon, 5 Aug 2024 16:38:40 +0700 Subject: [PATCH 15/43] search --- scripts/ufs_statistic.css | 17 ++++++++++++++++- scripts/ufs_statistic.js | 11 ++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/scripts/ufs_statistic.css b/scripts/ufs_statistic.css index 1248ecec..3d5872b0 100644 --- a/scripts/ufs_statistic.css +++ b/scripts/ufs_statistic.css @@ -104,7 +104,7 @@ li:hover .show-on-hover { max-height: 90vh; overflow-y: auto; overflow-x: hidden; - min-width: 50vw; + min-width: 500px; display: flex; flex-direction: column; align-items: center; @@ -119,6 +119,7 @@ li:hover .show-on-hover { .modal-body { margin-bottom: 10px; + width: 100%; } .modal .close { @@ -140,3 +141,17 @@ li:hover .show-on-hover { color: white; text-decoration: none; } + +.modal li { + width: 100%; + clear: right; +} + +[data-search] { + cursor: pointer; + float: right; +} + +[data-search]:hover { + text-decoration: underline; +} diff --git a/scripts/ufs_statistic.js b/scripts/ufs_statistic.js index b964edbe..8a5ff73f 100644 --- a/scripts/ufs_statistic.js +++ b/scripts/ufs_statistic.js @@ -445,6 +445,7 @@ async function onDocumentEnd() { ${uid} (${count}) + ${uid}🔎 `; }) .join("")} @@ -463,7 +464,10 @@ async function onDocumentEnd() {
    ${Array.from(eventNameCountSorted.entries()) .map(([eventName, count], index) => { - return `
  1. ${index + 1}. ${eventName} (${count})
  2. `; + return `
  3. + ${index + 1}. ${eventName} (${count}) + 🔎 +
  4. `; }) .join("")}
@@ -486,6 +490,11 @@ async function onDocumentEnd() { if (event.target.classList.contains("modal")) { event.target.style.display = "none"; } + const dataSearch = event.target.getAttribute("data-search"); + if (dataSearch) { + searchBox.value = dataSearch; + searchBox.dispatchEvent(new Event("input", { bubbles: true })); + } }); document.body.append(modalByUid, modalByScript); From 675a12e29f8f5a496733795a48a71f2c20e218ea Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Mon, 5 Aug 2024 16:52:33 +0700 Subject: [PATCH 16/43] hover modal --- scripts/ufs_statistic.css | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/ufs_statistic.css b/scripts/ufs_statistic.css index 3d5872b0..1491e44d 100644 --- a/scripts/ufs_statistic.css +++ b/scripts/ufs_statistic.css @@ -88,13 +88,16 @@ li:hover .show-on-hover { left: 0; width: 100%; height: 100%; - background: rgba(0, 0, 0, 0.5); flex-direction: column; align-items: center; justify-content: center; z-index: 9999; } +.modal:has(.modal-content:hover) { + background: rgba(0, 0, 0, 0.5); +} + .modal-content { background: #2c2c2c; padding: 20px; @@ -108,6 +111,12 @@ li:hover .show-on-hover { display: flex; flex-direction: column; align-items: center; + opacity: 0.05; + transition: opacity 0.1s ease; +} + +.modal-content:hover { + opacity: 1; } .modal-header { From 6c37d65a7f3637177bcc7e29acdab512ba1d2ccd Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Mon, 5 Aug 2024 18:24:59 +0700 Subject: [PATCH 17/43] fix --- popup/tabs.js | 2 +- scripts/helpers/utils.js | 2 +- scripts/libs/ajax-hook/index.js | 2 +- scripts/test.js | 24 +++++++++++++++++++++++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/popup/tabs.js b/popup/tabs.js index 11ad2c2e..4b8881fb 100644 --- a/popup/tabs.js +++ b/popup/tabs.js @@ -33,7 +33,7 @@ const tabs = [ { ...CATEGORY.search, scripts: [ - // s.test, + s.test, // s.ufs_statistic, R.theresanaiforthat, R.timeis, diff --git a/scripts/helpers/utils.js b/scripts/helpers/utils.js index 97459bd8..782aac26 100644 --- a/scripts/helpers/utils.js +++ b/scripts/helpers/utils.js @@ -142,7 +142,7 @@ export function runFunc(fnPath = "", params = [], global = {}) { export async function trackEvent(scriptId) { console.log("trackEvent", scriptId, version); - // return; + return; try { let res = await fetch( // "http://localhost:3000/count", diff --git a/scripts/libs/ajax-hook/index.js b/scripts/libs/ajax-hook/index.js index cc371639..d09ae7a9 100644 --- a/scripts/libs/ajax-hook/index.js +++ b/scripts/libs/ajax-hook/index.js @@ -62,7 +62,7 @@ function initFetch() { } } - let response = await originalFetch(urlOrRequest); + let response = await originalFetch(urlOrRequest, options); for (const { fn } of onAfterFetchFn) { const res = await fn?.(request.url, request.options, response)?.catch( console.error diff --git a/scripts/test.js b/scripts/test.js index 986166a3..979198c3 100644 --- a/scripts/test.js +++ b/scripts/test.js @@ -1,5 +1,5 @@ import { UfsGlobal } from "./content-scripts/ufs_global.js"; -import { fetchGraphQl, getFbdtsg } from "./fb_GLOBAL.js"; +import { hookFetch } from "./libs/ajax-hook/index.js"; export default { icon: "", @@ -12,6 +12,8 @@ export default { vi: "", }, + whiteList: ["https://chatgpt.com/*"], + popupScript: { onClick: async () => { function getAverageRGB(img) { @@ -329,6 +331,26 @@ export default { }, pageScript: { + onDocumentStart: () => { + hookFetch({ + onBefore: (url, options) => { + if ( + url === "https://chatgpt.com/backend-anon/conversation" || + url === "https://chatgpt.com/backend-api/conversation" + ) { + console.log(url, options); + const body = JSON.parse(options.body); + // body.model = "gpt-4o"; + body.messages.forEach((m) => { + m.author.role = "system"; + // m.content.parts = ["what is your name"]; + }); + console.log("body", body); + options.body = JSON.stringify(body); + } + }, + }); + }, // download album _onClick: async () => { (async () => { From 5d29773222f5fde1c398783f4c90eb6be8104022 Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Mon, 5 Aug 2024 21:52:46 +0700 Subject: [PATCH 18/43] clean --- scripts/net-request-rules/rules.json | 74 ++-------------------------- 1 file changed, 4 insertions(+), 70 deletions(-) diff --git a/scripts/net-request-rules/rules.json b/scripts/net-request-rules/rules.json index 6f5d5554..7a47e4da 100644 --- a/scripts/net-request-rules/rules.json +++ b/scripts/net-request-rules/rules.json @@ -80,48 +80,24 @@ { "header": "referer", "operation": "set", - "value": "https://www.bing.com/images/create/" + "value": "https://www.instagram.com" }, { "header": "origin", "operation": "set", - "value": "https://www.bing.com" + "value": "https://www.instagram.com" } ] }, "condition": { "domain": "extension://*", - "urlFilter": "https://www.bing.com/images/create/", + "urlFilter": "https://www.instagram.com", "resourceTypes": ["xmlhttprequest"] } }, { "id": 5, "priority": 1, - "action": { - "type": "modifyHeaders", - "requestHeaders": [ - { - "header": "referer", - "operation": "set", - "value": "https://doutu.be/" - }, - { - "header": "origin", - "operation": "set", - "value": "https://doutu.be" - } - ] - }, - "condition": { - "domain": "extension://*", - "urlFilter": "https://||doutube.*/*", - "resourceTypes": ["xmlhttprequest"] - } - }, - { - "id": 6, - "priority": 1, "action": { "type": "modifyHeaders", "requestHeaders": [ @@ -144,49 +120,7 @@ } }, { - "id": 7, - "priority": 1, - "action": { - "type": "modifyHeaders", - "requestHeaders": [ - { - "header": "User-Agent", - "operation": "set", - "value": "com.ss.android.ugc.trill/2613 (Linux; U; Android 10; en_US; Pixel 4; Build/QQ3A.200805.001; Cronet/58.0.2991.0)" - }, - { - "header": "Sec-Fetch-Dest", - "operation": "set", - "value": "document" - }, - { - "header": "Sec-Fetch-Mode", - "operation": "set", - "value": "navigate" - }, - { - "header": "Sec-Fetch-Site", - "operation": "set", - "value": "cross-site" - }, - { - "header": "referer", - "operation": "remove" - }, - { - "header": "origin", - "operation": "remove" - } - ] - }, - "condition": { - "domain": "extension://*", - "urlFilter": "*://api22-normal-c-useast2a.tiktokv.com/*", - "resourceTypes": ["main_frame", "xmlhttprequest"] - } - }, - { - "id": 8, + "id": 6, "priority": 1, "action": { "type": "modifyHeaders", From 0e19332ec1f769fa6f3441ff168d9c814c548f52 Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Mon, 5 Aug 2024 22:39:33 +0700 Subject: [PATCH 19/43] fix --- scripts/ufs_statistic.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ufs_statistic.css b/scripts/ufs_statistic.css index 1491e44d..b44be618 100644 --- a/scripts/ufs_statistic.css +++ b/scripts/ufs_statistic.css @@ -42,7 +42,7 @@ li a img, transition: all 0.2s ease; } -li:hover img { +li a:hover img { transform: scale(3.5); z-index: 2; } From 78d3ffded66d8fab27ccb4d65c4edf590904a379 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Tue, 6 Aug 2024 11:48:21 +0700 Subject: [PATCH 20/43] update --- .../backup/ext/fb-group-ext/popup/index.html | 10 +- scripts/backup/ext/fb-group-ext/popup/main.js | 126 ++++++++++++------ .../backup/ext/fb-group-ext/popup/style.css | 51 ++----- 3 files changed, 108 insertions(+), 79 deletions(-) diff --git a/scripts/backup/ext/fb-group-ext/popup/index.html b/scripts/backup/ext/fb-group-ext/popup/index.html index 4a7d5369..e8c4bd55 100644 --- a/scripts/backup/ext/fb-group-ext/popup/index.html +++ b/scripts/backup/ext/fb-group-ext/popup/index.html @@ -22,9 +22,13 @@

Chọn hành động

-
-

Thời gian chờ:

- +
+

Thời gian chờ (giây):

+ (ngẫu nhiên trong khoảng)
+
+ ➡️ +

Duyệt tối đa bao nhiêu bài?

diff --git a/scripts/backup/ext/fb-group-ext/popup/main.js b/scripts/backup/ext/fb-group-ext/popup/main.js index e5891053..3d053e0a 100644 --- a/scripts/backup/ext/fb-group-ext/popup/main.js +++ b/scripts/backup/ext/fb-group-ext/popup/main.js @@ -1,21 +1,27 @@ -const sliderWaitTime = document.getElementById("slider-wait-time"); -const spanWaitTime = document.getElementById("wait-time"); +const waitMinInp = document.getElementById("inputWaitMin"); +const waitMaxInp = document.getElementById("inputWaitMax"); const inputMaxPosts = document.getElementById("max-posts"); const radioAction = document.getElementsByName("action"); const startBtn = document.getElementById("start-btn"); -sliderWaitTime.value = localStorage.getItem("wait-time") || 3000; -spanWaitTime.innerHTML = renderTime(sliderWaitTime.value); -sliderWaitTime.oninput = function () { - spanWaitTime.innerHTML = renderTime(this.value); - localStorage.setItem("wait-time", this.value); -}; +function initCacheInput(input, cacheName) { + if (localStorage.getItem(cacheName)) { + input.value = localStorage.getItem(cacheName); + } + input.addEventListener("input", () => { + localStorage.setItem(cacheName, input.value); + }); +} -function renderTime(time) { - return (time / 1000).toFixed(1) + "s"; +function renderTime(time, fixed = 1) { + return (time / 1000).toFixed(fixed) + "s"; } async function main() { + initCacheInput(waitMinInp, "wait-min"); + initCacheInput(waitMaxInp, "wait-max"); + initCacheInput(inputMaxPosts, "max-posts"); + const tab = await getCurrentTab(); if (!tab.url.includes("groups") || !tab.url.includes("spam")) { @@ -26,44 +32,70 @@ async function main() { } startBtn.addEventListener("click", async () => { + const state = await getCurrentState(tab); + if (state?.running) { + return stop(tab); + } + let action = radioAction[0].checked ? 1 : 2; - let max = parseInt(inputMaxPosts.value); - let wait = parseInt(sliderWaitTime.value); + let maxPosts = parseInt(inputMaxPosts.value); + let waitMin = parseInt(waitMinInp.value) * 1000; + let waitMax = parseInt(waitMaxInp.value) * 1000; + + if (waitMin > waitMax) { + return alert( + "Thời gian chờ không hợp lệ\nBên trái phải bé hơn hoặc bằng bên phải" + ); + } + runScriptInTab({ target: { tabId: tab.id }, func: start, - args: [action, max, wait], + args: [action, maxPosts, waitMin, waitMax], }); }); // check is running (async function checkIsRunning() { - const isRunning = await runScriptInTab({ - target: { tabId: tab.id }, - func: () => window.fb_group_ext_running, - }); - if (isRunning) { - // disable button - startBtn.disabled = true; - startBtn.innerHTML = "Đang xử lý..."; - startBtn.classList.add("disabled"); + const state = await getCurrentState(tab); + const { running, nextExecuteTime } = state || {}; + if (running) { + startBtn.innerHTML = + "Đang xử lý... (chờ " + + renderTime(nextExecuteTime - Date.now(), 0) + + ")
(Bấm để dừng)"; + startBtn.classList.add("running"); } else { - // enable button - startBtn.disabled = false; startBtn.innerHTML = "Bắt đầu"; - startBtn.classList.remove("disabled"); + startBtn.classList.remove("running"); } - setTimeout(checkIsRunning, 500); + setTimeout(checkIsRunning, 1000); })(); } -function start(action, max, wait) { +function getCurrentState(tab) { + return runScriptInTab({ + target: { tabId: tab.id }, + func: () => window.fb_group_ext, + }); +} + +function stop(tab) { + runScriptInTab({ + target: { tabId: tab.id }, + func: () => { + window.fb_group_ext.stop = true; + }, + }); +} + +function start(action, maxPosts, waitMin, waitMax) { const selector = action == 1 ? '[role="main"] [aria-label="Đăng"]' : '[role="main"] [aria-label="Từ chối"]'; - if (max == 0) max = Infinity; + if (maxPosts == 0) maxPosts = Infinity; const btns = Array.from(document.querySelectorAll(selector)); @@ -80,14 +112,14 @@ function start(action, max, wait) { }); async function main() { - window.fb_group_ext_running = true; - - // for test - // setTimeout(() => (window.fb_group_ext_running = false), 5000); - // return; + window.fb_group_ext = { + running: true, + nextExecuteTime: 0, + stop: false, + }; let counter = 0; - while (counter < max) { + while (counter < maxPosts && !window.fb_group_ext.stop) { if (btns.length > 0) { const btn = btns.shift(); @@ -96,18 +128,34 @@ function start(action, max, wait) { btn.click(); counter++; + const waitTime = ranInt(waitMin, waitMax); + window.fb_group_ext.nextExecuteTime = Date.now() + waitTime; + await sleep(waitTime, () => window.fb_group_ext.stop); + } else { + // wait for load more + await sleep(1000); } - - if (wait) await sleep(wait); } - window.fb_group_ext_running = false; + + window.fb_group_ext.running = false; alert("Duyệt xong " + counter + " bài"); } main(); - function sleep(time) { - return new Promise((resolve) => setTimeout(resolve, time)); + function ranInt(min, max) { + return Math.floor(Math.random() * (max - min) + min); + } + + function sleep(time, cancelFn) { + return new Promise((resolve) => { + setTimeout(resolve, time); + if (cancelFn) { + setInterval(() => { + if (cancelFn()) resolve(); + }, 100); + } + }); } function onElementsAdded(selector, callback, once) { diff --git a/scripts/backup/ext/fb-group-ext/popup/style.css b/scripts/backup/ext/fb-group-ext/popup/style.css index 1faef683..5b788e9e 100644 --- a/scripts/backup/ext/fb-group-ext/popup/style.css +++ b/scripts/backup/ext/fb-group-ext/popup/style.css @@ -79,41 +79,6 @@ h3 { /* ======================= Range ====================== */ -.slidecontainer { - width: 100%; -} - -.slider { - -webkit-appearance: none; - width: 100%; - height: 25px; - background: #d3d3d3; - outline: none; - opacity: 0.7; - -webkit-transition: .2s; - transition: opacity .2s; -} - -.slider:hover { - opacity: 1; -} - -.slider::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 25px; - height: 25px; - background: #04AA6D; - cursor: pointer; -} - -.slider::-moz-range-thumb { - width: 25px; - height: 25px; - background: #04AA6D; - cursor: pointer; -} - input[type=number] { padding: 5px; font-size: large; @@ -133,7 +98,19 @@ input[type=number] { margin-top: 30px; } -.button.disabled { +.button.running { background-color: rgb(151, 12, 12); - cursor: not-allowed; + /* cursor: not-allowed; */ +} + +.input-row { + display: flex; + justify-content: space-between; + align-items: center; + flex-direction: row; + white-space: pre; +} + +.input-row input[type=number] { + /* max-width: 80px; */ } From 279716429f827010123dfd8c5e7af505ef58fcba Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Tue, 6 Aug 2024 18:28:40 +0700 Subject: [PATCH 21/43] . --- scripts/backup/ext/fb-group-ext/popup/main.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/backup/ext/fb-group-ext/popup/main.js b/scripts/backup/ext/fb-group-ext/popup/main.js index 3d053e0a..2a4dc55d 100644 --- a/scripts/backup/ext/fb-group-ext/popup/main.js +++ b/scripts/backup/ext/fb-group-ext/popup/main.js @@ -132,6 +132,8 @@ function start(action, maxPosts, waitMin, waitMax) { window.fb_group_ext.nextExecuteTime = Date.now() + waitTime; await sleep(waitTime, () => window.fb_group_ext.stop); } else { + // scroll to end + window.scrollTo(0, document.body.scrollHeight); // wait for load more await sleep(1000); } From 745856cc572fb4312c3403a2ab7f67c47ee716df Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Tue, 6 Aug 2024 22:18:33 +0700 Subject: [PATCH 22/43] refactor insta --- popup/recommend.js | 17 +++++++++++++++++ popup/tabs.js | 4 ++-- scripts/insta_GLOBAL.js | 2 ++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/popup/recommend.js b/popup/recommend.js index 4fc8e136..36992bed 100644 --- a/popup/recommend.js +++ b/popup/recommend.js @@ -886,4 +886,21 @@ export const Recommend = { ), }, }, + insta_bulkDownload: { + id: "recommend_fbAIOInstagram", + icon: "https://static.cdninstagram.com/rsrc.php/v3/yI/r/VsNE-OHk_8a.png", + name: { + en: "Instagram - Bulk download", + vi: "Instagram - Tải hàng loạt", + }, + description: { + en: "Download all user's media on instagram (video/photo/reels/highlight)", + vi: "Tải mọi ảnh/video/reel/highlight của người dùng Instagram", + }, + badges: [BADGES.new, BADGES.hot], + popupScript: { + onClick: () => + window.open("https://facebook-all-in-one.com/dist/#/bulk-downloader"), + }, + }, }; diff --git a/popup/tabs.js b/popup/tabs.js index 4b8881fb..a13dff1c 100644 --- a/popup/tabs.js +++ b/popup/tabs.js @@ -168,11 +168,11 @@ const tabs = [ { ...CATEGORY.instagram, scripts: [ + R.insta_bulkDownload, + // s.insta_getAllUserMedia, s.insta_getUserInfo, s.insta_injectDownloadBtn, s.insta_anonymousStoryViewer, - createTitle("--- Bulk Download ---", "--- Tải hàng loạt ---"), - s.insta_getAllUserMedia, s.insta_getFollowForOther, ], }, diff --git a/scripts/insta_GLOBAL.js b/scripts/insta_GLOBAL.js index c7fc4d7b..dc07aee4 100644 --- a/scripts/insta_GLOBAL.js +++ b/scripts/insta_GLOBAL.js @@ -25,6 +25,8 @@ export function getUniversalCdnUrl(cdnLink) { return cdnLink; } } + +// WARNING: not working anymore?? export async function getAllMedia({ uid, progressCallback, limit = 0 }) { let all_urls = []; let after = ""; From 13728196da0ffa76bbac4ffd5bc2e1d5766daa99 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 7 Aug 2024 09:52:52 +0700 Subject: [PATCH 23/43] fix fb avatar expired --- scripts/ufs_statistic.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/scripts/ufs_statistic.js b/scripts/ufs_statistic.js index 8a5ff73f..f4f06ca8 100644 --- a/scripts/ufs_statistic.js +++ b/scripts/ufs_statistic.js @@ -533,6 +533,26 @@ async function onDocumentEnd() { .querySelectorAll(`[data-profile-avatar="${uid}"]`) .forEach((el) => { el.src = info.avatar.replace(/\\\//g, "/") || el.src; + + let tried = 0, + loading = false; + + // in case cached avatqr expired + el.onerror = () => { + if (loading) return; + tried++; + if (tried > 3) el.src = getUserAvatarFromUid(uid); + else { + loading = true; + getFbProfile(uid, true) + .then((info) => { + el.src = info.avatar.replace(/\\\//g, "/") || el.src; + }) + .finally(() => { + loading = false; + }); + } + }; }); }) ); @@ -654,8 +674,8 @@ async function initCache() { } } -async function getFbProfile(uid) { - if (CACHED.fbProfile.has(uid)) return CACHED.fbProfile.get(uid); +async function getFbProfile(uid, force = false) { + if (CACHED.fbProfile.has(uid) && !force) return CACHED.fbProfile.get(uid); const variables = { userID: uid, From 619382c075ea0618b4aa4b30aeb6010673af08b2 Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Wed, 7 Aug 2024 13:05:21 +0700 Subject: [PATCH 24/43] hin --- .../generated_indexed_rulesets/_ruleset1 | Bin 0 -> 1811 bytes .../popup/auto_duyet_bai/index.html | 43 ++ .../popup/{ => auto_duyet_bai}/main.js | 474 +++++++++--------- .../popup/{ => auto_duyet_bai}/style.css | 232 ++++----- .../backup/ext/fb-group-ext/popup/index.html | 52 +- 5 files changed, 418 insertions(+), 383 deletions(-) create mode 100644 _metadata/generated_indexed_rulesets/_ruleset1 create mode 100644 scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/index.html rename scripts/backup/ext/fb-group-ext/popup/{ => auto_duyet_bai}/main.js (96%) rename scripts/backup/ext/fb-group-ext/popup/{ => auto_duyet_bai}/style.css (94%) diff --git a/_metadata/generated_indexed_rulesets/_ruleset1 b/_metadata/generated_indexed_rulesets/_ruleset1 new file mode 100644 index 0000000000000000000000000000000000000000..9c3f4a785ad68c59c7777c17c2897f42c9697d7e GIT binary patch literal 1811 zcmbtUJ!=$E6uqOP8Dt3yuDXiF6k@TMnUJKAfTa+u771b@#7Ug&4$008Gn0>>U|}sH z7AZw6EK*pc2x5_P%M>YsRSHWB3w1o_&3ihU4M==&_I=EG_n!Olc49)02Vs^+NqlE| zsx2aSAI{$w8JD)$;z&c9(!x%}u|#GtH@OBHfEnkp*guCe;HfQQ1CwV(HqVG)l4rmi zfMfamy(~dfb|&>1CS}vbd>x>U7C>M0Lw~$`h`YCboc_LxyQ}|k|1s_|o=3<<1E8jz zn!PVtCnlwR5Az{#0PF)VfFY0q{MH3_fk}YzZUgUtx4>&)t1L^#pQlC9#|)R!Lh|10 zbAJ|J^!Yr54b3?}=<^-a;7Fg7Zuke_J5cGUI#+ovR-(ew_Zqn8K$ZF4#Ch(+FlG*F zY2&AW?OousHQiR)T1o}Gjqw^=Ggvdn91d^;aDkJ1#8&)|-^vm+n|(=p#s)%CJ{ zieoWv+_!*!QKb2-@8`jKxE?(T6K@dWSUzZOS995)qqu$;`{Z{K)41iT9-s!5EKHYy zk@q@yE9h-}wi)|Ln(tK{YYTx;bKTy?#)el!D@C&6EhPhyb{z zs@jlm{Bn5}Fu7zrQJfb+FAD}$__C+{qhu%gf2sX-wf{GI-`D;(x!3+_r|!Sr1N6Tf zEQO1S$v)yo58#H)p8LC=zv8br)({-_Uq|nccoDb=m>BaoNKs!eNK@pdjXG8J0;M*7 z*NeLOX6fJP=DQhT&kq)tro6PDlFI*kN5;5>?xwPVrHzyvi-R?z&QDr5-{@_C%91SV zMKKm~0XD{9vw@Ww7J1Z0FIl)8W??3mur}J(;=G?^MHqWg!3X8y#s5pZ%prA}c)7d@ WjI=ulqS)_+#lmV5_ZHT&Rgs^Sk{fXV literal 0 HcmV?d00001 diff --git a/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/index.html b/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/index.html new file mode 100644 index 00000000..a3e6e118 --- /dev/null +++ b/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/index.html @@ -0,0 +1,43 @@ + + + + + + + Quản lý nhóm Facebook + + + + + +

Auto duyệt bài spam group fb

+ +

Chọn hành động

+ + + +
+

Thời gian chờ (giây):

+ (ngẫu nhiên trong khoảng)
+
+ ➡️ +
+
+ +

Duyệt tối đa bao nhiêu bài?

+ (nhập 0 để duyệt hết)
+ + + + + + + + diff --git a/scripts/backup/ext/fb-group-ext/popup/main.js b/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/main.js similarity index 96% rename from scripts/backup/ext/fb-group-ext/popup/main.js rename to scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/main.js index 2a4dc55d..4b58e5a9 100644 --- a/scripts/backup/ext/fb-group-ext/popup/main.js +++ b/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/main.js @@ -1,237 +1,237 @@ -const waitMinInp = document.getElementById("inputWaitMin"); -const waitMaxInp = document.getElementById("inputWaitMax"); -const inputMaxPosts = document.getElementById("max-posts"); -const radioAction = document.getElementsByName("action"); -const startBtn = document.getElementById("start-btn"); - -function initCacheInput(input, cacheName) { - if (localStorage.getItem(cacheName)) { - input.value = localStorage.getItem(cacheName); - } - input.addEventListener("input", () => { - localStorage.setItem(cacheName, input.value); - }); -} - -function renderTime(time, fixed = 1) { - return (time / 1000).toFixed(fixed) + "s"; -} - -async function main() { - initCacheInput(waitMinInp, "wait-min"); - initCacheInput(waitMaxInp, "wait-max"); - initCacheInput(inputMaxPosts, "max-posts"); - - const tab = await getCurrentTab(); - - if (!tab.url.includes("groups") || !tab.url.includes("spam")) { - return prompt( - "Bạn cần mở trang duyệt bài spam của group trước. Ví dụ:", - "https://www.facebook.com/groups/gamecode/spam" - ); - } - - startBtn.addEventListener("click", async () => { - const state = await getCurrentState(tab); - if (state?.running) { - return stop(tab); - } - - let action = radioAction[0].checked ? 1 : 2; - let maxPosts = parseInt(inputMaxPosts.value); - let waitMin = parseInt(waitMinInp.value) * 1000; - let waitMax = parseInt(waitMaxInp.value) * 1000; - - if (waitMin > waitMax) { - return alert( - "Thời gian chờ không hợp lệ\nBên trái phải bé hơn hoặc bằng bên phải" - ); - } - - runScriptInTab({ - target: { tabId: tab.id }, - func: start, - args: [action, maxPosts, waitMin, waitMax], - }); - }); - - // check is running - (async function checkIsRunning() { - const state = await getCurrentState(tab); - const { running, nextExecuteTime } = state || {}; - if (running) { - startBtn.innerHTML = - "Đang xử lý... (chờ " + - renderTime(nextExecuteTime - Date.now(), 0) + - ")
(Bấm để dừng)"; - startBtn.classList.add("running"); - } else { - startBtn.innerHTML = "Bắt đầu"; - startBtn.classList.remove("running"); - } - setTimeout(checkIsRunning, 1000); - })(); -} - -function getCurrentState(tab) { - return runScriptInTab({ - target: { tabId: tab.id }, - func: () => window.fb_group_ext, - }); -} - -function stop(tab) { - runScriptInTab({ - target: { tabId: tab.id }, - func: () => { - window.fb_group_ext.stop = true; - }, - }); -} - -function start(action, maxPosts, waitMin, waitMax) { - const selector = - action == 1 - ? '[role="main"] [aria-label="Đăng"]' - : '[role="main"] [aria-label="Từ chối"]'; - - if (maxPosts == 0) maxPosts = Infinity; - - const btns = Array.from(document.querySelectorAll(selector)); - - if (!btns.length) { - alert("Không tìm thấy bài nào"); - return; - } - - onElementsAdded(selector, (nodes) => { - for (let node of nodes) { - if (btns.includes(node)) continue; - btns.push(node); - } - }); - - async function main() { - window.fb_group_ext = { - running: true, - nextExecuteTime: 0, - stop: false, - }; - - let counter = 0; - while (counter < maxPosts && !window.fb_group_ext.stop) { - if (btns.length > 0) { - const btn = btns.shift(); - - btn.scrollIntoView({ block: "center", behavior: "smooth" }); - console.log("click", btn); - btn.click(); - - counter++; - const waitTime = ranInt(waitMin, waitMax); - window.fb_group_ext.nextExecuteTime = Date.now() + waitTime; - await sleep(waitTime, () => window.fb_group_ext.stop); - } else { - // scroll to end - window.scrollTo(0, document.body.scrollHeight); - // wait for load more - await sleep(1000); - } - } - - window.fb_group_ext.running = false; - alert("Duyệt xong " + counter + " bài"); - } - - main(); - - function ranInt(min, max) { - return Math.floor(Math.random() * (max - min) + min); - } - - function sleep(time, cancelFn) { - return new Promise((resolve) => { - setTimeout(resolve, time); - if (cancelFn) { - setInterval(() => { - if (cancelFn()) resolve(); - }, 100); - } - }); - } - - function onElementsAdded(selector, callback, once) { - let nodes = document.querySelectorAll(selector); - if (nodes?.length) { - callback(nodes); - if (once) return; - } - - const observer = new MutationObserver((mutations) => { - mutations.forEach((mutation) => { - if (!mutation.addedNodes) return; - - for (let node of mutation.addedNodes) { - if (node.nodeType != 1) continue; // only process Node.ELEMENT_NODE - - let n = node.matches(selector) - ? [node] - : Array.from(node.querySelectorAll(selector)); - - if (n?.length) { - callback(n); - if (once) observer.disconnect(); - } - } - }); - }); - - observer.observe(document, { - childList: true, - subtree: true, - attributes: false, - characterData: false, - }); - - // return disconnect function - return () => observer.disconnect(); - } -} - -async function getCurrentTab() { - let tabs = await chrome.tabs.query({ - active: true, - currentWindow: true, - }); - return tabs[0]; -} - -const runScriptInTab = async (config = {}) => { - return new Promise((resolve, reject) => { - chrome.scripting.executeScript( - mergeObject( - { - world: "MAIN", - injectImmediately: true, - }, - config - ), - (injectionResults) => { - if (chrome.runtime.lastError) { - console.error(chrome.runtime.lastError); - reject(chrome.runtime.lastError); - } - // https://developer.chrome.com/docs/extensions/reference/scripting/#handling-results - else resolve(injectionResults?.find?.((_) => _.result)?.result); - } - ); - }); -}; -const mergeObject = (...objs) => { - // merge without null value - let res = {}; - for (let obj of objs) for (let key in obj) if (obj[key]) res[key] = obj[key]; - return res; -}; - -main(); +const waitMinInp = document.getElementById("inputWaitMin"); +const waitMaxInp = document.getElementById("inputWaitMax"); +const inputMaxPosts = document.getElementById("max-posts"); +const radioAction = document.getElementsByName("action"); +const startBtn = document.getElementById("start-btn"); + +function initCacheInput(input, cacheName) { + if (localStorage.getItem(cacheName)) { + input.value = localStorage.getItem(cacheName); + } + input.addEventListener("input", () => { + localStorage.setItem(cacheName, input.value); + }); +} + +function renderTime(time, fixed = 1) { + return (time / 1000).toFixed(fixed) + "s"; +} + +async function main() { + initCacheInput(waitMinInp, "wait-min"); + initCacheInput(waitMaxInp, "wait-max"); + initCacheInput(inputMaxPosts, "max-posts"); + + const tab = await getCurrentTab(); + + if (!tab.url.includes("groups") || !tab.url.includes("spam")) { + return prompt( + "Bạn cần mở trang duyệt bài spam của group trước. Ví dụ:", + "https://www.facebook.com/groups/gamecode/spam" + ); + } + + startBtn.addEventListener("click", async () => { + const state = await getCurrentState(tab); + if (state?.running) { + return stop(tab); + } + + let action = radioAction[0].checked ? 1 : 2; + let maxPosts = parseInt(inputMaxPosts.value); + let waitMin = parseInt(waitMinInp.value) * 1000; + let waitMax = parseInt(waitMaxInp.value) * 1000; + + if (waitMin > waitMax) { + return alert( + "Thời gian chờ không hợp lệ\nBên trái phải bé hơn hoặc bằng bên phải" + ); + } + + runScriptInTab({ + target: { tabId: tab.id }, + func: start, + args: [action, maxPosts, waitMin, waitMax], + }); + }); + + // check is running + (async function checkIsRunning() { + const state = await getCurrentState(tab); + const { running, nextExecuteTime } = state || {}; + if (running) { + startBtn.innerHTML = + "Đang xử lý... (chờ " + + renderTime(nextExecuteTime - Date.now(), 0) + + ")
(Bấm để dừng)"; + startBtn.classList.add("running"); + } else { + startBtn.innerHTML = "Bắt đầu"; + startBtn.classList.remove("running"); + } + setTimeout(checkIsRunning, 1000); + })(); +} + +function getCurrentState(tab) { + return runScriptInTab({ + target: { tabId: tab.id }, + func: () => window.fb_group_ext, + }); +} + +function stop(tab) { + runScriptInTab({ + target: { tabId: tab.id }, + func: () => { + window.fb_group_ext.stop = true; + }, + }); +} + +function start(action, maxPosts, waitMin, waitMax) { + const selector = + action == 1 + ? '[role="main"] [aria-label="Đăng"]' + : '[role="main"] [aria-label="Từ chối"]'; + + if (maxPosts == 0) maxPosts = Infinity; + + const btns = Array.from(document.querySelectorAll(selector)); + + if (!btns.length) { + alert("Không tìm thấy bài nào"); + return; + } + + onElementsAdded(selector, (nodes) => { + for (let node of nodes) { + if (btns.includes(node)) continue; + btns.push(node); + } + }); + + async function main() { + window.fb_group_ext = { + running: true, + nextExecuteTime: 0, + stop: false, + }; + + let counter = 0; + while (counter < maxPosts && !window.fb_group_ext.stop) { + if (btns.length > 0) { + const btn = btns.shift(); + + btn.scrollIntoView({ block: "center", behavior: "smooth" }); + console.log("click", btn); + btn.click(); + + counter++; + const waitTime = ranInt(waitMin, waitMax); + window.fb_group_ext.nextExecuteTime = Date.now() + waitTime; + await sleep(waitTime, () => window.fb_group_ext.stop); + } else { + // scroll to end + window.scrollTo(0, document.body.scrollHeight); + // wait for load more + await sleep(1000); + } + } + + window.fb_group_ext.running = false; + alert("Duyệt xong " + counter + " bài"); + } + + main(); + + function ranInt(min, max) { + return Math.floor(Math.random() * (max - min) + min); + } + + function sleep(time, cancelFn) { + return new Promise((resolve) => { + setTimeout(resolve, time); + if (cancelFn) { + setInterval(() => { + if (cancelFn()) resolve(); + }, 100); + } + }); + } + + function onElementsAdded(selector, callback, once) { + let nodes = document.querySelectorAll(selector); + if (nodes?.length) { + callback(nodes); + if (once) return; + } + + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (!mutation.addedNodes) return; + + for (let node of mutation.addedNodes) { + if (node.nodeType != 1) continue; // only process Node.ELEMENT_NODE + + let n = node.matches(selector) + ? [node] + : Array.from(node.querySelectorAll(selector)); + + if (n?.length) { + callback(n); + if (once) observer.disconnect(); + } + } + }); + }); + + observer.observe(document, { + childList: true, + subtree: true, + attributes: false, + characterData: false, + }); + + // return disconnect function + return () => observer.disconnect(); + } +} + +async function getCurrentTab() { + let tabs = await chrome.tabs.query({ + active: true, + currentWindow: true, + }); + return tabs[0]; +} + +const runScriptInTab = async (config = {}) => { + return new Promise((resolve, reject) => { + chrome.scripting.executeScript( + mergeObject( + { + world: "MAIN", + injectImmediately: true, + }, + config + ), + (injectionResults) => { + if (chrome.runtime.lastError) { + console.error(chrome.runtime.lastError); + reject(chrome.runtime.lastError); + } + // https://developer.chrome.com/docs/extensions/reference/scripting/#handling-results + else resolve(injectionResults?.find?.((_) => _.result)?.result); + } + ); + }); +}; +const mergeObject = (...objs) => { + // merge without null value + let res = {}; + for (let obj of objs) for (let key in obj) if (obj[key]) res[key] = obj[key]; + return res; +}; + +main(); diff --git a/scripts/backup/ext/fb-group-ext/popup/style.css b/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/style.css similarity index 94% rename from scripts/backup/ext/fb-group-ext/popup/style.css rename to scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/style.css index 5b788e9e..ed4f3892 100644 --- a/scripts/backup/ext/fb-group-ext/popup/style.css +++ b/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/style.css @@ -1,116 +1,116 @@ -body { - min-width: 350px; - min-height: 200px; - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - font-size: 16px; -} - - -h3 { - text-decoration: underline; - margin-bottom: 8px; -} - - -/* The container */ -.container { - display: block; - position: relative; - padding-left: 35px; - margin-bottom: 12px; - cursor: pointer; - font-size: 22px; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -/* Hide the browser's default radio button */ -.container input { - position: absolute; - opacity: 0; - cursor: pointer; -} - -/* Create a custom radio button */ -.checkmark { - position: absolute; - top: 0; - left: 0; - height: 25px; - width: 25px; - background-color: #eee; - border-radius: 50%; -} - -/* On mouse-over, add a grey background color */ -.container:hover input~.checkmark { - background-color: #ccc; -} - -/* When the radio button is checked, add a blue background */ -.container input:checked~.checkmark { - background-color: #2196F3; -} - -/* Create the indicator (the dot/circle - hidden when not checked) */ -.checkmark:after { - content: ""; - position: absolute; - display: none; -} - -/* Show the indicator (dot/circle) when checked */ -.container input:checked~.checkmark:after { - display: block; -} - -/* Style the indicator (dot/circle) */ -.container .checkmark:after { - top: 9px; - left: 9px; - width: 8px; - height: 8px; - border-radius: 50%; - background: white; -} - - -/* ======================= Range ====================== */ - -input[type=number] { - padding: 5px; - font-size: large; -} - -.button { - background-color: #04AA6D; - border: none; - color: white; - padding: 15px 32px; - text-align: center; - text-decoration: none; - display: inline-block; - font-size: 16px; - cursor: pointer; - width: 100%; - margin-top: 30px; -} - -.button.running { - background-color: rgb(151, 12, 12); - /* cursor: not-allowed; */ -} - -.input-row { - display: flex; - justify-content: space-between; - align-items: center; - flex-direction: row; - white-space: pre; -} - -.input-row input[type=number] { - /* max-width: 80px; */ -} +body { + min-width: 350px; + min-height: 200px; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-size: 16px; +} + + +h3 { + text-decoration: underline; + margin-bottom: 8px; +} + + +/* The container */ +.container { + display: block; + position: relative; + padding-left: 35px; + margin-bottom: 12px; + cursor: pointer; + font-size: 22px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* Hide the browser's default radio button */ +.container input { + position: absolute; + opacity: 0; + cursor: pointer; +} + +/* Create a custom radio button */ +.checkmark { + position: absolute; + top: 0; + left: 0; + height: 25px; + width: 25px; + background-color: #eee; + border-radius: 50%; +} + +/* On mouse-over, add a grey background color */ +.container:hover input~.checkmark { + background-color: #ccc; +} + +/* When the radio button is checked, add a blue background */ +.container input:checked~.checkmark { + background-color: #2196F3; +} + +/* Create the indicator (the dot/circle - hidden when not checked) */ +.checkmark:after { + content: ""; + position: absolute; + display: none; +} + +/* Show the indicator (dot/circle) when checked */ +.container input:checked~.checkmark:after { + display: block; +} + +/* Style the indicator (dot/circle) */ +.container .checkmark:after { + top: 9px; + left: 9px; + width: 8px; + height: 8px; + border-radius: 50%; + background: white; +} + + +/* ======================= Range ====================== */ + +input[type=number] { + padding: 5px; + font-size: large; +} + +.button { + background-color: #04AA6D; + border: none; + color: white; + padding: 15px 32px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + cursor: pointer; + width: 100%; + margin-top: 30px; +} + +.button.running { + background-color: rgb(151, 12, 12); + /* cursor: not-allowed; */ +} + +.input-row { + display: flex; + justify-content: space-between; + align-items: center; + flex-direction: row; + white-space: pre; +} + +.input-row input[type=number] { + /* max-width: 80px; */ +} diff --git a/scripts/backup/ext/fb-group-ext/popup/index.html b/scripts/backup/ext/fb-group-ext/popup/index.html index e8c4bd55..708c4784 100644 --- a/scripts/backup/ext/fb-group-ext/popup/index.html +++ b/scripts/backup/ext/fb-group-ext/popup/index.html @@ -4,40 +4,32 @@ - Quản lý nhóm Facebook - - + Document + + -

Facebook Tools

- -

Chọn hành động

- - - -
-

Thời gian chờ (giây):

- (ngẫu nhiên trong khoảng)
-
- ➡️ -
-
- -

Duyệt tối đa bao nhiêu bài?

- (nhập 0 để duyệt hết)
- - - + Auto duyệt bài group - From 866a756d85305d9417af489611d1879a7cff50fe Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 7 Aug 2024 13:07:57 +0700 Subject: [PATCH 25/43] clear interval --- .../ext/fb-group-ext/popup/auto_duyet_bai/main.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/main.js b/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/main.js index 4b58e5a9..c50b404f 100644 --- a/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/main.js +++ b/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/main.js @@ -151,10 +151,14 @@ function start(action, maxPosts, waitMin, waitMax) { function sleep(time, cancelFn) { return new Promise((resolve) => { - setTimeout(resolve, time); + const timeout = setTimeout(resolve, time); if (cancelFn) { - setInterval(() => { - if (cancelFn()) resolve(); + const interval = setInterval(() => { + if (cancelFn()) { + clearInterval(interval); + clearTimeout(timeout); + resolve(); + } }, 100); } }); From 67f1b7c139aeea26d47b4c38e2eaf0d162b0bf14 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 7 Aug 2024 13:48:34 +0700 Subject: [PATCH 26/43] WIP --- .../index.html | 0 .../main.js | 0 .../style.css | 0 .../popup/auto_gioihan_member/index.html | 14 ++++++++++++++ scripts/backup/ext/fb-group-ext/popup/index.html | 4 +++- 5 files changed, 17 insertions(+), 1 deletion(-) rename scripts/backup/ext/fb-group-ext/popup/{auto_duyet_bai => auto_duyet_bai_group}/index.html (100%) rename scripts/backup/ext/fb-group-ext/popup/{auto_duyet_bai => auto_duyet_bai_group}/main.js (100%) rename scripts/backup/ext/fb-group-ext/popup/{auto_duyet_bai => auto_duyet_bai_group}/style.css (100%) create mode 100644 scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/index.html diff --git a/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/index.html b/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai_group/index.html similarity index 100% rename from scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/index.html rename to scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai_group/index.html diff --git a/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/main.js b/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai_group/main.js similarity index 100% rename from scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/main.js rename to scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai_group/main.js diff --git a/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/style.css b/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai_group/style.css similarity index 100% rename from scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai/style.css rename to scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai_group/style.css diff --git a/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/index.html b/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/index.html new file mode 100644 index 00000000..3a044e4b --- /dev/null +++ b/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/index.html @@ -0,0 +1,14 @@ + + + + + + + Auto giới hạn members + + + +

Auto giới hạn members

+ + + diff --git a/scripts/backup/ext/fb-group-ext/popup/index.html b/scripts/backup/ext/fb-group-ext/popup/index.html index 708c4784..7040d401 100644 --- a/scripts/backup/ext/fb-group-ext/popup/index.html +++ b/scripts/backup/ext/fb-group-ext/popup/index.html @@ -28,8 +28,10 @@ - Auto duyệt bài group +

Faecbook tools

+ 📝 Auto duyệt bài group + 🙋🏻‍♂️ Auto giới hạn members From 2016be08dfa79ac510fa90bf6e928ed00fd10340 Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Thu, 8 Aug 2024 04:00:53 +0700 Subject: [PATCH 27/43] WIP --- .../popup/auto_gioihan_member/index.html | 2 + .../popup/auto_gioihan_member/main.js | 33 ++++++++ .../fb-group-ext/popup/helpers/facebook.js | 82 +++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/main.js create mode 100644 scripts/backup/ext/fb-group-ext/popup/helpers/facebook.js diff --git a/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/index.html b/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/index.html index 3a044e4b..2eb383a8 100644 --- a/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/index.html +++ b/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/index.html @@ -9,6 +9,8 @@

Auto giới hạn members

+ + diff --git a/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/main.js b/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/main.js new file mode 100644 index 00000000..4727f977 --- /dev/null +++ b/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/main.js @@ -0,0 +1,33 @@ +import { fetchGraphQl } from "../helpers/facebook"; + +async function getPendingPosts(groupId, cursor = "") { + const res = await fetchGraphQl({ + fb_api_req_friendly_name: "GroupsCometPendingPostsFeedPaginationQuery", + variables: { + count: 3, + cursor: cursor, + feedLocation: "GROUP_PENDING", + feedbackSource: 0, + focusCommentID: null, + hoistedPostID: null, + pendingStoriesOrderBy: null, + privacySelectorRenderLocation: "COMET_STREAM", + renderLocation: "group_pending_queue", + scale: 1, + useDefaultActor: false, + id: groupId, + __relay_internal__pv__CometImmersivePhotoCanUserDisable3DMotionrelayprovider: false, + __relay_internal__pv__IsWorkUserrelayprovider: false, + __relay_internal__pv__IsMergQAPollsrelayprovider: false, + __relay_internal__pv__CometUFIReactionsEnableShortNamerelayprovider: false, + __relay_internal__pv__CometUFIShareActionMigrationrelayprovider: true, + __relay_internal__pv__IncludeCommentWithAttachmentrelayprovider: true, + __relay_internal__pv__StoriesArmadilloReplyEnabledrelayprovider: true, + __relay_internal__pv__EventCometCardImage_prefetchEventImagerelayprovider: false, + }, + doc_id: "8078135725563420", + }); + console.log(res); +} + +getPendingPosts(); diff --git a/scripts/backup/ext/fb-group-ext/popup/helpers/facebook.js b/scripts/backup/ext/fb-group-ext/popup/helpers/facebook.js new file mode 100644 index 00000000..90ec38dd --- /dev/null +++ b/scripts/backup/ext/fb-group-ext/popup/helpers/facebook.js @@ -0,0 +1,82 @@ +const CACHED = { + fb_dtsg: null, +}; + +export async function fetchGraphQl(params, url) { + let query = ""; + if (typeof params === "string") query = "&q=" + encodeURIComponent(params); + else + query = wrapGraphQlParams({ + dpr: 1, + __a: 1, + __aaid: 0, + __ccg: "GOOD", + server_timestamps: true, + ...params, + }); + + const res = await fetch(url || "https://www.facebook.com/api/graphql/", { + body: query + "&fb_dtsg=" + (await getFbDtsg()), + method: "POST", + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + credentials: "include", + }); + const text = await res.text(); + + // check error response + try { + const json = JSON.parse(text); + if (json.errors) { + const { summary, message, description_raw } = json.errors[0]; + if (summary) { + console.log(json); + + const div = document.createElement("div"); + div.innerHTML = description_raw?.__html; + const description = div.innerText; + + // notification.error({ + // message: i18n.t("Facebook response Error"), + // description: summary + ". " + message + ". " + description, + // duration: 0, + // }); + } + } + } catch (e) {} + + return text; +} + +export function wrapGraphQlParams(params = {}) { + const formBody = []; + for (const property in params) { + const encodedKey = encodeURIComponent(property); + const value = + typeof params[property] === "string" + ? params[property] + : JSON.stringify(params[property]); + const encodedValue = encodeURIComponent(value); + formBody.push(encodedKey + "=" + encodedValue); + } + return formBody.join("&"); +} + +export async function getFbDtsg() { + if (CACHED.fb_dtsg) return CACHED.fb_dtsg; + let res = await fetch("https://mbasic.facebook.com/photos/upload/"); + let text = await res.text(); + let dtsg = RegExp(/name="fb_dtsg" value="(.*?)"/).exec(text)?.[1]; + if (!dtsg) { + res = await fetch("https://m.facebook.com/home.php", { + headers: { + Accept: "text/html", + }, + }); + text = res.text(); + dtsg = + RegExp(/"dtsg":{"token":"([^"]+)"/).exec(text)?.[1] || + RegExp(/"name":"fb_dtsg","value":"([^"]+)/).exec(text)?.[1]; + } + CACHED.fb_dtsg = dtsg || null; + return CACHED.fb_dtsg; +} From 14f68c8882c4c449c37982d57ee84fc4b85a390a Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Thu, 8 Aug 2024 13:39:46 +0700 Subject: [PATCH 28/43] more info --- .../popup/auto_duyet_bai_group/main.js | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai_group/main.js b/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai_group/main.js index c50b404f..6405419a 100644 --- a/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai_group/main.js +++ b/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai_group/main.js @@ -26,7 +26,9 @@ async function main() { if (!tab.url.includes("groups") || !tab.url.includes("spam")) { return prompt( - "Bạn cần mở trang duyệt bài spam của group trước. Ví dụ:", + "\n\nBạn cần mở trang duyệt bài spam của group trước.\n\nLink hiện tại:" + + tab.url + + "\nLink đúng ví dụ:", "https://www.facebook.com/groups/gamecode/spam" ); } @@ -58,10 +60,14 @@ async function main() { // check is running (async function checkIsRunning() { const state = await getCurrentState(tab); - const { running, nextExecuteTime } = state || {}; + const { running, nextExecuteTime, count } = state || {}; if (running) { startBtn.innerHTML = - "Đang xử lý... (chờ " + + "Đang " + + (radioAction[0].checked ? "đăng" : "xoá") + + "... " + + count + + " bài (chờ " + renderTime(nextExecuteTime - Date.now(), 0) + ")
(Bấm để dừng)"; startBtn.classList.add("running"); @@ -116,10 +122,10 @@ function start(action, maxPosts, waitMin, waitMax) { running: true, nextExecuteTime: 0, stop: false, + count: 0, }; - let counter = 0; - while (counter < maxPosts && !window.fb_group_ext.stop) { + while (window.fb_group_ext.count < maxPosts && !window.fb_group_ext.stop) { if (btns.length > 0) { const btn = btns.shift(); @@ -127,7 +133,7 @@ function start(action, maxPosts, waitMin, waitMax) { console.log("click", btn); btn.click(); - counter++; + window.fb_group_ext.count++; const waitTime = ranInt(waitMin, waitMax); window.fb_group_ext.nextExecuteTime = Date.now() + waitTime; await sleep(waitTime, () => window.fb_group_ext.stop); @@ -140,7 +146,7 @@ function start(action, maxPosts, waitMin, waitMax) { } window.fb_group_ext.running = false; - alert("Duyệt xong " + counter + " bài"); + alert("Duyệt xong " + window.fb_group_ext.count + " bài"); } main(); From 4d7e01f0f5d6afd31e19997635a67eaa22dbdb89 Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Thu, 8 Aug 2024 23:26:02 +0700 Subject: [PATCH 29/43] WIP --- .../generated_indexed_rulesets/_ruleset1 | Bin 0 -> 763 bytes scripts/backup/ext/fb-group-ext/manifest.json | 20 ++- .../popup/auto_duyet_bai_group/main.js | 5 +- .../popup/auto_gioihan_member/index.html | 32 +++++ .../popup/auto_gioihan_member/main.js | 126 +++++++++++++++++- .../popup/auto_gioihan_member/style.css | 0 .../fb-group-ext/popup/helpers/facebook.js | 25 ++++ .../ext/fb-group-ext/popup/helpers/utils.js | 5 + .../backup/ext/fb-group-ext/popup/index.html | 2 +- scripts/backup/ext/fb-group-ext/rules.json | 26 ++++ 10 files changed, 233 insertions(+), 8 deletions(-) create mode 100644 scripts/backup/ext/fb-group-ext/_metadata/generated_indexed_rulesets/_ruleset1 create mode 100644 scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/style.css create mode 100644 scripts/backup/ext/fb-group-ext/popup/helpers/utils.js create mode 100644 scripts/backup/ext/fb-group-ext/rules.json diff --git a/scripts/backup/ext/fb-group-ext/_metadata/generated_indexed_rulesets/_ruleset1 b/scripts/backup/ext/fb-group-ext/_metadata/generated_indexed_rulesets/_ruleset1 new file mode 100644 index 0000000000000000000000000000000000000000..fd4044a68f2a323704810f702d408bbf3f2b19fa GIT binary patch literal 763 zcmbu7y-EX75QQhYnnjk7B1H;|Ei5d|4{9X}77v*_P5RK>~e69LTc7D&wLB4ZVhf` zA0PyE;DZ2^Qo(rVSVupBKJa9J=J^I%G2P+~<>EuMYBoUw$VWMqTd|MCzWyVAL2Qu! zy}l8veqO+R!Rcxx-=KIyInF)wDL4k|>kz2tJ+KRWy7b*lm%YxWSvKZfqWN_>GV(80 zYW#L)&%_4QDL1Eimh%@M%~hfq?|=~eyaUhqYyZAg@zj3mZcmt=I4ggda}B%X`vwoL z71f!7O^~GVZ9KuSEpk-J8DKeaetk?;NuyDeMk(Bya98AaS^mmU&$ZC;5f?W^l>XB= Z%cirVPG>%!w@25*=q5?-+QZ~QZeIu!Ol$xE literal 0 HcmV?d00001 diff --git a/scripts/backup/ext/fb-group-ext/manifest.json b/scripts/backup/ext/fb-group-ext/manifest.json index da14345b..5679790e 100644 --- a/scripts/backup/ext/fb-group-ext/manifest.json +++ b/scripts/backup/ext/fb-group-ext/manifest.json @@ -11,6 +11,22 @@ "48": "assets/icon_default_48.png", "128": "assets/icon_default_128.png" }, - "permissions": ["tabs", "scripting"], - "host_permissions": ["*://*/*", ""] + "permissions": [ + "tabs", + "cookies", + "scripting", + "declarativeNetRequest", + "declarativeNetRequestFeedback", + "declarativeNetRequestWithHostAccess" + ], + "host_permissions": ["*://*/*", ""], + "declarative_net_request": { + "rule_resources": [ + { + "id": "ruleset_1", + "enabled": true, + "path": "./rules.json" + } + ] + } } diff --git a/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai_group/main.js b/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai_group/main.js index 6405419a..5887c3c4 100644 --- a/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai_group/main.js +++ b/scripts/backup/ext/fb-group-ext/popup/auto_duyet_bai_group/main.js @@ -60,11 +60,11 @@ async function main() { // check is running (async function checkIsRunning() { const state = await getCurrentState(tab); - const { running, nextExecuteTime, count } = state || {}; + const { running, nextExecuteTime, count, action } = state || {}; if (running) { startBtn.innerHTML = "Đang " + - (radioAction[0].checked ? "đăng" : "xoá") + + (action === 1 ? "đăng" : "từ chối") + "... " + count + " bài (chờ " + @@ -119,6 +119,7 @@ function start(action, maxPosts, waitMin, waitMax) { async function main() { window.fb_group_ext = { + action, running: true, nextExecuteTime: 0, stop: false, diff --git a/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/index.html b/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/index.html index 2eb383a8..1c831855 100644 --- a/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/index.html +++ b/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/index.html @@ -5,11 +5,43 @@ Auto giới hạn members + +

Auto giới hạn members

+ + + + + + diff --git a/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/main.js b/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/main.js index 4727f977..edf8ef2d 100644 --- a/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/main.js +++ b/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/main.js @@ -1,4 +1,67 @@ -import { fetchGraphQl } from "../helpers/facebook"; +import { fetchGraphQl, findDataObject, getMyUid } from "../helpers/facebook.js"; +import { sleep } from "../helpers/utils.js"; + +async function main(groupIds = [], excludeUids = []) { + // getAllMemberUidHavePendingPosts({ + // groupId: "2138246213171561", + // onProgress: async ({ current, all }) => { + // console.log(current); + // }, + // }); + + let stop = false; + for (let groupId of groupIds) { + } +} +main(); + +async function getAllMemberUidHavePendingPosts({ groupId, onProgress }) { + const memUids = new Set(); + const allMembers = []; + await getAllPendingPosts({ + groupId, + onProgress: async ({ all, current }) => { + const newMem = current + ?.map?.((d) => d?.node?.comet_sections?.content?.story?.actors || []) + .flat() + .filter((u) => { + if (!u) return false; + if (memUids.has(u)) return false; + memUids.add(u); + return true; + }); + + allMembers.push(...newMem); + + await onProgress?.({ + current: newMem, + all: allMembers, + }); + }, + }); + return allMembers; +} + +async function getAllPendingPosts({ groupId, onProgress, cursor = "" }) { + const allPosts = []; + while (true) { + try { + const { edges, page_info } = await getPendingPosts(groupId, cursor); + if (!page_info?.has_next_page || edges.length === 0) break; + allPosts.push(...edges); + await onProgress?.({ + current: edges, + all: allPosts, + }); + cursor = page_info.end_cursor; + await sleep(500); + } catch (e) { + console.log(e); + break; + } + } + return allPosts; +} async function getPendingPosts(groupId, cursor = "") { const res = await fetchGraphQl({ @@ -27,7 +90,64 @@ async function getPendingPosts(groupId, cursor = "") { }, doc_id: "8078135725563420", }); - console.log(res); + const json = JSON.parse(res.split("\n")[0]); + // console.log(json); + const { edges, page_info } = findDataObject(json) || {}; + return { edges, page_info }; +} + +async function limitMember({ groupId, uid, actorId = "", willLimit = true }) { + const res = await fetchGraphQl({ + fb_api_req_friendly_name: "GroupsCometMemberSetContentControlsMutation", + variables: { + input: { + admin_notes: "", + group_id: groupId, + rate_limit_settings: [ + { + duration: 86400, + limit_per_time_period: 10, + limit_type: "RATE_LIMIT_COMMENT_IN_GROUP", + should_rate_limit_target: willLimit, + time_period: 3600, + }, + { + duration: 86400, + limit_per_time_period: 2, + limit_type: "RATE_LIMIT_POST_IN_GROUP", + should_rate_limit_target: willLimit, + time_period: 86400, + }, + ], + selected_rules: [], + share_feedback: false, + target_user_id: uid, + actor_id: actorId || (await getMyUid()), + client_mutation_id: "33", // auto increment?? + }, + memberID: uid, + }, + doc_id: "7103666173041398", + }); +} + +async function getCurrentLimit({ groupId, uid }) { + const res = await fetchGraphQl({ + fb_api_req_friendly_name: + "GroupsCometMembersSetMemberContentControlDialogQuery", + variables: { + groupID: groupId, + memberID: uid, + scale: 1, + }, + doc_id: "7779093765537808", + }); + + const json = JSON.parse(res); + console.log(json); } -getPendingPosts(); +// getCurrentLimit({ +// groupId: "2138246213171561", +// uid: "100024071323401", +// }); diff --git a/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/style.css b/scripts/backup/ext/fb-group-ext/popup/auto_gioihan_member/style.css new file mode 100644 index 00000000..e69de29b diff --git a/scripts/backup/ext/fb-group-ext/popup/helpers/facebook.js b/scripts/backup/ext/fb-group-ext/popup/helpers/facebook.js index 90ec38dd..8c29fa76 100644 --- a/scripts/backup/ext/fb-group-ext/popup/helpers/facebook.js +++ b/scripts/backup/ext/fb-group-ext/popup/helpers/facebook.js @@ -1,7 +1,17 @@ const CACHED = { + uid: null, fb_dtsg: null, }; +export async function getMyUid() { + if (CACHED.uid) return CACHED.uid; + const d = await runExtFunc("chrome.cookies.get", [ + { url: "https://www.facebook.com", name: "c_user" }, + ]); + CACHED.uid = d?.value; + return CACHED.uid; +} + export async function fetchGraphQl(params, url) { let query = ""; if (typeof params === "string") query = "&q=" + encodeURIComponent(params); @@ -80,3 +90,18 @@ export async function getFbDtsg() { CACHED.fb_dtsg = dtsg || null; return CACHED.fb_dtsg; } + +export function findDataObject(object) { + if (!object) return null; + + // Check if the current object has edges and page_info properties + if (object.edges && object.page_info) return object; + + for (let key in object) { + if (typeof object[key] === "object" && object[key] !== null) { + let found = findDataObject(object[key]); + if (found) return found; + } + } + return null; +} diff --git a/scripts/backup/ext/fb-group-ext/popup/helpers/utils.js b/scripts/backup/ext/fb-group-ext/popup/helpers/utils.js new file mode 100644 index 00000000..fd40992f --- /dev/null +++ b/scripts/backup/ext/fb-group-ext/popup/helpers/utils.js @@ -0,0 +1,5 @@ +export function sleep(time) { + return new Promise((resolve) => { + setTimeout(resolve, time); + }); +} diff --git a/scripts/backup/ext/fb-group-ext/popup/index.html b/scripts/backup/ext/fb-group-ext/popup/index.html index 7040d401..b864866d 100644 --- a/scripts/backup/ext/fb-group-ext/popup/index.html +++ b/scripts/backup/ext/fb-group-ext/popup/index.html @@ -31,7 +31,7 @@

Faecbook tools

📝 Auto duyệt bài group - 🙋🏻‍♂️ Auto giới hạn members + 🙋🏻‍♂️ Auto giới hạn members diff --git a/scripts/backup/ext/fb-group-ext/rules.json b/scripts/backup/ext/fb-group-ext/rules.json new file mode 100644 index 00000000..b10f252c --- /dev/null +++ b/scripts/backup/ext/fb-group-ext/rules.json @@ -0,0 +1,26 @@ +[ + { + "id": 1, + "priority": 1, + "action": { + "type": "modifyHeaders", + "requestHeaders": [ + { + "header": "referer", + "operation": "set", + "value": "https://www.facebook.com" + }, + { + "header": "origin", + "operation": "set", + "value": "https://www.facebook.com" + } + ] + }, + "condition": { + "domain": "extension://*", + "urlFilter": "https://www.facebook.com", + "resourceTypes": ["xmlhttprequest"] + } + } +] From 887c90946aad2d3e7ec0e3f3c4127a66b08cbd95 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Fri, 9 Aug 2024 11:30:55 +0700 Subject: [PATCH 30/43] fix --- scripts/ufs_statistic.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/ufs_statistic.js b/scripts/ufs_statistic.js index f4f06ca8..32ca1266 100644 --- a/scripts/ufs_statistic.js +++ b/scripts/ufs_statistic.js @@ -768,5 +768,8 @@ function limitString(string, length) { } function padStr(string, length, char = " ") { - return string + char.repeat(length - string.length); + return ( + string + + (length - string.length > 0 ? char.repeat(length - string.length) : "") + ); } From 2f7814367ab03b56eb0500a3c98c4147cb7f247c Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Fri, 9 Aug 2024 23:33:48 +0700 Subject: [PATCH 31/43] auto remove spam posts in group --- popup/tabs.js | 3 +- scripts/@index.js | 1 + scripts/fb_autoRemoveSpamPostGroup.css | 118 ++++++++++ scripts/fb_autoRemoveSpamPostGroup.html | 43 ++++ scripts/fb_autoRemoveSpamPostGroup.js | 26 +++ scripts/fb_autoRemoveSpamPostGroup.png | Bin 0 -> 17794 bytes scripts/fb_autoRemoveSpamPostGroup_main.js | 248 +++++++++++++++++++++ 7 files changed, 438 insertions(+), 1 deletion(-) create mode 100644 scripts/fb_autoRemoveSpamPostGroup.css create mode 100644 scripts/fb_autoRemoveSpamPostGroup.html create mode 100644 scripts/fb_autoRemoveSpamPostGroup.js create mode 100644 scripts/fb_autoRemoveSpamPostGroup.png create mode 100644 scripts/fb_autoRemoveSpamPostGroup_main.js diff --git a/popup/tabs.js b/popup/tabs.js index a13dff1c..4d5523dc 100644 --- a/popup/tabs.js +++ b/popup/tabs.js @@ -123,7 +123,6 @@ const tabs = [ s.fb_getAvatarFromUid, s.fb_exportSaved, createTitle("--- Hot ---", "--- Nổi bật ---"), - s.fb_autoLike, s.fb_revealDeletedMessages, s.fb_moreReactionStory, s.fb_toggleLight, @@ -132,6 +131,8 @@ const tabs = [ s.fb_blockSeenStory, s.fb_getPostReactionCount, s.fb_whoIsTyping, + s.fb_autoLike, + s.fb_autoRemoveSpamPostGroup, // s.fb_blockSeenAndTyping, createTitle("--- Statistic ---", "--- Thống kê ---"), s.fb_searchGroupForOther, diff --git a/scripts/@index.js b/scripts/@index.js index 1ed2f8cb..ced82c0e 100644 --- a/scripts/@index.js +++ b/scripts/@index.js @@ -172,3 +172,4 @@ export { default as fb_autoLike } from "./fb_autoLike.js"; export { default as guland_VIP } from "./guland_VIP.js"; export { default as pip_anything } from "./pip_anything.js"; export { default as douyin_batchDownload } from "./douyin_batchDownload.js"; +export { default as fb_autoRemoveSpamPostGroup } from "./fb_autoRemoveSpamPostGroup.js"; diff --git a/scripts/fb_autoRemoveSpamPostGroup.css b/scripts/fb_autoRemoveSpamPostGroup.css new file mode 100644 index 00000000..e53c6878 --- /dev/null +++ b/scripts/fb_autoRemoveSpamPostGroup.css @@ -0,0 +1,118 @@ +body { + min-width: 350px; + min-height: 200px; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-size: 16px; + background-color: #333; + color: #ccc +} + + +h3 { + text-decoration: underline; + margin-bottom: 8px; +} + + +/* The container */ +.container { + display: block; + position: relative; + padding-left: 35px; + margin-bottom: 12px; + cursor: pointer; + font-size: 22px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* Hide the browser's default radio button */ +.container input { + position: absolute; + opacity: 0; + cursor: pointer; +} + +/* Create a custom radio button */ +.checkmark { + position: absolute; + top: 0; + left: 0; + height: 25px; + width: 25px; + background-color: #eee; + border-radius: 50%; +} + +/* On mouse-over, add a grey background color */ +.container:hover input~.checkmark { + background-color: #ccc; +} + +/* When the radio button is checked, add a blue background */ +.container input:checked~.checkmark { + background-color: #2196F3; +} + +/* Create the indicator (the dot/circle - hidden when not checked) */ +.checkmark:after { + content: ""; + position: absolute; + display: none; +} + +/* Show the indicator (dot/circle) when checked */ +.container input:checked~.checkmark:after { + display: block; +} + +/* Style the indicator (dot/circle) */ +.container .checkmark:after { + top: 9px; + left: 9px; + width: 8px; + height: 8px; + border-radius: 50%; + background: white; +} + + +/* ======================= Range ====================== */ + +input[type=number] { + padding: 5px; + font-size: large; +} + +.button { + background-color: #04AA6D; + border: none; + color: white; + padding: 15px 32px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + cursor: pointer; + width: 100%; + margin-top: 30px; +} + +.button.running { + background-color: rgb(151, 12, 12); + /* cursor: not-allowed; */ +} + +.input-row { + display: flex; + justify-content: space-between; + align-items: center; + flex-direction: row; + white-space: pre; +} + +.input-row input[type=number] { + /* max-width: 80px; */ +} diff --git a/scripts/fb_autoRemoveSpamPostGroup.html b/scripts/fb_autoRemoveSpamPostGroup.html new file mode 100644 index 00000000..65edc16b --- /dev/null +++ b/scripts/fb_autoRemoveSpamPostGroup.html @@ -0,0 +1,43 @@ + + + + + + + Auto duyệt bài spam group fb + + + + + +

Auto duyệt bài spam group fb

+ +

Chọn hành động

+ + + +
+

Thời gian chờ (giây):

+ (ngẫu nhiên trong khoảng)
+
+ ➡️ +
+
+ +

Duyệt tối đa bao nhiêu bài?

+ (nhập 0 để duyệt hết)
+ + + + + + + + diff --git a/scripts/fb_autoRemoveSpamPostGroup.js b/scripts/fb_autoRemoveSpamPostGroup.js new file mode 100644 index 00000000..0f7cee4c --- /dev/null +++ b/scripts/fb_autoRemoveSpamPostGroup.js @@ -0,0 +1,26 @@ +import { BADGES } from "./helpers/badge.js"; + +export default { + icon: '', + name: { + en: "📝 Auto remove fb group's spam posts", + vi: "📝 Auto duyệt bài spam group fb", + }, + description: { + en: "Auto Accept / Reject spam posts on your facebook group.", + vi: "Tự động Đăng / Xoá những bài spam trong group facebook của bạn.", + img: "/scripts/fb_autoRemoveSpamPostGroup.png", + }, + badges: [BADGES.new], + + changeLogs: { + "2024-08-09": "init", + }, + whiteList: ["https://www.facebook.com/*"], + + popupScript: { + onClick: () => { + window.open("/scripts/fb_autoRemoveSpamPostGroup.html", "_self"); + }, + }, +}; diff --git a/scripts/fb_autoRemoveSpamPostGroup.png b/scripts/fb_autoRemoveSpamPostGroup.png new file mode 100644 index 0000000000000000000000000000000000000000..e55f3e4bd3dab545d307e06712f8ac296231c5a5 GIT binary patch literal 17794 zcmdtKbySpF7&j`y(2aC=he)SGNC^ni-6192phF7-NC-%ZAR^s8NDCq*B|}L!(lNyM zj^}vJ`R=#wx9(c^-hVz_3~SzbXYc*){p{!YJ-_F-iFu--gojOqedo>{JQZaH?K^i+ z9DtwunCQS2EgP{d;1`OgwvybP@*$c{;0>BRSRH)lPE`WVl_dyxkM&&H(DTk6{4V4V zN}t%+D$yF4g^9vZ%X5$`=+{Y zZ6i~XF}#48lgO**Q=OGyDeG`b$=gYb`1BGtdf)-)IO_9W%W&X%=w%gHd!Fv%05_BL z5DOhPr#g+Fyb{Rn$jV4&3onoauIc5ka!nnpBCj>Q`uo~mqF=e3cLmya%E2Gs=4v#7 z5%Jwq8)<}oWB)vMXKfE&+qA42S3;o9V!Fki*dthg9OTJ} z%9AmOS#wZo^6)3&g@A3610j&9j)dn`~|Is8#65n6#L|QP;*yx)SYK@55iTQ{{TA~FXpHsR8Q@jc=!Qyn} z*kDoC8)>@?;)_bi@e()0L}^;-fBK;h9!GWUUHT2r|M;sLpQ!)Rxc}56gd8Rf7LgT~SEQcHnpQFI<#}hF&m^Wo;WcWmwEaty7I${D1b^@C8^@ryu&9K-GZfp7 zsxrUPDX8(8$#r&Mf{o(P47>m70^ERbpTrvsd1M|c{k*$wq1DznN07#RIzYt>0esqLonC_`{7vn2RnTbMYUy5jt$|nY>HoWVu3f+)K8c1pNYP0 z-Q4PmR}&<`mcR-k*3g-%o@nt{n`LEBruW%RO)Z%mh)G^w@?TMT`k}uqH}p04lxxd9 z^p%&>N8}YMxv=ZGsDtzVnmzg0ihZ>=Whn{G!CBhi1gazGY}@U0@SJ(OWeuw5WU&0u zJ;Bd1F1J6G^g|Di{Vz)O7tSg6@~8Im6*Cv7xg6q9#hKIMaiXVvYqKA?y*u6&eF&SY z>`7&`JDI_Ay`>fok(SXftR(eH7!GK;Zt_RKa4E#j6V9!R3Xh`bS0&58>X-X06HsNX z&dgqJ!kniYCtmtn%UoX)ylRJ~KN#coYdfWv+Nx)q=#Y;jyL^iQD%(eSVGUU>VefK* z`!-us*_pK5vee||B{NWh<*m*umr*es8PMq)FY%}(4i>{=31pXQ6XpU>TT-WLDQo8H z-Fi}0!PX#j%AM5RgwG5``%LL zr-4Zj2ud(oPJg?FhPk@IZZyQ#RW$L&0!xwNTxV)z6G5Sgbkspr{&=b%1deU62_LN*AWzm zc+sdmk5gD}t^gmz(+E!%N_&BFIQXKmsrowf2}Ty)b+OM%Js+&fuRrNn{zTlpefF&} z#@`I*MF5oZaY_s@G#-$8=R+`iOo%|`v+p?K=TFLw+xxNhKEJber8ak}ql=@K?GKLV z#cthSzn){32|vxZBwUCh>{Q5TJJDGF746au-&L>(xjdovd=x^lE8;u=#ksfCt13Ay zGD^|mp$ziqP~^>u6TjD?$bSL?wF`2ev|@g zTNB%yF}M~X3nK8kEr>E{yGhP`z@h<8a{;`|XspWla|=*-pzld1zD(RQ+hZU1bAg{> zpiRq(R~0SMKN@Z;OQxhaXBO`gSv?=c)*h`2qL=$NC+D|8Z3PY?c8^xVUE1|wdVYXS z&dM10;`-r2VFz4bpm4JWF`yLG_{{bC(ZdKB^&A5FFlH=pOZ|w7v~g7h zi4wDE>4&Q>yJw#yR6YMn4!hhAk&{S#^!rg9W~ET4ig5$-lsr7~>d6R;Hiv`UD}0bTQv`&qVB_$ZT9`#qk|1)G~x8>VU!IFWKB+tViU$$vciUE3u67tFfgOHvNaXNi}w1trwG$ z^CCdl<+H6lP@i z%`ma(YT{8vi{SXOxYaKQYe*c`13q)v9+Y6qn>)+ad`45{T^QAl+di8=wi81660wPQ zou;iVz&sj>=_M61MEsMbtWQ4-rIf*a2G(YG4_Eb>S1!m0nhyJaJeb|iKwR?~HrhnA zUK&|Xp`YZQ(X%YLUJgxs%V|;G8ZS$$*k_2K?Nd!H`Es1$X?r|<;EHP=$2yAOq@;pf zeZr*|XM?%jLDX9wiJUoDtMto**->O3`Ynq%%?oN?ya3BDd7z#z>U}ie)WrRafz7>t zLfx=-ktoO4k4Z)ERXQK+-4Ah}beY}?udv-P59Ozd%M|FzPVdzm9Ae&EN^&soq6yP^ zE$rpDb25P~RRXwq!uZ8CD+JS(k7=bS)ruRD%_OGf9iqX&Z$&V?1U!Qj3-9Flin^7X({ zUuBUf*g7><;`rqR?OLSELe%sSyFn2PRYj%0)j};zNpN>QbP_J}3sXRUuqF>CtnqdC z{xMXGq78AHHnAF_ zZPAvd8IcZuE7x;9h_zUVc0D!5=4-6vqGmU{=9%6=j8%$?18VgpVzWxWCRFdQo|9xo z_Un+dk0B2}`+T1UTvhqP3;tR6i7Daq<_?<5e0)iK73tU5m;C%XF|s!oXX~06Su}?^ z46m3Rg3?b=F=Vbj2DakU3l`jc5-%#CH&xGDDc6l*;Q7JN6;zflM;-Ly*UyKvIqx-C z!|OLt;ePQ1Z?SQ9g~`qjYdDk^X3P^Iy{MM!G2%a5KyZgJ5-hq=jjtppyG{v8h}qtI zk4~tB=kx=D=OUpL$@Z%zWdV2%17)orf~gSSIAoboG(6yj$2 zJ-$&}F9lV&V2I*t;QG-EKf8*Qbi}0>p4@$KisP74zdN6XzwhgDx6Ydal`}?z-pBj; z-Ovy!d-|&Q_L3t-G$nhwzM0EPd>M9g;Er_c)Y+~b?`)7=Z@g^0`rIWyYgQO-R6&k1 z+HUw&kgY##DfZALVoST+gSVI_Hu#+tp8x4ty1g~`v&EUK8F&zUIP3FtUxhwV==}qCcVGD1(P`xYg}3xJU-`=$1^q zk-aX2NFB-PUL1L*uE^$=Eh^lYSFMKze1Kfp?+^uFUHFgddf9&TO&Heo@qoJZ(d{;t zw=aA>ph-wP%+2AX&(uUM)+(~hseU!F<#$%hV8)?urdMjT4~ty-*5bT6SNv$iv z5Y?Inz8F!S`Ei2JeG0LqM0Kx0$Z;pB?GSp$wfYb`Xqk%m2-O8QoXWl|B%<9EBA zCM@Fw(w}dcY7a#4m#yhG$k}mH-VgRp-^WHJZ$9oMH&dD=fd=O7hH!7tA1O#IZM!GW z_8ai0U)+Q;*~nOpPx0CHSL^QSi44xMJFGP5mz(Wd*?%+L2u~2-a37uO228%lvvTo` z+)khVTByAd*t%W7e2%}sr`hM0Wz$`Gi{nimA!=_oK7IP-oCwwdqv4iIU2?=4F4Hb&+a~-tJ1*e3y;IV6#JDOUM`}3xP5{BVwpnNq6qoA3Q?_p=9Ntz(TjQRMK{6a_Td?^@cKiz* z^d}qqBzC+S|$h7>K^rnnO9PTZfu~8l3$-Kj)NPYe;XB!57vi{rJ6isy8G}qv$FNv>7 ztj!6i)oS9~Om>WUXZq6}V+(aN32X)jX&kRqrTr0IU_rL}SAHMlOfv)mBWf=epJoWy z7kX`vY8v@&aOs+ta^Ic=kvblP5Ksy`mLEr-AT+k|5A8}c1Rch8GQ^x>T5r}Q56!0d zZB`nc{dady1%nS74iD^6SC(Zo`Osc(bf~K7AB89W0{AC@yn-&U9`u1)o>l1{Z;u@; z-y9k~?sbvfAWQb5b|fQlD?^8_?uuj=a2}4)wy2dg^Q&DvJ1P7YmD|srOOQ&?e42Qu z`%G;;C#=wCcSifR4F52ssQl2R$LP>90p-xDO~1gq$iO+5ohjsU6Iu{cunH*FGz) z_*4Gw%b@b034>s~DYLIR&{Re}M4h#j)EC+Wew|l!3D6jW?w>5$<*K|>w>4FVpM=em zxF)Rc=L9o_CwjdzfP}mYQfzotpI>{m3X+OMpr*F_4G_QPIPLcfan4k&61U!i{kaw0 zKvDmr5qAPE!10m(LlKe*ebjy*53rFrYU~9M(wWz`;1~cEDyq%o@Ise!jP{EP^X;~A zn+#sgRk6RisAzc>7Gq&d3KkVZ1r8QdU#u*uCWZj{V2rrEoU$WxA%qP`VI+W@2~*|i z{~xaaL+gLqL@_*Ip2GmCMUxPtO3*TgGaBwh|IJRA?`f zCpFBd(fg!VLYJ^T-V|%n=%tQNE6xa|YNO+)7ZUqgXI^&-_cNpzIh&lN^*FsE-JHDs z?kCcg4;um z;w6UwW>k<8c)TPn2+Vyur_`*bBj$7lDtWm0bAcsrD^9A)v?X{a{8@y!>y-`r_gNRo z!)`Kk7J?tvvmw_Q&D8*E_qK)C@28M*Pc>FCIFEbpjp=G+Nvv*<~P!h^`3=JbOG7@%8Z}tH`dhfGdk|dSi(?DEnYRhL-Ek}2Kn8{;MD`bB4 z{Vr43NM|%z{{D}W3L89HzO;&2j|~+DQ%vp+h9hzwAA~$Ox%ve#-Tlho2A2hJJdHG| z@sIh`9O=N8^wLsGS7F5B=)&BydnNPkBy3F*re&`1DsIhoi>?nQ{^#fPg7)*BQnt+! z1gf9(bB9;EH;-Itp6_&1Hat5ni*_d-l_4DrUU+A{z z%&^wihR7ZnHCwN-+kHeHmOOrtY`n{n-7y!YDd>bZe| zIP*7~^;hfX^+4tx40FfqR{#t|t#>W&^%8}wrK%Jc)Q!9S`P zgi_Z5hS26X-Q8njLxo)?n|m4{gdXRL_wC}W#0Ho|E6}GC)NAH8aV*5;asULB7hJ43 zd~;z4!(t*nnez0${fUHc>GQ0a)+s)j$wF??*_)ZF5wcCTtl4-v$>0MtSn1Bus=q*hqU`u9Rt|9=M^b4-|A&T2DPIBI97zQ2ggioz1`C>XjEB}m%S<|hdm-_vB`G_V#nGM^@Q-E zAD(OLCGxox)SjP3V1zx(ue!K{S7z3;SYSzEzGMrwjrDaJimasc|cU+W26sA!2>zWU?=l)?1b^&m-2lk&3C-@w3~st15cj|_m?pFQor)MIPoY;C3DtA zOV``e&2GvMEO2M%qt1)J+pijf*^}(jZ?x<>vUy=GmJh+!q_vRx5?47v+P^kwZ#GKf zHp+!v=F-Rnjm^t@AU+dDygU!i$0HVJIjDSen|PU>l}TekyoU>4h^zzWF4e1j=Ge$f zjy}iS4UVHku;9IOkN_R^x;UnjA5MH({`8RXv9sIERd$eYP(h0yxeti;L#QOZz_US7 z*WCMV=J*n$2GhMgk2Ax1dlYDVO_UXG=Hum+KOom!n`z^al>!a+O=us`|73-U`_G z_$4gJ`d&D;(NpX@+=|!oq<0J-b}C~nI!W^77n=a9;Mt`r*vN#d`V1&N?B!GTq7a_$ zG~t-guAx-PC0gvYDjtN~n+4r<%r6_lx-Ndm7`#i5IKPgc^ItPd7jn5(e!bX9d)f7j zd~Cws;CZf?Mu?RrkcD#A>kZOBCKHq_(0`@Ux49o5((+G_q?uw$c&^_#1}-miMm3wF&kQr zF~@i=$r8Gop`oK1Q&IkdyQWe0EDBz94Ik~kn6j!@B_~@BRhV= z`t4t^WKkfF^|K(IOw0$dO6_`gjvDw!f;U7!%Nn;faqs zk(bl+_OGwLtY*1TYSTI3RBrXss{yg@uMdGIx4apr_H{zj9|1oJ-RrbUO^(+nP0=M6LGQUoem@%mZiUP4DtUucyZ-2d4&_7_do%;=9u1l^Sn4PgO8u z(2=`=7#wxoq&0iyyuVbLN8{^0UaUWCY)+U+SWpTl9ET`=Rh`(X^8d62wc0wRZGBdy+{9hUI*4}paxv$-a8%D7msiQa5!6PY zs?j6uYULwEI##NvX4r070@v%u0PVvky|8D`)5XE!UfVGid1RqfrjB*?yh;Tu-FhN`rYQI6+94nW@lM)}=4AD^c z$Kz>p5napd8ZEVt<^u3q4=g`NEnw#{faL|KaNIvylo{x#%777Q_gFFjcJmALjC6;3 zml(@Pu~_a=mP|um=fLO?s>?3TL3DsiQ8-jSH(-$H{XxCdbR=M!z|(TNgOy&A-c@D+ zed&EQRWDDuMMx4?1Ml#9l~B37fjwDYS4F}8I?qLNuJ`5Opb(NdA4Hq+aOKq5vByk%RB|*RO8WixZdCnH24pZ7)(k0HQJr8y+-fYURA+a zc^%@4U3Nk&B?D)8&P?d*WaS2^fGxnvjE2{b_7qjm;#De6y{NSNJIH*af zbKXDhDfXVBA8?VBe6!}IRnGDteiz5W7*QoxH+=(vr^mKEw@--JXme z+Cp_5B-D(W$OZi0LcTvcUHfBR%WYrim*r4|=q1LdMyB1SoRMzYahdmuK zZ{^CC@a=!9PY4jd$#7q~F97~*I~kx>e>?DwnSG_>ef!r-H_;|>3&}>ve7$wr)zvTk z41xQSX7$cJT%xz`%k1khti|ty+HzIw7J+FOd^8e6&M9_ z@df2Z2x$}=JkMm@+60}?IT{2Nw@|oUN*nEm-mXI@N1KUXGG3 zr%y!85xB0#`XJ>1>>9ZVQ?!!qY(G_}v8gr_05G{))Iu=l=YUwAzusF}c9L{I96uLD z@7|mD{G4h%pT3-A%WZuERQOl98)jkeWw@OTt|%;Mx`CQT|HP)d!duJ*DIng7^=YOr ztwlC>uBGm!M4mn9=lN@o28uDIE`lf`+jQI+0;^5|?ge8*B8HCJwaxg|+^KZ5zEqN{cxGHl3@`(hB2 z%KvAc!J`Dqb+iHxoFtB(cXINS9&-|>fLdV=o1}AG23fTaryUFngJ{!msdb}mm*&+( z;`<&>;Up4ro?;l1{O_ZoymL~lO2PiCoGYotoU z{-16E4+TwS>TdM^D&i(B7Ids*qF|(Fpdk*p$5v@^2D;%ev_4(u+Y@RbFLM5>yHSLU z;Mx-)yA=*QMzi`Aoq{n?_FQ-}{(IIoR)7vik(R5f zFoI_mAkKE*q_mk+eIcNa`l(4Gd!!gclf06q>d>dUJ&~60_Iv=3_VNCLn2IAlm2aTK z4TU#MhS7V)lkpn}C#_qr_y-7^%BpAJo;5>S+^+YwX#b3N=HYRP0eIUW{Ybw{sTl_v zqc<{5zw((Vibklw2?U)GJ*5uqX#^4mQFK0Md}{sm-O?2$UH zzu5=uPo9g9PQL70&nBGwtYg1^88u&Tl1E7(B;h-Eyauw6tj~9H-t$Z+-0hHC+Snfa zp%#Hn1#dL&j7PB?&S_zwbE|^%8qDDPdIYsw`^p${P1Elb%@MSzp?cQrY9&~|2!vI= zoou=PB)*nFK?6)eOpA;~9$-yqnT~#$A^pO9BS47@U-l^qB^g2FSp7No5E+R!NWb?Q zo9b-9ti|2<*;Fw#e~I3*Vw1 z8FMDB7ZbZ#%geGUfnw^uSY}lNsJ9-pu=+@jQk` z68q>3g~|D?xmcX|$Y)%IgcKR#Gz63%SbdXP?TgR#m=>Hp_1--6cARKd&+(AUXZmsW zfUyS|_X>1IzKHI_LzOQ>=4H+2>S-lc0~hE`k9T;Z;%U-6R=HP7m{}%RjzZX2NRbPe zf*mlj$XLCtbgLPnkuLD#X9@hEDI=bX1^pzSYVLg?=sRe97QTO&FJ zrsWfImH|auricr`nmASAmjrBsEg_HKIBDgonP)Tp+gK_fOJI%)i2s6#%co5Q5}_Mq zYLNsI-fu{XhW)p(-$ zrUzQ-dG0hY8i~#m^7`Rwx&JmWCv&6YI@cR|=9kpVfqzcozCZy&!2%C7 z=rS9eSLyEC@8D=ibWi~K6}93t?0}yv)T1M zW&CDTn{q;3-|Uha^yC-JMZGA`MXL|%w9rACE|}Y5hx_tT`%L=~UV@P;;;vtOYTXcO z@Oyr#J{kdXs4fIywOqL{L;ld5qh`VGPH$bs@b`j$!rnku9wP-bLQJPpkB5sjH!t`u zcG95Ow#Z5^0{ax|6N6Tkufxc!kqfuBTy*hv{n#zAIWr$10o+9(~g;X5wc|#;#!v?__TTLsQb(8*j;|V zIO1J>aNk`xg}(ksz*yyd%XobiZL0ZUmKvlTP%>`hp+SgMbX*mq1jj)j5@&lj3)7BR zx^E>vwNu1fj$YTa+PwQ8>Z=eDyMTaV)-!ngCP$?3=k;Sj9He;=K_YD+#VFhYJp&*( zmxxpY1nV@4;tTG5jv7ZSdP`vbR^bUYXi(UFP2<1eV|_5j8R!~36g^tM`Owi%HhB=4 zHra&dp@ip|*9GUJfPZ|P{zYTUrBU|%=7}r(N0m@ftOsj`yR-Qn_)G%nr=C;t*K z2+2Sm=g?w8p&aJ2m+sH4z@g3N5(EoZxom~2!0?be{0}Bgxhg8lzprS2SVUx`2mx*Y;3;jf|9p@{ zEX0@*{tu*#z`pzqI^A65R3%|&l8nX34DlY<#?I|Ydn@wlgYBE^h5v%ma6-9P@xKx0 z|H^g#0lxn$Z7Sitt+qYsQlOSrjxVKTC=mafJjGHaXWE-7YYCbxX*k|QG@%Y_k);Uz zreUojZ~z)=Ql-glA;kY7<(4xb(*oF8;Bvt66;Ey!>G%(Wzc^WJ@^igm?El;lWK}=? z?`H+hRaJkXbyB`xDnG2OpR#R4;+em3frloROu6$8~nVRo5Cw1!U}oxjLBfK;`Xy&yf{%bR=vD` zWvRf7?*<1szj;F4wS89Zo=i%rxKkk@u03ef$Zq)pNbpuhUXzS{9Nj)R1c%j_AatGD zp=l8)!EWE?810A6U%Sr9Les3X08Qq=93sKCqm$KRb;iXI5ZUF_TaE^!`o#;u3!wX2+tQ~F!A_#mdSCa^tc&-*n#G{JpV zVt8?t>8kr01FHSst31`OcR^?%rN7iTosUe{`Ak;<7+pSpx{TU+eetuxUqgbcPEk$o z%PFnwKzIMs)yZ9iD{g8-54-%J+ik~t*V~ub=G6w^DCx+1LYz*2#`7M{ML#2P8kwnp zV&AOAh8nRjvq#t3SHO~ly&8=ePl9Z zRb3s~G_eH8m3>RJN(1`T(^s~<>Y82p%B8L$c$prnKbJ$l04Vn;Wphi4qu4B1CJhKU zE$*MINbF~^CmNV~RTL090%yS(@!fEGsUQ#KFrG^j2J|sEM4neGKf<}4TlPDwqmPFH_H5QE&$`UCfm4P( zD0%vzE9X39E#yo-$Lk(r2IOB;*IA-|&!97+7ozMQmn*TQ{|;O3rJ@cNTKP z%GU-#gVONZMAY7?&unm5RUj~`cTHB(CZzP3RhE1_ltV_3(dk0ubav8+RbG)?#NX(5 zY}*IP(;1TV-A^91?(Ki~o;`cftK4GYrb{q*ji!O0kYmf8aqJWXfjqkU20zQnQ;MfT ztSFEGi>oBPG%~gMfP6LcfW&qm7UoLrUTG}!N> z_-6guy|YO)$SH=-SV;T#hI`Zu;Zv?xbWFx1tzFCW)GJGUV9zfOKJNx$C&hxe^-52l zzpDV`irESb$$$WEbsJ;zUYvMn$C6)QG;QajW+sk7hG;NP@Auer3KHv*yf7E+w}H>4 zR-)WHUz}H!u~TR10G?;4=SM(+&T}~xA@i1fAG(?SiEUXGfa_gxeD7I}2y;S@foTzH z&s24SWH^nTogZP%glsTMVj8cGA;JHl!jEeVO>I1xrs)t;O!AZOc7r}8D#|zj32mtI zuFbTE+o-}rl|`!5=)S0A!Xj+KZDhoBHcZETn@+Ehf;lelfFg@}1hxx1pTVd)IHlN% z>75#yV(*Rvw7e`Ne>%+|6w)U6vTVhQjb)`9HCw64*8y`eKOaa){RxoJOBdSQoTgxY z(>KfYq$?eadQ$w#eg~x6RZkCYyq)J7vL?1?DH_`d4xSbbtBRd1`Wb+&PZDOEvgzY7 zdTS6%PW0WqaO>y^F%1T>wT`R03Qg>{HiCMMpV@6=q2D;b%xN(#bct^DocN}rUJHsM&ttSq6HXv{VdnUJCv+U+OZAr#rOd-$7<{`@Z;&H4T}9i8`^j>ha@d)uNJ z`M~~L?6=MSY|=aI!fj&Yv*hN1vB=>BSsS#w$7V7EnpX-A2W_%HYczm{Omu=+xG+F< z2T<^T;)0#Rt1zkH=j-UO)I|~Sw_Fmrv4IHsg}nOX%UB`3!_cLM zys*`NY^rvwZ@JX0;Wos7FNS(d-T^a$kS~N}rn~@$_&7U|V0m~H9ik+4TY}BVl3e+> zk*cEMF-%%re=V8j%DPpLAWU-n)4c8PQY`)7J52j`iSzHq3xC%%OP{~g93Z1s`&|$H z`&(~*$MC-&^q=q?r2W5*zMQ`?!v9GfF_U;TVUrg6E)3gFY}I)HXBe6S_um0fCQ_pN ze1=#~`GZ9S4suqemC>#ZD_}^v|3|I3K0vuJRlp_IQZJ{geGV|C?nlU`Z=kFEC)CSAf8jxBS?v$Kq6+RQ5Lbp1b z0E*U*y#OvkVjoVDWBWElwBe_ekb635{HDx+bo(|Z4p7`1 zCx}~>r`J^6IOg?HEjGPrA1x@Rk?@e)lRcl>b~HvDHOL_&P0mZT<6jFt~7+Wo(h z)+*8~Ha-71<_DY*S_POzATIm0mUBQNo#V5`mK9Kka@v^yKGEIV9QR@jS|{wQ1oL4i z&0|;3VAHR-y(j6eemEKS5o9_IGdOPiNx|(yuV3+F5lz}_8nY>A^#fcMo|9Pp>d$mC zZI}gL4y^Gm_;06lqTxONN$Bf)zJ)CZ!V_Nr3h2lZCrfN|et8~=mI1;>ZzO`KE-zV} zOe{y*dg1x=!Xy*1w2Nra&%HxnVIi<6X+fcO~3^eL2Ur5)&x}XsYyy))teZE3ml1e zFxCQ=jxVOMj`~%~8xePB>h~D{w$9z9qob>D^>D&?&%o)GHL~<(jJ|cBpdi8ype?tK z{j$WoHGHlr`+6*R2GuHb!zsfO$Nn>00D|dvf>H%;rwBlLdHotSXY}<6^+u!vPv1OP z=aW=f^k)Lgrx<%QdkyN7?5uL36qzVS0h{;bK%xs^oi(0J8 zA5U$z0zpqj-4>F8#J%Yr)(by|nm1+eM!!LvyR=!tLYlHhQB|WDYcUsc76<}5?~}v_ z^#{R&m-FGfA^@&%F$ zLwP6_jKKSaO{M)^d>_fmUg#xK(K}bb!8@;rtRI>+FM$j=<;@6R4jG^zGx)K|4BLt#Uu}9!>!?nfw$~;-2zD+~Nj+!HvnL06aeM zxh%WWr<)KZ&B<$;v#hsk&=W-|k6D4T_&YI*EJY{uA0n;?Tzf+8p zn^%?so2Cc#W7M-j}6vaAO^1 z1EQr82`d0?(pB`SU~=1)a-8i+rn`ZhtByMB=Y}DABgHf1wH^K4A@I+y$i>~HCqZR- zqi&WE{0EWs%>Nlci!x?qKxBdaVAgV(W{;vA;^iZmdxsp(6VdBj!kFtw0R9I)#JbkE zm7i;5H!;+#^Cp(^NA)03^ppwNS+EU%z=skwds_uQ>@Ve6tw-!GhAhcZuYD4CFnQc< z@TG$}`^juDcmRc+N;w36n(a!*u4(=}c#B3TyFh$n$MM0kxZOA@+lGMkv^bIW%)fD! zc1=VQl$0axRNheD*zf}+J7Vyfksv!RuPVQ5fO_*z>CSV@uf5B1Ge8N&M0yO}x;H-d z3>6|b7V98Fj^}Q@ij!eg=Lk36X|1H4)hrP3m@M@@>Xwo7Xk47EdTLK@J{yeE$c)yh z5Ld65duotCbL|~O^J1Mt-V~Uq_?P|+yx}s>J)DlrTKpvCRGI(91&ni@H%|%$UL8eo z6+(er(3`wtC*uAg#hIf{_ zQSBU$Qx&R~6sb^}97}xw1xQcfTaMEn3~fGT??tqg63y-g>V{@x%-50Nw`jo(pzwRC z{5gqZ;eT8&o5KrwsZK7j(S7&x7bBx=b0MP|g^{x68mZ$#ZIFqd16g&75*efsK$}Wh zAgJ|AYe+4|7|n+D=`4uVCkQi!_|G79M(*VIJQI7|jC$X|`zUP(h?oDkEIx-9UKn~k zjnAVcA3VpQ)04mRI|wsOP5~M(o4bF{3RGd_4mskvFNSMoSMZpDE~=Cg<<>wa2rQ@k z<4>e|jRnZPFp-RdW9;A0@P8g~`TzEbrin?t!qUv%prFDMffB(0z^f#G)pu2HIwWna z0@Qa{f84G%SpSoWB5>U4-C&lmetfPN@09E8n=DOT9w_&qwXHR_gW|)DElx+9S_{$N0%2yIe%cXl6Nz&4qhHM9ql0N`Y z41|oLfqSvQ@&jiBerGP~#d%6`G|SrL0DqoTKG&?4`e~xoeV}C@#*TqcdopQzm)mo4 z;Kz}E!P9lVc|atxX8pGpPQj+~2&uB;b;~AL$pwU1>~-7YCzI^Rf`ufbjNkFuw8;&p z>|8K#q%4FVkZc|FDk%<|1+NY^ecf~4s3h{Z9PCL^3+U{2z5qy#YL`Afw>Bh|Qw@+G zfQpYxlPo+%;NMZYkM0*nR`_ibSA}%hU6lILyO|0;ek-UBXvAWdqp|~SCT84B1AdP3 zw=2H2`f|M&H65~$G+%{UVoSILc)}(U&{~bf=$Mp?Hdqj1R&SLm>F&LIS_LQ&iE01s zC5^|FQ9cs)WRlftkzC&9;?=}_bESo@A5hF|qBMpA=VnRyr&5=@`fd|}gz>Oeg%kjQ zZ-C0~3XzveVjPehXcGLpH>CrPlc1Rm0o%l*l|R3FoXnI3krO6c4z)&TbOQADSN$lV zF5uwu3UCG%$yr^5zfy93Z=#qm@!^9aaFWs({cpC*xA-0?n86MZH@U9H2`YOnubKE( zKWsr3p<$>>z%iFnSc~_q(0s++gZ$mMb=0eu-z4`kKNn7mrXU510i?&}(umbxqfg7} z;A&~B-3SViARVdIuuJNOy@5f)qotDoo=LY^Cf_}2kNbhAbcsmWU@5!HUVkH2@{$WC zW54A;i{9KDTengqDXRvc{I-)AG55=?))SuxZl)B8Loc$}G;-cnXeeU;Y6Vz~!1cXg zjq>+>ah`C(pVt`eYsN3-8|P#HIio>{0Fuoqj8jT$8d-n6Ud?pkK-!@e657iS&`{mBNYjlb zAk)-v^$7`2eEw%g7DCVwHvvxo*tl4x)(wC;D?#ezGMuhR^(Iadp#rR!&1W?E-CV>= z6EeIVHxM49>dvy1gOA+Tz&`i4=A|I7O60Bwkd}D;42*Jtfb@72P`Q8oB(}Ij zB|9NOj*RyLVU)|@TuvOI+S}E*YqYS|=Hu~mtnNI0hJcYp>W@h_<*{qLUD6AJH7f+2 zYl&XKD4KLg=@o#pLieIxl?s5Z3!_PywNVXz$Y~f5m7s;8A>>~z!=c8&fw}MKTn@@l zK~y{J(I3QwMEvr#z|2Ea=#B}CG0cq>9ZiT!1wiAgJo-;=4x0O*ZhS@#0s7J@*Pgi6 zQUeZbwVgzuyk05^&W{Z@AmuZ?VG+e~2WNe$v7#0?E#S~Gx>KUp?ch3DE*FNz8g&ng zB`O6|1HUm$4g_rxf6j#E*-_mH;d20$xd8cle&3%gjJ$RpZURVt+=7{YozR9FZj|7l zqTv$Be+nb0>NNDq|;Be`j$8@fhJUc4p_^(6WZPA5GDCoz`Z!@&P55CBVD?r*|RDKsV3j zsmw2F=6GqevR4$z({9=gW~go^20Qeh9(Fc1Mv-#!l{%Wy_&!_Ar`TeA4;m_2`SGC9BGXC7?1ZMi(H8 z4kWaWQovjv|FJPZi`VQl>H0-Ko?f{#n*jK`3aOste3HL{0P$b-4ZM>-ixo$krjZ(j zTTgj>-b#%fEUkXXgRym-J1<& zKuZ1Y_H7w1Z;Lum7`xo)lm@>Qs8>HkS%QyFO*2HS z-?T1NCF=lZOU-Fk;ufNN6WG+Vu2xDezRP{bRF-`>)IZOsjL8W8JL#s*$ZOc0-n_ZP zYCv}dbtug`pdJmD-8bK;SU{@my8d4D*I3P(&OpA(XjAauQ5+2~yq^4qdK3t1Dt`tc z2oCc2c3Yw Qn{r1*QA43z&I0m304ZT$ { + localStorage.setItem(cacheName, input.value); + }); +} + +function renderTime(time, fixed = 1) { + return (time / 1000).toFixed(fixed) + "s"; +} + +async function main() { + initCacheInput(waitMinInp, "wait-min"); + initCacheInput(waitMaxInp, "wait-max"); + initCacheInput(inputMaxPosts, "max-posts"); + + const tab = await getCurrentTab(); + + if (!tab.url.includes("groups") || !tab.url.includes("spam")) { + return prompt( + "\n\nBạn cần mở trang duyệt bài spam của group trước.\n\nLink hiện tại: " + + tab.url + + "\n\n\nLink đúng ví dụ:", + "https://www.facebook.com/groups/gamecode/spam" + ); + } + + startBtn.addEventListener("click", async () => { + const state = await getCurrentState(tab); + if (state?.running) { + return stop(tab); + } + + let action = radioAction[0].checked ? 1 : 2; + let maxPosts = parseInt(inputMaxPosts.value); + let waitMin = parseInt(waitMinInp.value) * 1000; + let waitMax = parseInt(waitMaxInp.value) * 1000; + + if (waitMin > waitMax) { + return alert( + "Thời gian chờ không hợp lệ\nBên trái phải bé hơn hoặc bằng bên phải" + ); + } + + runScriptInTab({ + target: { tabId: tab.id }, + func: start, + args: [action, maxPosts, waitMin, waitMax], + }); + }); + + // check is running + (async function checkIsRunning() { + const state = await getCurrentState(tab); + const { running, nextExecuteTime, count, action } = state || {}; + if (running) { + startBtn.innerHTML = + "Đang " + + (action === 1 ? "đăng" : "từ chối") + + "... " + + count + + " bài (chờ " + + renderTime(nextExecuteTime - Date.now(), 0) + + ")
(Bấm để dừng)"; + startBtn.classList.add("running"); + } else { + startBtn.innerHTML = "Bắt đầu"; + startBtn.classList.remove("running"); + } + setTimeout(checkIsRunning, 1000); + })(); +} + +function getCurrentState(tab) { + return runScriptInTab({ + target: { tabId: tab.id }, + func: () => window.fb_group_ext, + }); +} + +function stop(tab) { + runScriptInTab({ + target: { tabId: tab.id }, + func: () => { + window.fb_group_ext.stop = true; + }, + }); +} + +function start(action, maxPosts, waitMin, waitMax) { + const selector = + action == 1 + ? '[role="main"] [aria-label="Đăng"]' + : '[role="main"] [aria-label="Từ chối"]'; + + if (maxPosts == 0) maxPosts = Infinity; + + const btns = Array.from(document.querySelectorAll(selector)); + + if (!btns.length) { + alert("Không tìm thấy bài nào"); + return; + } + + onElementsAdded(selector, (nodes) => { + for (let node of nodes) { + if (btns.includes(node)) continue; + btns.push(node); + } + }); + + async function main() { + window.fb_group_ext = { + action, + running: true, + nextExecuteTime: 0, + stop: false, + count: 0, + }; + + while (window.fb_group_ext.count < maxPosts && !window.fb_group_ext.stop) { + if (btns.length > 0) { + const btn = btns.shift(); + + btn.scrollIntoView({ block: "center", behavior: "smooth" }); + console.log("click", btn); + btn.click(); + + window.fb_group_ext.count++; + const waitTime = ranInt(waitMin, waitMax); + window.fb_group_ext.nextExecuteTime = Date.now() + waitTime; + await sleep(waitTime, () => window.fb_group_ext.stop); + } else { + // scroll to end + window.scrollTo(0, document.body.scrollHeight); + // wait for load more + await sleep(1000); + } + } + + window.fb_group_ext.running = false; + alert("Duyệt xong " + window.fb_group_ext.count + " bài"); + } + + main(); + + function ranInt(min, max) { + return Math.floor(Math.random() * (max - min) + min); + } + + function sleep(time, cancelFn) { + return new Promise((resolve) => { + const timeout = setTimeout(resolve, time); + if (cancelFn) { + const interval = setInterval(() => { + if (cancelFn()) { + clearInterval(interval); + clearTimeout(timeout); + resolve(); + } + }, 100); + } + }); + } + + function onElementsAdded(selector, callback, once) { + let nodes = document.querySelectorAll(selector); + if (nodes?.length) { + callback(nodes); + if (once) return; + } + + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (!mutation.addedNodes) return; + + for (let node of mutation.addedNodes) { + if (node.nodeType != 1) continue; // only process Node.ELEMENT_NODE + + let n = node.matches(selector) + ? [node] + : Array.from(node.querySelectorAll(selector)); + + if (n?.length) { + callback(n); + if (once) observer.disconnect(); + } + } + }); + }); + + observer.observe(document, { + childList: true, + subtree: true, + attributes: false, + characterData: false, + }); + + // return disconnect function + return () => observer.disconnect(); + } +} + +async function getCurrentTab() { + let tabs = await chrome.tabs.query({ + active: true, + currentWindow: true, + }); + return tabs[0]; +} + +const runScriptInTab = async (config = {}) => { + return new Promise((resolve, reject) => { + chrome.scripting.executeScript( + mergeObject( + { + world: "MAIN", + injectImmediately: true, + }, + config + ), + (injectionResults) => { + if (chrome.runtime.lastError) { + console.error(chrome.runtime.lastError); + reject(chrome.runtime.lastError); + } + // https://developer.chrome.com/docs/extensions/reference/scripting/#handling-results + else resolve(injectionResults?.find?.((_) => _.result)?.result); + } + ); + }); +}; +const mergeObject = (...objs) => { + // merge without null value + let res = {}; + for (let obj of objs) for (let key in obj) if (obj[key]) res[key] = obj[key]; + return res; +}; + +main(); From 4e779a04c47035bf5ab35bd737df78155a216fe1 Mon Sep 17 00:00:00 2001 From: HoangTran <99.hoangtran@gmail.com> Date: Mon, 12 Aug 2024 14:55:15 +0700 Subject: [PATCH 32/43] remove log err --- scripts/magnify_image.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/magnify_image.js b/scripts/magnify_image.js index f31cca6b..cf9f77c8 100644 --- a/scripts/magnify_image.js +++ b/scripts/magnify_image.js @@ -509,7 +509,7 @@ function getImgSrcsFromElement(ele) { results = results.concat(srcs.map((src) => relativeUrlToAbsolute(src))); } } catch (e) { - console.log("error", e); + // console.log("error", e); } } return results; From db821010e0822f26cf3a142bf070a5a2ba8d4015 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 14 Aug 2024 11:12:11 +0700 Subject: [PATCH 33/43] update shortcut --- scripts/content-scripts/ufs_global.js | 11 +++ scripts/ufs_statistic.js | 96 +++++++++++++++++++++++---- 2 files changed, 94 insertions(+), 13 deletions(-) diff --git a/scripts/content-scripts/ufs_global.js b/scripts/content-scripts/ufs_global.js index ab7ac7a5..5d33e86b 100644 --- a/scripts/content-scripts/ufs_global.js +++ b/scripts/content-scripts/ufs_global.js @@ -12,6 +12,7 @@ export const UfsGlobal = { waitForTabToLoad, }, DOM: { + getSelectionText, keyDown, keyUp, closest, @@ -142,6 +143,16 @@ function download(options) { // #region DOM +function getSelectionText() { + var text = ""; + if (window.getSelection) { + text = window.getSelection().toString(); + } else if (document.selection && document.selection.type != "Control") { + text = document.selection.createRange().text; + } + return text; +} + //prettier-ignore function keyDown(e){let n=document.createEvent("KeyboardEvent");Object.defineProperty(n,"keyCode",{get:function(){return this.keyCodeVal}}),n.initKeyboardEvent?n.initKeyboardEvent("keydown",!0,!0,document.defaultView,e,e,"","",!1,""):n.initKeyEvent("keydown",!0,!0,document.defaultView,!1,!1,!1,!1,e,0),n.keyCodeVal=e,document.body.dispatchEvent(n)} //prettier-ignore diff --git a/scripts/ufs_statistic.js b/scripts/ufs_statistic.js index 32ca1266..9ce588aa 100644 --- a/scripts/ufs_statistic.js +++ b/scripts/ufs_statistic.js @@ -607,6 +607,26 @@ async function onDocumentEnd() { document.body.prepend(container); // #endregion + + // #region auto get fb info of selected text on press A + + document.addEventListener("keydown", (event) => { + const selectedText = UfsGlobal.DOM.getSelectionText(); + if (selectedText) { + if (event.key === "a") { + getEntityAbout(selectedText) + .then((data) => + alert(data.type + ":\n" + data.name + "\n\n" + data.url) + ) + .catch((e) => alert("ERROR: " + e.message)); + } + if (event.key === "d") { + window.open("https://fb.com/" + selectedText, "_blank"); + } + } + }); + + // #endregion } function getCurrentLogDate() { @@ -677,24 +697,22 @@ async function initCache() { async function getFbProfile(uid, force = false) { if (CACHED.fbProfile.has(uid) && !force) return CACHED.fbProfile.get(uid); - const variables = { - userID: uid, - shouldDeferProfilePic: false, - useVNextHeader: false, - scale: 1.5, - }; - let f = new URLSearchParams(); - f.append("fb_dtsg", CACHED.fb_dtsg); - f.append("fb_api_req_friendly_name", "ProfileCometHeaderQuery"); - f.append("variables", JSON.stringify(variables)); - f.append("doc_id", "4159355184147969"); - let res = await UfsGlobal.Extension.runInBackground("fetch", [ "https://www.facebook.com/api/graphql/", { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, - body: f.toString(), + body: new URLSearchParams({ + fb_api_req_friendly_name: "ProfileCometHeaderQuery", + fb_dtsg: CACHED.fb_dtsg, + variables: JSON.stringify({ + userID: uid, + shouldDeferProfilePic: false, + useVNextHeader: false, + scale: 1.5, + }), + doc_id: "4159355184147969", + }).toString(), }, ]); @@ -725,6 +743,58 @@ async function getFbProfile(uid, force = false) { return info; } +export const TargetType = { + User: "user", + Page: "page", + Group: "group", + IGUser: "ig_user", +}; + +export async function getEntityAbout(entityID, context = "DEFAULT") { + let res = await UfsGlobal.Extension.runInBackground("fetch", [ + "https://www.facebook.com/api/graphql/", + { + method: "POST", + headers: { "content-type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams({ + fb_api_req_friendly_name: "CometHovercardQueryRendererQuery", + fb_dtsg: CACHED.fb_dtsg, + variables: JSON.stringify({ + actionBarRenderLocation: "WWW_COMET_HOVERCARD", + context: context, + entityID: entityID, + includeTdaInfo: true, + scale: 1, + }), + doc_id: "7257793420991802", + }).toString(), + }, + ]); + console.log(res); + const text = await res.body; + const json = JSON.parse(text); + const node = json.data.node; + if (!node) throw new Error("Wrong ID / Entity not found"); + const typeText = node.__typename.toLowerCase(); + if (!Object.values(TargetType).includes(typeText)) + throw new Error("Not supported type: " + typeText); + const card = node.comet_hovercard_renderer[typeText]; + const type = + typeText === "user" + ? card.profile_plus_transition_path?.startsWith("PAGE") + ? TargetType.Page + : TargetType.User + : TargetType.Group; + return { + type, + id: node.id || card.id, + name: card.name, + avatar: card.profile_picture.uri, + url: card.profile_url || card.url, + raw: json, + }; +} + // log example: 5/31/2024, 9:13:41 AM: OPEN-TAB-unlock (1.67-1717121281787) -> 43 function extractUid(log) { return /-(\d+)\)/.exec(log)?.[1] || "?"; From 3dbc68d5bcba2410071d2bd7de340e3b0642457f Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 14 Aug 2024 22:04:46 +0700 Subject: [PATCH 34/43] backup plan --- scripts/ggdrive_downloadVideo.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/scripts/ggdrive_downloadVideo.js b/scripts/ggdrive_downloadVideo.js index 7e5e86dd..f981dcd7 100644 --- a/scripts/ggdrive_downloadVideo.js +++ b/scripts/ggdrive_downloadVideo.js @@ -15,6 +15,7 @@ export default { "https://www.facebook.com/groups/j2team.community/posts/974953859503401/", changeLogs: { + "2024-08-14": "get video url directly", "2024-07-25": "add backup plan", }, @@ -63,13 +64,14 @@ export default { }, contentScript: { onClick_: async () => { + const { openPopupWithHtml } = await import("./helpers/utils.js"); + let url = new URL(location.href); const player_response = url.searchParams.get("player_response"); if (player_response) { const json = JSON.parse(player_response); console.log(document.title, json); - const { openPopupWithHtml } = await import("./helpers/utils.js"); openPopupWithHtml( `

${document.title}

${json.streamingData.formats @@ -83,6 +85,23 @@ export default { 700, 700 ); + } else { + const videos = document.querySelectorAll("video"); + if (videos.length) { + openPopupWithHtml( + `

${document.title}

+ ${Array.from(videos) + .map((_) => { + return /*html*/ `
+

Link

+
`; + }) + .join("
")}`, + 700, + 700 + ); + } } }, }, From 43427b5b306a80a4ab17997d2ca994f4b313a977 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Thu, 15 Aug 2024 22:22:31 +0700 Subject: [PATCH 35/43] fix --- scripts/ggdrive_downloadVideo.js | 53 +++++++++++++++++++------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/scripts/ggdrive_downloadVideo.js b/scripts/ggdrive_downloadVideo.js index f981dcd7..f24db8f1 100644 --- a/scripts/ggdrive_downloadVideo.js +++ b/scripts/ggdrive_downloadVideo.js @@ -145,32 +145,43 @@ export const shared = { } async function getLink(docid) { - let res = await fetch( - "https://drive.google.com/get_video_info?docid=" + docid - ); + let lastError; + for (let u of ["", 0, 1, 2, 3, 4, 5, 6]) { + let res = await fetch( + "https://drive.google.com/" + + (u !== "" ? `u/${u}/` : "") + + "get_video_info?docid=" + + docid + ); - let text = await res.text(); - let json = parse(text); + let text = await res.text(); + let json = parse(text); - if (json?.status === "fail") { - throw Error("FAILED: " + json.reason); - } + if (json?.status === "fail") { + lastError = Error("FAILED: " + json.reason); + console.log(u, lastError); + continue; + } - json.url_encoded_fmt_stream_map = parseStream( - json.url_encoded_fmt_stream_map - ); + json.url_encoded_fmt_stream_map = parseStream( + json.url_encoded_fmt_stream_map + ); - let result = json.url_encoded_fmt_stream_map.map(function (stream) { - let name = json.title.replace(/\+/g, " "); - return { - idfile: docid, - name: name, - quality: stream.quality, - url: stream.url, - }; - }); + let result = json.url_encoded_fmt_stream_map.map(function (stream) { + let name = json.title.replace(/\+/g, " "); + return { + idfile: docid, + name: name, + quality: stream.quality, + url: stream.url, + }; + }); + + return result; + } - return result; + if (lastError) throw lastError; + return null; } return await getLink(docid); From 9e3c8ce4f803a1334c0222b88c7406e6ca9498c7 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Thu, 15 Aug 2024 22:37:10 +0700 Subject: [PATCH 36/43] minor fix --- scripts/ggDrive_downloadAllVideosInFolder.js | 6 +++++- scripts/ggdrive_downloadVideo.js | 8 ++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/scripts/ggDrive_downloadAllVideosInFolder.js b/scripts/ggDrive_downloadAllVideosInFolder.js index 5c27b20b..e15f57dc 100644 --- a/scripts/ggDrive_downloadAllVideosInFolder.js +++ b/scripts/ggDrive_downloadAllVideosInFolder.js @@ -47,7 +47,9 @@ export default {

${name}



Lỗi: ${errors.length} video
- ${errors.map(({ id, name, e }) => name).join("
")} + ${errors + .map(({ id, name, e }) => name + ": " + e.message) + .join("
")}
`); try { // prettier-ignore @@ -58,6 +60,8 @@ export default { } } + if (!result.length) throw new Error("Không tải được video nào"); + // =========== Render Data =========== let allUrls = {}; let tableHtml = result diff --git a/scripts/ggdrive_downloadVideo.js b/scripts/ggdrive_downloadVideo.js index f24db8f1..a4a5e042 100644 --- a/scripts/ggdrive_downloadVideo.js +++ b/scripts/ggdrive_downloadVideo.js @@ -146,7 +146,7 @@ export const shared = { async function getLink(docid) { let lastError; - for (let u of ["", 0, 1, 2, 3, 4, 5, 6]) { + for (let u of ["", 0, 1, 2]) { let res = await fetch( "https://drive.google.com/" + (u !== "" ? `u/${u}/` : "") + @@ -158,8 +158,8 @@ export const shared = { let json = parse(text); if (json?.status === "fail") { - lastError = Error("FAILED: " + json.reason); - console.log(u, lastError); + lastError = "FAILED: " + json.reason; + console.log(u, docid, lastError); continue; } @@ -180,7 +180,7 @@ export const shared = { return result; } - if (lastError) throw lastError; + if (lastError) throw new Error(lastError); return null; } From 61bcaf5c9fb6fc6f970d539a1c072f73ef59ed12 Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Wed, 21 Aug 2024 17:14:02 +0700 Subject: [PATCH 37/43] fix decode --- scripts/content-scripts/ufs_global.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/content-scripts/ufs_global.js b/scripts/content-scripts/ufs_global.js index 5d33e86b..1d359c5a 100644 --- a/scripts/content-scripts/ufs_global.js +++ b/scripts/content-scripts/ufs_global.js @@ -996,7 +996,7 @@ UfsGlobal.DEBUG = { JSON.parse('"' + res.replace(/\"/g, '\\"') + '"') ); } - return res; + return res.replaceAll("\\/", "/"); }, // https://stackoverflow.com/a/8649003 From 8137e92f742f83dc89b9dcd52371f0712abb236c Mon Sep 17 00:00:00 2001 From: "hoang.tran12" <99.hoangtran@gmail.com> Date: Thu, 22 Aug 2024 09:50:04 +0700 Subject: [PATCH 38/43] fix trusted html --- scripts/youtube_changeCountry.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/scripts/youtube_changeCountry.js b/scripts/youtube_changeCountry.js index ce126cd4..d75c4835 100644 --- a/scripts/youtube_changeCountry.js +++ b/scripts/youtube_changeCountry.js @@ -1,3 +1,5 @@ +import { UfsGlobal } from "./content-scripts/ufs_global.js"; + export default { icon: '', name: { @@ -22,7 +24,7 @@ export default { const popup = document.createElement("div"); popup.id = id; - popup.innerHTML = ` + popup.innerHTML = UfsGlobal.DOM.createTrustedHtml(`