SnapEnhance Web

A userscript to Enhance the User experience on Snapchat Web

当前为 2024-01-03 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name SnapEnhance Web
  3. // @description A userscript to Enhance the User experience on Snapchat Web
  4. // @version 1.0.1
  5. // @author SnapEnhance
  6. // @source https://github.com/SnapEnhance/web/
  7. // @license GPLv3
  8. // @match *://web.snapchat.com/*
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=snapchat.com
  10. // @grant unsafeWindow
  11. // @run-at document-start
  12. // @namespace https://greasyfork.org/users/1242486
  13. // ==/UserScript==
  14. (function (window) {
  15. function simpleHook(object, name, proxy) {
  16. const old = object[name];
  17. object[name] = proxy(old, object);
  18. }
  19. // Bypass upload size
  20. Object.defineProperty(File.prototype, "size", {
  21. get: function () {
  22. return 500;
  23. }
  24. });
  25. // Inject into worker
  26. function workerInjected() {
  27. function hookPreRequest(request) {
  28. if (request.url.endsWith("messagingcoreservice.MessagingCoreService/SendTypingNotification")) {
  29. console.log("bypassed typing notification");
  30. return null;
  31. }
  32. if (request.url.endsWith("messagingcoreservice.MessagingCoreService/UpdateConversation")) {
  33. console.log("bypassed read receipt");
  34. return null;
  35. }
  36. return request;
  37. }
  38. function _arrayBufferToBase64(buffer) {
  39. const binary = new Uint8Array(buffer).reduce((acc, byte) => acc + String.fromCharCode(byte), '');
  40. return btoa(binary);
  41. }
  42. async function hookPostRequest(request, response) {
  43. if (request.headers && request.headers.get("content-type") === "application/grpc-web+proto") {
  44. const arrayBuffer = await response.arrayBuffer();
  45. console.log("Response", response.url, _arrayBufferToBase64(arrayBuffer));
  46. response.arrayBuffer = async () => arrayBuffer;
  47. }
  48. return response;
  49. }
  50. // Hook websocket (hide bitmoji)
  51. WebSocket.prototype.send = new Proxy(WebSocket.prototype.send, {
  52. apply: function (target, thisArg, argumentsList) {
  53. console.log("WebSocket send", argumentsList[0]);
  54. // return target.apply(thisArg, argumentsList);
  55. }
  56. });
  57. // Hook worker web requests
  58. const oldFetch = fetch;
  59. // @ts-ignore
  60. // eslint-disable-next-line no-implicit-globals
  61. fetch = async (...args) => {
  62. args[0] = hookPreRequest(args[0]);
  63. if (args[0] == null) {
  64. throw new Error();
  65. }
  66. const requestBody = args[0].body;
  67. console.log(args[0]);
  68. if (requestBody && !requestBody.locked) {
  69. const buffer = await requestBody.getReader().read();
  70. if (buffer.value) {
  71. console.log("Request", args[0].url, _arrayBufferToBase64(buffer.value));
  72. }
  73. args[0] = new Request(args[0], {
  74. body: buffer.value,
  75. headers: args[0].headers
  76. });
  77. }
  78. // @ts-ignore
  79. const result = oldFetch(...args);
  80. return new Promise(async (resolve, reject) => {
  81. try {
  82. resolve(await hookPostRequest(args[0], await result));
  83. }
  84. catch (e) {
  85. console.info("Fetch error", e);
  86. reject(e);
  87. }
  88. });
  89. };
  90. }
  91. const oldBlobClass = window.Blob;
  92. window.Blob = class HookedBlob extends Blob {
  93. constructor(...args) {
  94. const data = args[0][0];
  95. if (typeof data === "string" && data.startsWith("importScripts")) {
  96. args[0][0] += `${workerInjected.toString()};workerInjected();`;
  97. window.Blob = oldBlobClass;
  98. }
  99. super(...args);
  100. }
  101. };
  102. simpleHook(document, "createElement", (proxy, instance) => (...args) => {
  103. const result = proxy.call(instance, ...args);
  104. // Allow audio note and image download
  105. if (args[0] === "audio" || args[0] === "video" || args[0] === "img") {
  106. simpleHook(result, "setAttribute", (proxy2, instance2) => (...args2) => {
  107. if (args2[0] === "controlsList")
  108. return;
  109. proxy2.call(instance2, ...args2);
  110. });
  111. result.addEventListener("contextmenu", (event) => {
  112. event.stopImmediatePropagation();
  113. });
  114. }
  115. return result;
  116. });
  117. // Always focused
  118. simpleHook(document, "hasFocus", () => () => true);
  119. const oldAddEventListener = EventTarget.prototype.addEventListener;
  120. Object.defineProperty(EventTarget.prototype, "addEventListener", {
  121. value: function (...args) {
  122. const eventName = args[0];
  123. if (eventName === "keydown")
  124. return;
  125. if (eventName === "focus")
  126. return;
  127. return oldAddEventListener.call(this, ...args);
  128. }
  129. });
  130. })(window.unsafeWindow || window);