4chan anonymize file names

Anonymizes file names when posting on 4chan

  1. // ==UserScript==
  2. // @name 4chan anonymize file names
  3. // @namespace a508vdvu3inxqz4zeagu
  4. // @match https://boards.4chan.org/*
  5. // @match https://boards.4channel.org/*
  6. // @grant none
  7. // @version 1.1
  8. // @description Anonymizes file names when posting on 4chan
  9. // @inject-into content
  10. // @run-at document-start
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. "use strict";
  15.  
  16. const { window, document, Set, String, Math, performance, MutationObserver } = globalThis;
  17. const File = window.File;
  18.  
  19.  
  20. // Changes the file name to a random plausible timestamp
  21. const genFileName = (oldName) => {
  22. const minute = 60 * 1_000_000; // microseconds
  23. const year = minute * 60 * 24 * 365;
  24. const now = Math.floor((performance.timeOrigin + performance.now()) * 1000);
  25.  
  26. // Random timestamp 1 minute to 1 year in the past
  27. let newName = String(now - Math.floor(Math.random() * (year - minute + 1) + minute));
  28.  
  29. const dot = oldName.lastIndexOf(".");
  30. if (dot !== -1) {
  31. // Copy over file extension
  32. newName += oldName.substring(dot).toLowerCase();
  33. }
  34.  
  35. return newName;
  36. };
  37.  
  38.  
  39. // Set up UI elements (2 paired checkboxes)
  40. const checkbox = document.createElement("input");
  41. const label = document.createElement("label");
  42. const checkboxQR = document.createElement("input");
  43. const labelQR = document.createElement("label");
  44.  
  45. checkbox.type = checkboxQR.type = "checkbox";
  46. label.title = labelQR.title = "Send actual filename when uploading";
  47.  
  48. label.append(checkbox, " Filename");
  49. labelQR.append(checkboxQR, " Filename");
  50.  
  51.  
  52. // Turn off file name rewriting via the checkboxes
  53. let rewritingEnabled = true;
  54.  
  55. const checkboxListener = function () {
  56. rewritingEnabled = !this.checked;
  57.  
  58. // Transfer state to the other checkbox since it's global
  59. if (this === checkbox) {
  60. checkboxQR.checked = this.checked;
  61. } else {
  62. checkbox.checked = this.checked;
  63. }
  64. };
  65.  
  66. checkbox.addEventListener("input", checkboxListener);
  67. checkboxQR.addEventListener("input", checkboxListener);
  68.  
  69.  
  70. // Watch for forms being serialized, and change file names.
  71. window.addEventListener("formdata", ({ formData }) => {
  72. // Use global var instead of unregistering/re-registering this listener.
  73. // Keeping it in place ensures we always run first.
  74. if (!rewritingEnabled) {
  75. return;
  76. }
  77.  
  78. // Gather keys pointing to at least 1 file entry.
  79. const fileEntries = new Set();
  80.  
  81. for (const [key, val] of formData) {
  82. if (val instanceof File) {
  83. fileEntries.add(key);
  84. }
  85. }
  86.  
  87. // For each key...
  88. for (const key of fileEntries) {
  89. // Get all entry values
  90. const values = formData.getAll(key);
  91.  
  92. // Remove them
  93. formData.delete(key);
  94.  
  95. // Re-add them
  96. for (const val of values) {
  97. // ... with a new file name, if it is a File.
  98. if (val instanceof File) {
  99. formData.append(key, val, genFileName(val.name));
  100. } else {
  101. formData.append(key, val);
  102. }
  103. }
  104. }
  105. }, { capture: true, passive: true });
  106.  
  107.  
  108. const tryAddQuickReply = () => {
  109. if (!labelQR.isConnected) {
  110. document.getElementById("qrFile")?.after(" ", labelQR);
  111. }
  112. };
  113.  
  114.  
  115. // Insert post form checkbox on load
  116. window.addEventListener("DOMContentLoaded", () => {
  117. document.getElementById("postFile").after(" ", label);
  118. tryAddQuickReply();
  119.  
  120. // Wait for quick reply form to show up
  121. new MutationObserver(tryAddQuickReply).observe(document.body, { childList: true });
  122. });
  123. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址