-
Notifications
You must be signed in to change notification settings - Fork 0
/
inject.js
175 lines (146 loc) · 7.16 KB
/
inject.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/**
* This script is meant to be injected and ran when we use the popup. This is the entrypoint.
*/
async function main() {
const post = scrapeFacebookPost();
if (post.retrieved) {
// we want to send that the content was successfully scraped
await chrome.runtime.sendMessage({ 'status': 'success-content-scraped', 'payload': post });
try {
await chrome.runtime.sendMessage({ 'status': 'pending-api-request' });
// this is the fake news API
const response = await fetch('https://api-inference.huggingface.co/models/jvictoryliner/Fake-News-Bert-Detect',
{
method: 'POST',
headers: { Authorization: 'Bearer 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' },
body: JSON.stringify({ 'inputs': post.content }),
}
);
// this means that the API doesn't respond with a 200 even though it connects
if (!response.ok) return chrome.runtime.sendMessage({ 'status': 'failed-api-request' });
const result = await response.json();
const scoreOne = result[0][0].score;
const scoreTwo = result[0][1].score;
let isRealNews = false;
const fakeIsFirst = result[0][0].label === "LABEL_0";
if (fakeIsFirst) {
isRealNews = scoreOne < scoreTwo;
} else {
isRealNews = scoreOne > scoreTwo;
}
const highestScore = (Math.max(scoreOne, scoreTwo) * 100).toFixed(2);
// we send the result if it's a faker news or not
await chrome.runtime.sendMessage({ 'status': 'success-api-request', 'payload': {
'isRealNews': isRealNews,
'highestScore': highestScore
} });
} catch (error) {
console.log(error)
// we also want to send a message if there is a network failure (certain status codes trigger this)
await chrome.runtime.sendMessage({ 'status': 'failed-network' });
}
} else {
await chrome.runtime.sendMessage({ 'status': 'failed-content-scraped' });
}
}
/**
* This scrapes the current facebook post opened.
* @returns an object with a postedBy and content property both of which could be strings or null
*/
function scrapeFacebookPost() {
// IMPORTANT: this query sequence works specifically when you open a facebook post
let matches = matchElementsOfQuerySequence([
'a svg image', // profile picture - not text
'h2 span a', // profile name
'span a span', // post time
'div span', // unknown - skippable
'div span', // post privacy
'div span', // post content
'div span', // unknown - skippable
], document, true); // true means this array will only have at most 1 element
if (matches.length > 0) return { postedBy: matches[0][1].textContent, content: matches[0][5].textContent, retrieved: true }
// if the first sequence doesn't work, there is another sequence we could try
// it seems this works for ad posts
if (matches.length === 0) {
matches = matchElementsOfQuerySequence([
'a div a svg', // profile picture
'div span h2', // profile name
'div div > svg + div', // more button
'div[data-ad-preview]' // post content
], document, true);
return { postedBy: matches[0][1].textContent, content: matches[0][3].textContent, retrieved: true };
}
return { postedBy: null, content: null, retrieved: false };
}
/**
* The function that will find elements that are siblings or cousins of one another based on the sequence of queries.
* @param {String[]} matchPatternsOfQueries - An array of queries where the resulting elements should be "siblings" or "cousins".
* @param {Document} document - An HTML Document.
* @param {boolean} [firstMatchOnly=false] - indicates if the algorithm includes all matches.
* @returns {Element[][]} The array of results following the matched pattern.
*/
function matchElementsOfQuerySequence (matchPatternsOfQueries, document, firstMatchOnly=false) {
const basis = document.querySelectorAll(matchPatternsOfQueries[0]);
const flatList = flattenDocumentDepthFirst(document);
let results = [];
for (let first of basis) {
const parent = first.parentNode;
if (!parent) continue;
let initialMatches = new Array(matchPatternsOfQueries.length).fill(null);
initialMatches[0] = first;
let sequence = findFirstElementSequenceRecursive(matchPatternsOfQueries, first, parent, initialMatches, 1, flatList);
if (sequence.every(element => element != null)) {
results.push(sequence);
if (firstMatchOnly) break;
}
}
return results;
}
/**
* The recursive function which returns a sequence of elements according to the queries.
* @param {String[]} queries - the queries given, doesn't change
* @param {Element} first - very first element, doesn't change
* @param {Element} parent - current parent
* @param {Element[]} matches - current matches
* @param {Number} index - current index in the queries
* @param {Element[]} flatList - the flat list of DOM elements for sequence checks
*/
function findFirstElementSequenceRecursive(queries, first, parent, matches, index, flatList) {
if (index >= queries.length) return matches;
let targetElements = parent.querySelectorAll(queries[index]);
if (targetElements.length > 0) {
for (let targetElement of targetElements) {
if ( matches.some(element => {
if (element === null) return false;
return (
element === targetElement ||
element.contains(targetElement) ||
targetElement.contains(element) ||
flatList.indexOf(element) >= flatList.indexOf(targetElement)
);
})
) continue;
matches[index] = targetElement;
matches = findFirstElementSequenceRecursive(queries, first, parent, matches, index + 1, flatList);
}
} else if (parent.parentNode) {
matches = findFirstElementSequenceRecursive(queries, first, parent.parentNode, matches, index, flatList);
}
return matches;
}
/**
* Flatten document into an array using a DFS algorithm.
* @param {Document} document - The document object.
* @param {Element} element - The current element, used in recursion.
* @param {Element[]} elements - The current elements, used in recursion
* @returns {Element[]} The elements in the flattened document.
*/
function flattenDocumentDepthFirst(document, element=null, elements=[]) {
if (!element) element = document.body;
elements.push(element);
for (let children of element.children) {
flattenDocumentDepthFirst(document, children, elements);
}
return elements;
}
main();