Plugin
ClearURLs
Automatically removes tracking elements from URLs you send
1
import {2
MessageObject3
} from "@api/MessageEvents";4
import { Devs } from "@utils/constants";5
import definePlugin from "@utils/types";6
7
const CLEAR_URLS_JSON_URL = "https:class="ts-cmt">//raw.githubusercontent.com/ClearURLs/Rules/master/data.min.json";8
9
interface Provider {10
urlPattern: string;11
completeProvider: boolean;12
rules?: string[];13
rawRules?: string[];14
referralMarketing?: string[];15
exceptions?: string[];16
redirections?: string[];17
forceRedirection?: boolean;18
}19
20
interface ClearUrlsData {21
providers: Record<string, Provider>;22
}23
24
interface RuleSet {25
name: string;26
urlPattern: RegExp;27
rules?: RegExp[];28
rawRules?: RegExp[];29
exceptions?: RegExp[];30
}31
32
export default definePlugin({33
name: "ClearURLs",34
description: "Automatically removes tracking elements from URLs you send",35
tags: ["Privacy", "Utility"],36
authors: [Devs.adryd, Devs.thororen],37
38
rules: [] as RuleSet[],39
40
async start() {41
await this.createRules();42
},43
44
stop() {45
this.rules = [];46
},47
48
onBeforeMessageSend(_, msg) {49
return this.cleanMessage(msg);50
},51
52
onBeforeMessageEdit(_cid, _mid, msg) {53
return this.cleanMessage(msg);54
},55
56
async createRules() {57
const res = await fetch(CLEAR_URLS_JSON_URL)58
.then(res => res.json()) as ClearUrlsData;59
60
this.rules = [];61
62
for (const [name, provider] of Object.entries(res.providers)) {63
const urlPattern = new RegExp(provider.urlPattern, "i");64
65
const rules = provider.rules?.map(rule => new RegExp(rule, "i"));66
const rawRules = provider.rawRules?.map(rule => new RegExp(rule, "i"));67
const exceptions = provider.exceptions?.map(ex => new RegExp(ex, "i"));68
69
this.rules.push({70
name,71
urlPattern,72
rules,73
rawRules,74
exceptions,75
});76
}77
},78
79
replacer(match: string) {80
// Parse URL without throwing errors81
try {82
var url = new URL(match);83
} catch (error) {84
// Don't modify anything if we can't parse the URL85
return match;86
}87
88
// Cheap way to check if there are any search params89
if (url.searchParams.entries().next().done) return match;90
91
// Check rules for each provider that matches92
this.rules.forEach(({ urlPattern, exceptions, rawRules, rules }) => {93
if (!urlPattern.test(url.href) || exceptions?.some(ex => ex.test(url.href))) return;94
95
const toDelete: string[] = [];96
97
if (rules) {98
// Add matched params to delete list99
url.searchParams.forEach((_, param) => {100
if (rules.some(rule => rule.test(param))) {101
toDelete.push(param);102
}103
});104
}105
106
// Delete matched params from list107
toDelete.forEach(param => url.searchParams.delete(param));108
109
// Match and remove any raw rules110
let cleanedUrl = url.href;111
rawRules?.forEach(rawRule => {112
cleanedUrl = cleanedUrl.replace(rawRule, "");113
});114
url = new URL(cleanedUrl);115
});116
117
return url.toString();118
},119
120
cleanMessage(msg: MessageObject) {121
// Only run on messages that contain URLs122
if (/http(s)?:\/\class="ts-cmt">//.test(msg.content)) {123
msg.content = msg.content.replace(124
/(https?:\/\/[^\s<]+[^<.,:;"039;>)|\]\s])/g,125
match => this.replacer(match)126
);127
}128
},129
});130