Skip to content

Commit eac8da5

Browse files
committed
fb more reaction story
1 parent c776127 commit eac8da5

13 files changed

+1080
-11
lines changed

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
],
4646
"web_accessible_resources": [
4747
{
48-
"resources": ["scripts/*.js", "scripts/*.css"],
48+
"resources": ["scripts/*"],
4949
"matches": ["<all_urls>"]
5050
}
5151
],

popup/tabs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ const tabs = [
9898
s.fb_toggleLight,
9999
s.fb_toggleNewFeed,
100100
s.fb_invisible_message,
101+
s.fb_moreReactionStory,
101102
createTitle("--- Download ---", "--- Tải xuống ---"),
102103
s.fb_downloadWatchingVideo,
103104
s.fb_storySaver,

scripts/content-scripts/document_start.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
// communication between page-script and content-script
2+
(() => {
3+
function sendToPageScript(event, data) {
4+
window.dispatchEvent(
5+
new CustomEvent("ufs-contentscript-sendto-pagescript", {
6+
detail: { event, data },
7+
})
8+
);
9+
}
10+
window.addEventListener("ufs-pagescript-sendto-contentscript", (e) => {
11+
let { event, data } = e.detail;
12+
switch (event) {
13+
case "getURL":
14+
sendToPageScript(event, chrome.runtime.getURL(data));
15+
break;
16+
}
17+
});
18+
})();
19+
20+
// run all scripts that has onDocumentStart event
121
(async () => {
222
injectScript(
323
chrome.runtime.getURL(
@@ -23,6 +43,7 @@
2343
);
2444
})();
2545

46+
// Run script on user click (if clicked script has onClickContentScript event)
2647
(async () => {
2748
try {
2849
const { MsgType, ClickType } = await import("../helpers/constants.js");

scripts/content-scripts/scripts/ufs_global_webpage_context.js

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,35 @@
11
// Tất cả các hàm/biến toàn cục được nhúng vào trang web at document_start
22
// Có thể truy cập từ các script chạy trong webpage context (có hàm onClick)
33

4-
const UsefulScriptGlobalWebpageContext = {
4+
const UsefulScriptGlobalPageContext = {
5+
Extension: {
6+
sendToContentScript: function (event, data) {
7+
return new Promise((resolve, reject) => {
8+
let listenerKey = "ufs-contentscript-sendto-pagescript";
9+
let listener = (evt) => {
10+
if (evt.detail.event === event) {
11+
resolve(evt.detail.data);
12+
window.removeEventListener(listenerKey, listener);
13+
}
14+
};
15+
window.addEventListener(listenerKey, listener);
16+
window.dispatchEvent(
17+
new CustomEvent("ufs-pagescript-sendto-contentscript", {
18+
detail: { event, data },
19+
})
20+
);
21+
});
22+
},
23+
getURL: async function (filePath) {
24+
return await UsefulScriptGlobalPageContext.Extension.sendToContentScript(
25+
"getURL",
26+
filePath
27+
);
28+
},
29+
},
530
DOM: {
631
deleteElements(selector, willReRun) {
7-
UsefulScriptGlobalWebpageContext.onElementsVisible(
32+
UsefulScriptGlobalPageContext.onElementsVisible(
833
selector,
934
(nodes) => {
1035
[].forEach.call(nodes, function (node) {
@@ -18,7 +43,7 @@ const UsefulScriptGlobalWebpageContext = {
1843

1944
waitForElements(selector) {
2045
return new Promise((resolve, reject) => {
21-
UsefulScriptGlobalWebpageContext.onElementsVisible(
46+
UsefulScriptGlobalPageContext.onElementsVisible(
2247
selector,
2348
resolve,
2449
false
@@ -71,6 +96,14 @@ const UsefulScriptGlobalWebpageContext = {
7196
else css.innerText = code;
7297
document.head.appendChild(css);
7398
},
99+
100+
injectCssFile(filePath) {
101+
var css = document.createElement("link");
102+
css.setAttribute("rel", "stylesheet");
103+
css.setAttribute("type", "text/css");
104+
css.setAttribute("href", filePath);
105+
document.head.appendChild(css);
106+
},
74107
},
75108
Facebook: {
76109
getUserAvatarFromUid(uid) {
@@ -85,7 +118,7 @@ const UsefulScriptGlobalWebpageContext = {
85118
},
86119
},
87120
};
88-
window.UsefulScriptGlobalWebpageContext = UsefulScriptGlobalWebpageContext;
121+
window.UsefulScriptGlobalPageContext = UsefulScriptGlobalPageContext;
89122

90123
// Chứa các hàm hỗ trợ việc hack web :))
91124
const UsefulScriptsUtils = {

scripts/detect_zeroWidthCharacters.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export default {
7474
}, 10);
7575
})();
7676

77-
UsefulScriptGlobalWebpageContext.DOM.injectCssCode(`
77+
UsefulScriptGlobalPageContext.DOM.injectCssCode(`
7878
.zero-width-characters {
7979
background-color: rgba(255, 0, 0, 0.2) !important;
8080
position: relative;

scripts/fb_moreReactionStory.css

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
::-webkit-scrollbar {
2+
width: 10px;
3+
}
4+
5+
::-webkit-scrollbar-track {
6+
background: #f1f1f1;
7+
}
8+
9+
::-webkit-scrollbar-thumb {
10+
background: #888;
11+
}
12+
13+
::-webkit-scrollbar-thumb:hover {
14+
background: #555;
15+
}
16+
17+
.react-container {
18+
position: relative;
19+
user-select: none;
20+
}
21+
22+
.btn-react {
23+
height: 45px;
24+
width: 45px;
25+
background-color: #fff;
26+
display: flex;
27+
justify-content: center;
28+
align-items: center;
29+
border-radius: 50%;
30+
font-weight: bold;
31+
cursor: pointer;
32+
}
33+
34+
.emoji-group {
35+
position: absolute;
36+
bottom: 60px;
37+
height: 200px;
38+
right: 0;
39+
width: 250px;
40+
padding: 15px;
41+
list-style: none;
42+
margin: 0;
43+
display: none;
44+
flex-flow: row wrap;
45+
justify-content: space-between;
46+
overflow-y: scroll;
47+
background-color: #fff;
48+
border-radius: 10px;
49+
}
50+
51+
52+
.emoji-group--show {
53+
display: flex;
54+
}
55+
56+
.emoji {
57+
height: 50px;
58+
width: 50px;
59+
display: flex;
60+
justify-content: center;
61+
align-items: center;
62+
font-size: 30px;
63+
cursor: pointer;
64+
transition: all 0.15s;
65+
}
66+
67+
.emoji.loading {
68+
animation: spin 2s linear infinite;
69+
}
70+
71+
.emoji:hover {
72+
transform: scale(1.5);
73+
}
74+
75+
.emoji:active {
76+
transform: scale(0.9);
77+
}
78+
79+
.floating-emoji {
80+
animation: bubble 2s ease-out;
81+
pointer-events: none;
82+
height: 50px;
83+
width: 50px;
84+
font-size: 30px;
85+
transition: all 0.3s;
86+
}
87+
88+
@keyframes spin {
89+
0% { transform: rotate(0deg); }
90+
100% { transform: rotate(360deg); }
91+
}
92+
93+
@keyframes bubble {
94+
0% { transform: translateY(0px); opacity: 1; }
95+
100% { transform: translateY(-200px); opacity: 0.1; }
96+
}

scripts/fb_moreReactionStory.js

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
export default {
2+
icon: "😍",
3+
name: {
4+
en: "Facebook Story - send more emoji reactions",
5+
vi: "Facebook Story - thêm nhiều loại emoji",
6+
},
7+
description: {
8+
en: "React story Facebook with more emojis",
9+
vi: "React story Facebook với nhiều loại emoji khác nhau",
10+
},
11+
whiteList: ["https://www.facebook.com/*"],
12+
13+
// Source https://github.com/whoant/react-story-facebook
14+
onDocumentStart: async () => {
15+
// const ID_USER = require('RelayAPIConfigDefaults').actorID;
16+
// const FB_DTSG = require('DTSGInitData').token;
17+
18+
(async () => {
19+
try {
20+
let url = await UsefulScriptGlobalPageContext.Extension.getURL(
21+
"scripts/fb_moreReactionStory.json"
22+
);
23+
const emojiJson = await fetch(url);
24+
const EMOJI_LIST = await emojiJson.json();
25+
loadModal(EMOJI_LIST);
26+
} catch (e) {
27+
console.error(e);
28+
}
29+
})();
30+
31+
function loadModal(EMOJI_LIST) {
32+
const fb_dtsg = getFbdtsg();
33+
const user_id = getUserId();
34+
35+
const timeoutCheckStoriesFooter = setInterval(() => {
36+
if (!window.location.href.includes("facebook.com/stories/")) return;
37+
if (!!document.querySelector(".btn-react")) return;
38+
39+
const btnReact = document.createElement("div");
40+
btnReact.textContent = "MORE";
41+
btnReact.setAttribute("class", "btn-react");
42+
43+
const emojiGroup = document.createElement("ul");
44+
emojiGroup.setAttribute("class", "emoji-group");
45+
46+
btnReact.onclick = function () {
47+
emojiGroup.classList.toggle("emoji-group--show");
48+
};
49+
50+
EMOJI_LIST.forEach((emoji) => {
51+
const emojiLi = document.createElement("li");
52+
emojiLi.setAttribute("class", "emoji");
53+
emojiLi.setAttribute("value", emoji.value);
54+
emojiLi.textContent = emoji.value;
55+
emojiLi.onclick = async function () {
56+
const storyId = getStoryId();
57+
try {
58+
emojiLi.classList.add("loading");
59+
await reactStory(user_id, fb_dtsg, storyId, emoji.value);
60+
emojiLi.classList.remove("loading");
61+
addFloatingEmoji(emoji, emojiLi);
62+
} catch (e) {
63+
console.error(e);
64+
}
65+
};
66+
67+
emojiGroup.appendChild(emojiLi);
68+
});
69+
70+
const reactContainer = document.createElement("div");
71+
reactContainer.setAttribute("class", "react-container");
72+
reactContainer.appendChild(btnReact);
73+
reactContainer.appendChild(emojiGroup);
74+
75+
const storiesFooter = document.getElementsByClassName(
76+
"x11lhmoz x78zum5 x1q0g3np xsdox4t x10l6tqk xtzzx4i xwa60dl xl56j7k xtuxyv6"
77+
);
78+
if (storiesFooter.length > 0) {
79+
// clearInterval(timeoutCheckStoriesFooter);
80+
storiesFooter[storiesFooter.length - 1].appendChild(reactContainer);
81+
}
82+
}, 1e3);
83+
}
84+
function addFloatingEmoji(emoji, ele) {
85+
let floatingEmoji = document.createElement("div");
86+
floatingEmoji.setAttribute("class", "floating-emoji");
87+
floatingEmoji.textContent = emoji.value;
88+
89+
let { top, left } = ele.getBoundingClientRect();
90+
floatingEmoji.style.position = "fixed";
91+
floatingEmoji.style.top = top + "px";
92+
floatingEmoji.style.left = left + "px";
93+
floatingEmoji.style.zIndex = 10000;
94+
document.body.appendChild(floatingEmoji);
95+
96+
setTimeout(() => {
97+
floatingEmoji.remove();
98+
}, 2e3);
99+
}
100+
function getStoryId() {
101+
const htmlStory = document.getElementsByClassName(
102+
"xh8yej3 x1n2onr6 xl56j7k x5yr21d x78zum5 x6s0dn4"
103+
);
104+
return htmlStory[htmlStory.length - 1].getAttribute("data-id");
105+
}
106+
function getFbdtsg() {
107+
const regex = /"DTSGInitialData",\[],{"token":"(.+?)"/gm;
108+
const resp = regex.exec(document.documentElement.innerHTML);
109+
return resp[1];
110+
}
111+
function getUserId() {
112+
const regex = /c_user=(\d+);/gm;
113+
const resp = regex.exec(document.cookie);
114+
return resp[1];
115+
}
116+
function reactStory(user_id, fb_dtsg, story_id, message) {
117+
return new Promise(async (resolve, reject) => {
118+
const variables = {
119+
input: {
120+
lightweight_reaction_actions: {
121+
offsets: [0],
122+
reaction: message,
123+
},
124+
story_id,
125+
story_reply_type: "LIGHT_WEIGHT",
126+
actor_id: user_id,
127+
client_mutation_id: 7,
128+
},
129+
};
130+
131+
const body = new URLSearchParams();
132+
body.append("av", user_id);
133+
body.append("__user", user_id);
134+
body.append("__a", 1);
135+
body.append("fb_dtsg", fb_dtsg);
136+
body.append("fb_api_caller_class", "RelayModern");
137+
body.append("fb_api_req_friendly_name", "useStoriesSendReplyMutation");
138+
body.append("variables", JSON.stringify(variables));
139+
body.append("server_timestamps", true);
140+
body.append("doc_id", "3769885849805751");
141+
142+
try {
143+
const response = await fetch(
144+
"https://www.facebook.com/api/graphql/",
145+
{
146+
method: "POST",
147+
headers: {
148+
"Content-Type": "application/x-www-form-urlencoded",
149+
},
150+
body,
151+
}
152+
);
153+
const res = await response.json();
154+
if (res.errors) return reject(res);
155+
resolve(res);
156+
} catch (error) {
157+
reject(error);
158+
}
159+
});
160+
}
161+
},
162+
163+
onDocumentIdle: async () => {
164+
let cssFile = await UsefulScriptGlobalPageContext.Extension.getURL(
165+
"scripts/fb_moreReactionStory.css"
166+
);
167+
UsefulScriptGlobalPageContext.DOM.injectCssFile(cssFile);
168+
},
169+
};

0 commit comments

Comments
 (0)