Plugin
AnonymiseFileNames
Anonymise uploaded file names
1
/*2
* FiveCord — a Discord client mod3
* Copyright (c) 2025 FiveCord4
* SPDX-License-Identifier: GPL-3.0-or-later5
*/6
7
import { definePluginSettings } from "@api/Settings";8
import ErrorBoundary from "@components/ErrorBoundary";9
import { Devs } from "@utils/constants";10
import definePlugin, { OptionType } from "@utils/types";11
import { CloudUpload } from "@vencord/discord-types";12
import { findByCodeLazy } from "@webpack";13
import { useState } from "@webpack/common";14
15
const ActionBarIcon = findByCodeLazy("Children.map", "isValidElement", "dangerous:");16
17
const enum Methods {18
Random,19
Consistent,20
Timestamp,21
}22
23
const ANONYMISE_UPLOAD_SYMBOL = Symbol("vcAnonymise");24
const tarExtMatcher = /\.tar\.\w+$/;25
26
const settings = definePluginSettings({27
anonymiseByDefault: {28
description: "Whether to anonymise file names by default",29
type: OptionType.BOOLEAN,30
default: true,31
},32
method: {33
description: "Anonymising method",34
type: OptionType.SELECT,35
options: [36
{ label: "Random Characters", value: Methods.Random, default: true },37
{ label: "Consistent", value: Methods.Consistent },38
{ label: "Timestamp", value: Methods.Timestamp },39
],40
},41
randomisedLength: {42
description: "Random characters length",43
type: OptionType.NUMBER,44
default: 745
},46
consistent: {47
description: "Consistent filename",48
type: OptionType.STRING,49
default: "image"50
},51
}, {52
randomisedLength: {53
disabled() { return this.store.method !== Methods.Random; },54
},55
consistent: {56
disabled() { return this.store.method !== Methods.Consistent; },57
}58
});59
60
export default definePlugin({61
name: "AnonymiseFileNames",62
authors: [Devs.fawn],63
description: "Anonymise uploaded file names",64
tags: ["Privacy", "Utility"],65
settings,66
67
patches: [68
{69
find: "async uploadFiles(",70
replacement: [71
{72
match: /async uploadFiles\((\i)\){/,73
replace: "$&$1.forEach($self.anonymise);"74
}75
],76
},77
{78
find: "#{intl::ATTACHMENT_UTILITIES_SPOILER}",79
replacement: {80
match: /(?<=children:\[)(?=.{10,80}tooltip:.{0,100}#{intl::ATTACHMENT_UTILITIES_SPOILER})/,81
replace: "arguments[0].canEdit!==false?$self.AnonymiseUploadButton(arguments[0]):null,"82
},83
},84
],85
86
AnonymiseUploadButton: ErrorBoundary.wrap(({ upload }: { upload: CloudUpload; }) => {87
const [anonymise, setAnonymise] = useState(upload[ANONYMISE_UPLOAD_SYMBOL] ?? settings.store.anonymiseByDefault);88
89
function onToggleAnonymise() {90
upload[ANONYMISE_UPLOAD_SYMBOL] = !anonymise;91
setAnonymise(!anonymise);92
}93
94
return (95
<ActionBarIcon96
tooltip={anonymise ? "Using anonymous file name" : "Using normal file name"}97
onClick={onToggleAnonymise}98
>99
{anonymise100
? <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>101
: <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>102
}103
</ActionBarIcon>104
);105
}, { noop: true }),106
107
anonymise(upload: CloudUpload) {108
if ((upload[ANONYMISE_UPLOAD_SYMBOL] ?? settings.store.anonymiseByDefault) === false) {109
return;110
}111
112
const originalFileName = upload.filename;113
const tarMatch = tarExtMatcher.exec(originalFileName);114
const extIdx = tarMatch?.index ?? originalFileName.lastIndexOf(".");115
const ext = extIdx !== -1 ? originalFileName.slice(extIdx) : "";116
117
const newFilename = (() => {118
switch (settings.store.method) {119
case Methods.Random:120
const chars = "0123456789bdfhjkmnpqrstvwxz";121
return Array.from(122
{ length: settings.store.randomisedLength },123
() => chars[Math.floor(Math.random() * chars.length)]124
).join("") + ext;125
case Methods.Consistent:126
return settings.store.consistent + ext;127
case Methods.Timestamp:128
return Date.now() + ext;129
}130
})();131
132
upload.filename = newFilename;133
}134
});135