ryd.background.js 7.3 KB

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