Plugin

AnonymiseFileNames

Anonymise uploaded file names

Privacy Utility
index.tsx
Download

Source

src/plugins/anonymiseFileNames/index.tsx
1import { definePluginSettings } from "@api/Settings";
2import ErrorBoundary from "@components/ErrorBoundary";
3import { Devs } from "@utils/constants";
4import definePlugin, { OptionType } from "@utils/types";
5import { CloudUpload } from "@vencord/discord-types";
6import { findByCodeLazy } from "@webpack";
7import { useState } from "@webpack/common";
8
9const ActionBarIcon = findByCodeLazy("Children.map", "isValidElement", "dangerous:");
10
11const enum Methods {
12 Random,
13 Consistent,
14 Timestamp,
15}
16
17const ANONYMISE_UPLOAD_SYMBOL = Symbol("vcAnonymise");
18const tarExtMatcher = /\.tar\.\w+$/;
19
20const settings = definePluginSettings({
21 anonymiseByDefault: {
22 description: "Whether to anonymise file names by default",
23 type: OptionType.BOOLEAN,
24 default: true,
25 },
26 method: {
27 description: "Anonymising method",
28 type: OptionType.SELECT,
29 options: [
30 { label: "Random Characters", value: Methods.Random, default: true },
31 { label: "Consistent", value: Methods.Consistent },
32 { label: "Timestamp", value: Methods.Timestamp },
33 ],
34 },
35 randomisedLength: {
36 description: "Random characters length",
37 type: OptionType.NUMBER,
38 default: 7
39 },
40 consistent: {
41 description: "Consistent filename",
42 type: OptionType.STRING,
43 default: "image"
44 },
45}, {
46 randomisedLength: {
47 disabled() { return this.store.method !== Methods.Random; },
48 },
49 consistent: {
50 disabled() { return this.store.method !== Methods.Consistent; },
51 }
52});
53
54export default definePlugin({
55 name: "AnonymiseFileNames",
56 authors: [Devs.fawn],
57 description: "Anonymise uploaded file names",
58 tags: ["Privacy", "Utility"],
59 settings,
60
61 patches: [
62 {
63 find: "async uploadFiles(",
64 replacement: [
65 {
66 match: /async uploadFiles\((\i)\){/,
67 replace: "$&$1.forEach($self.anonymise);"
68 }
69 ],
70 },
71 {
72 find: "#{intl::ATTACHMENT_UTILITIES_SPOILER}",
73 replacement: {
74 match: /(?<=children:\[)(?=.{10,80}tooltip:.{0,100}#{intl::ATTACHMENT_UTILITIES_SPOILER})/,
75 replace: "arguments[0].canEdit!==false?$self.AnonymiseUploadButton(arguments[0]):null,"
76 },
77 },
78 ],
79
80 AnonymiseUploadButton: ErrorBoundary.wrap(({ upload }: { upload: CloudUpload; }) => {
81 const [anonymise, setAnonymise] = useState(upload[ANONYMISE_UPLOAD_SYMBOL] ?? settings.store.anonymiseByDefault);
82
83 function onToggleAnonymise() {
84 upload[ANONYMISE_UPLOAD_SYMBOL] = !anonymise;
85 setAnonymise(!anonymise);
86 }
87
88 return (
89 <ActionBarIcon
90 tooltip={anonymise ? "Using anonymous file name" : "Using normal file name"}
91 onClick={onToggleAnonymise}
92 >
93 {anonymise
94 ? <svg xmlns="http:class="ts-cmt">//www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M17.06 13C15.2 13 13.64 14.33 13.24 16.1C12.29 15.69 11.42 15.8 10.76 16.09C10.35 14.31 8.79 13 6.94 13C4.77 13 3 14.79 3 17C3 19.21 4.77 21 6.94 21C9 21 10.68 19.38 10.84 17.32C11.18 17.08 12.07 16.63 13.16 17.34C13.34 19.39 15 21 17.06 21C19.23 21 21 19.21 21 17C21 14.79 19.23 13 17.06 13M6.94 19.86C5.38 19.86 4.13 18.58 4.13 17S5.39 14.14 6.94 14.14C8.5 14.14 9.75 15.42 9.75 17S8.5 19.86 6.94 19.86M17.06 19.86C15.5 19.86 14.25 18.58 14.25 17S15.5 14.14 17.06 14.14C18.62 14.14 19.88 15.42 19.88 17S18.61 19.86 17.06 19.86M22 10.5H2V12H22V10.5M15.53 2.63C15.31 2.14 14.75 1.88 14.22 2.05L12 2.79L9.77 2.05L9.72 2.04C9.19 1.89 8.63 2.17 8.43 2.68L6 9H18L15.56 2.68L15.53 2.63Z" /></svg>
95 : <svg xmlns="http:class="ts-cmt">//www.w3.org/2000/svg" viewBox="0 0 24 24" style={{ transform: "scale(-1,1)" }}><path fill="currentColor" d="M22.11 21.46L2.39 1.73L1.11 3L6.31 8.2L6 9H7.11L8.61 10.5H2V12H10.11L13.5 15.37C13.38 15.61 13.3 15.85 13.24 16.1C12.29 15.69 11.41 15.8 10.76 16.09C10.35 14.31 8.79 13 6.94 13C4.77 13 3 14.79 3 17C3 19.21 4.77 21 6.94 21C9 21 10.68 19.38 10.84 17.32C11.18 17.08 12.07 16.63 13.16 17.34C13.34 19.39 15 21 17.06 21C17.66 21 18.22 20.86 18.72 20.61L20.84 22.73L22.11 21.46M6.94 19.86C5.38 19.86 4.13 18.58 4.13 17C4.13 15.42 5.39 14.14 6.94 14.14C8.5 14.14 9.75 15.42 9.75 17C9.75 18.58 8.5 19.86 6.94 19.86M17.06 19.86C15.5 19.86 14.25 18.58 14.25 17C14.25 16.74 14.29 16.5 14.36 16.25L17.84 19.73C17.59 19.81 17.34 19.86 17.06 19.86M22 12H15.2L13.7 10.5H22V12M17.06 13C19.23 13 21 14.79 21 17C21 17.25 20.97 17.5 20.93 17.73L19.84 16.64C19.68 15.34 18.66 14.32 17.38 14.17L16.29 13.09C16.54 13.03 16.8 13 17.06 13M12.2 9L7.72 4.5L8.43 2.68C8.63 2.17 9.19 1.89 9.72 2.04L9.77 2.05L12 2.79L14.22 2.05C14.75 1.88 15.32 2.14 15.54 2.63L15.56 2.68L18 9H12.2Z" /></svg>
96 }
97 </ActionBarIcon>
98 );
99 }, { noop: true }),
100
101 anonymise(upload: CloudUpload) {
102 if ((upload[ANONYMISE_UPLOAD_SYMBOL] ?? settings.store.anonymiseByDefault) === false) {
103 return;
104 }
105
106 const originalFileName = upload.filename;
107 const tarMatch = tarExtMatcher.exec(originalFileName);
108 const extIdx = tarMatch?.index ?? originalFileName.lastIndexOf(".");
109 const ext = extIdx !== -1 ? originalFileName.slice(extIdx) : "";
110
111 const newFilename = (() => {
112 switch (settings.store.method) {
113 case Methods.Random:
114 const chars = "0123456789bdfhjkmnpqrstvwxz";
115 return Array.from(
116 { length: settings.store.randomisedLength },
117 () => chars[Math.floor(Math.random() * chars.length)]
118 ).join("") + ext;
119 case Methods.Consistent:
120 return settings.store.consistent + ext;
121 case Methods.Timestamp:
122 return Date.now() + ext;
123 }
124 })();
125
126 upload.filename = newFilename;
127 }
128});
129