ryd.background.js 7.3 KB

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