ryd.background.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. const apiUrl = "https://returnyoutubedislikeapi.com";
  2. const voteDisabledIconName = 'icon_hold128.png';
  3. const defaultIconName = 'icon128.png';
  4. let api;
  5. /** stores extension's global config */
  6. let extConfig = {
  7. disableVoteSubmission: false
  8. // coloredThumbs: false,
  9. // coloredBar: false,
  10. }
  11. if (isChrome()) api = chrome;
  12. else if (isFirefox()) api = browser;
  13. initExtConfig()
  14. api.runtime.onMessage.addListener((request, sender, sendResponse) => {
  15. if (request.message === "get_auth_token") {
  16. chrome.identity.getAuthToken({ interactive: true }, function (token) {
  17. console.log(token);
  18. chrome.identity.getProfileUserInfo(function (userInfo) {
  19. console.log(JSON.stringify(userInfo));
  20. });
  21. });
  22. } else if (request.message === "log_off") {
  23. // chrome.identity.clearAllCachedAuthTokens(() => console.log("logged off"));
  24. } else if (request.message == "set_state") {
  25. // chrome.identity.getAuthToken({ interactive: true }, function (token) {
  26. let token = "";
  27. fetch(
  28. `${apiUrl}/votes?videoId=${request.videoId}&likeCount=${
  29. request.likeCount || ""
  30. }`,
  31. {
  32. method: "GET",
  33. headers: {
  34. Accept: "application/json",
  35. },
  36. }
  37. )
  38. .then((response) => response.json())
  39. .then((response) => {
  40. sendResponse(response);
  41. })
  42. .catch();
  43. return true;
  44. } else if (request.message == "send_links") {
  45. toSend = toSend.concat(request.videoIds.filter((x) => !sentIds.has(x)));
  46. if (toSend.length >= 20) {
  47. fetch(`${apiUrl}/votes`, {
  48. method: "POST",
  49. headers: {
  50. "Content-Type": "application/json",
  51. },
  52. body: JSON.stringify(toSend),
  53. });
  54. for (const toSendUrl of toSend) {
  55. sentIds.add(toSendUrl);
  56. }
  57. toSend = [];
  58. }
  59. } else if (request.message == "register") {
  60. register();
  61. return true;
  62. } else if (request.message == "send_vote") {
  63. sendVote(request.videoId, request.vote);
  64. return true;
  65. }
  66. });
  67. async function sendVote(videoId, vote) {
  68. api.storage.sync.get(null, async (storageResult) => {
  69. if (!storageResult.userId || !storageResult.registrationConfirmed) {
  70. await register();
  71. return;
  72. }
  73. fetch(`${apiUrl}/interact/vote`, {
  74. method: "POST",
  75. headers: {
  76. "Content-Type": "application/json",
  77. },
  78. body: JSON.stringify({
  79. userId: storageResult.userId,
  80. videoId,
  81. value: vote,
  82. }),
  83. })
  84. .then(async (response) => {
  85. if (response.status == 401) {
  86. await register();
  87. await sendVote(videoId, vote);
  88. return;
  89. }
  90. return response.json()
  91. })
  92. .then((response) => {
  93. solvePuzzle(response).then((solvedPuzzle) => {
  94. fetch(`${apiUrl}/interact/confirmVote`, {
  95. method: "POST",
  96. headers: {
  97. "Content-Type": "application/json",
  98. },
  99. body: JSON.stringify({
  100. ...solvedPuzzle,
  101. userId: storageResult.userId,
  102. videoId,
  103. }),
  104. });
  105. });
  106. });
  107. });
  108. }
  109. function register() {
  110. let userId = generateUserID();
  111. api.storage.sync.set({ userId });
  112. return fetch(`${apiUrl}/puzzle/registration?userId=${userId}`, {
  113. method: "GET",
  114. headers: {
  115. Accept: "application/json",
  116. },
  117. })
  118. .then((response) => response.json())
  119. .then((response) => {
  120. return solvePuzzle(response).then((solvedPuzzle) => {
  121. return fetch(`${apiUrl}/puzzle/registration?userId=${userId}`, {
  122. method: "POST",
  123. headers: {
  124. "Content-Type": "application/json",
  125. },
  126. body: JSON.stringify(solvedPuzzle),
  127. }).then((response) =>
  128. response.json().then((result) => {
  129. if (result === true) {
  130. return api.storage.sync.set({ registrationConfirmed: true });
  131. }
  132. })
  133. );
  134. });
  135. })
  136. .catch();
  137. }
  138. api.storage.sync.get(null, (res) => {
  139. if (!res || !res.userId || !res.registrationConfirmed) {
  140. register();
  141. }
  142. });
  143. const sentIds = new Set();
  144. let toSend = [];
  145. function sendUserSubmittedStatisticsToApi(statistics) {
  146. fetch(`${apiUrl}/votes/user-submitted`, {
  147. method: "POST",
  148. headers: {
  149. "Content-Type": "application/json",
  150. },
  151. body: JSON.stringify(statistics),
  152. });
  153. }
  154. function countLeadingZeroes(uInt8View, limit) {
  155. let zeroes = 0;
  156. let value = 0;
  157. for (let i = 0; i < uInt8View.length; i++) {
  158. value = uInt8View[i];
  159. if (value === 0) {
  160. zeroes += 8;
  161. } else {
  162. let count = 1;
  163. if (value >>> 4 === 0) {
  164. count += 4;
  165. value <<= 4;
  166. }
  167. if (value >>> 6 === 0) {
  168. count += 2;
  169. value <<= 2;
  170. }
  171. zeroes += count - (value >>> 7);
  172. break;
  173. }
  174. if (zeroes >= limit) {
  175. break;
  176. }
  177. }
  178. return zeroes;
  179. }
  180. async function solvePuzzle(puzzle) {
  181. let challenge = Uint8Array.from(atob(puzzle.challenge), (c) =>
  182. c.charCodeAt(0)
  183. );
  184. let buffer = new ArrayBuffer(20);
  185. let uInt8View = new Uint8Array(buffer);
  186. let uInt32View = new Uint32Array(buffer);
  187. let maxCount = Math.pow(2, puzzle.difficulty) * 5;
  188. for (let i = 4; i < 20; i++) {
  189. uInt8View[i] = challenge[i - 4];
  190. }
  191. for (let i = 0; i < maxCount; i++) {
  192. uInt32View[0] = i;
  193. let hash = await crypto.subtle.digest("SHA-512", buffer);
  194. let hashUint8 = new Uint8Array(hash);
  195. if (countLeadingZeroes(hashUint8) >= puzzle.difficulty) {
  196. return {
  197. solution: btoa(String.fromCharCode.apply(null, uInt8View.slice(0, 4))),
  198. };
  199. }
  200. }
  201. }
  202. function generateUserID(length = 36) {
  203. const charset =
  204. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  205. let result = "";
  206. if (crypto && crypto.getRandomValues) {
  207. const values = new Uint32Array(length);
  208. crypto.getRandomValues(values);
  209. for (let i = 0; i < length; i++) {
  210. result += charset[values[i] % charset.length];
  211. }
  212. return result;
  213. } else {
  214. for (let i = 0; i < length; i++) {
  215. result += charset[Math.floor(Math.random() * charset.length)];
  216. }
  217. return result;
  218. }
  219. }
  220. function storageChangeHandler(changes, area) {
  221. if (changes.disableVoteSubmission !== undefined) {
  222. handleDisableVoteSubmissionChangeEvent(changes.disableVoteSubmission.newValue);
  223. }
  224. }
  225. function handleDisableVoteSubmissionChangeEvent(value) {
  226. extConfig.disableVoteSubmission = value;
  227. if (value === true) {
  228. changeIcon(voteDisabledIconName);
  229. } else {
  230. changeIcon(defaultIconName);
  231. }
  232. }
  233. function changeIcon(iconName) {
  234. if (api.action !== undefined) api.action.setIcon({path: "/icons/" + iconName});
  235. else if (api.browserAction !== undefined) api.browserAction.setIcon({path: "/icons/" + iconName});
  236. else console.log('changing icon is not supported');
  237. }
  238. api.storage.onChanged.addListener(storageChangeHandler);
  239. function initExtConfig() {
  240. initializeDisableVoteSubmission();
  241. }
  242. function initializeDisableVoteSubmission() {
  243. api.storage.sync.get(['disableVoteSubmission'], (res) => {
  244. if (res.disableVoteSubmission === undefined) {
  245. api.storage.sync.set({disableVoteSubmission: false});
  246. }
  247. else {
  248. extConfig.disableVoteSubmission = res.disableVoteSubmission;
  249. if (res.disableVoteSubmission) changeIcon(voteDisabledIconName);
  250. }
  251. });
  252. }
  253. function isChrome() {
  254. return typeof chrome !== "undefined" && typeof chrome.runtime !== "undefined";
  255. }
  256. function isFirefox() {
  257. return typeof browser !== "undefined" && typeof browser.runtime !== "undefined";
  258. }